]> git.lizzy.rs Git - rust.git/commitdiff
Add 'src/tools/clippy/' from commit 'd2708873ef711ec8ab45df1e984ecf24a96cd369'
authorOliver Scherer <github35764891676564198441@oli-obk.de>
Sat, 2 May 2020 07:49:00 +0000 (09:49 +0200)
committerOliver Scherer <github35764891676564198441@oli-obk.de>
Sat, 2 May 2020 07:49:00 +0000 (09:49 +0200)
git-subtree-dir: src/tools/clippy
git-subtree-mainline: 06c44816c1532e5ff08ad072f581fc068eb60e2e
git-subtree-split: d2708873ef711ec8ab45df1e984ecf24a96cd369

1286 files changed:
1  2 
src/tools/clippy/.cargo/config
src/tools/clippy/.editorconfig
src/tools/clippy/.gitattributes
src/tools/clippy/.github/ISSUE_TEMPLATE.md
src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md
src/tools/clippy/.github/deploy.sh
src/tools/clippy/.github/driver.sh
src/tools/clippy/.github/workflows/clippy.yml
src/tools/clippy/.github/workflows/clippy_bors.yml
src/tools/clippy/.github/workflows/clippy_dev.yml
src/tools/clippy/.github/workflows/deploy.yml
src/tools/clippy/.github/workflows/remark.yml
src/tools/clippy/.gitignore
src/tools/clippy/.remarkrc
src/tools/clippy/CHANGELOG.md
src/tools/clippy/CODE_OF_CONDUCT.md
src/tools/clippy/CONTRIBUTING.md
src/tools/clippy/COPYRIGHT
src/tools/clippy/Cargo.toml
src/tools/clippy/LICENSE-APACHE
src/tools/clippy/LICENSE-MIT
src/tools/clippy/README.md
src/tools/clippy/build.rs
src/tools/clippy/clippy_dev/Cargo.toml
src/tools/clippy/clippy_dev/src/fmt.rs
src/tools/clippy/clippy_dev/src/lib.rs
src/tools/clippy/clippy_dev/src/main.rs
src/tools/clippy/clippy_dev/src/new_lint.rs
src/tools/clippy/clippy_dev/src/stderr_length_check.rs
src/tools/clippy/clippy_dev/src/update_lints.rs
src/tools/clippy/clippy_dummy/Cargo.toml
src/tools/clippy/clippy_dummy/PUBLISH.md
src/tools/clippy/clippy_dummy/build.rs
src/tools/clippy/clippy_dummy/crates-readme.md
src/tools/clippy/clippy_dummy/src/main.rs
src/tools/clippy/clippy_lints/Cargo.toml
src/tools/clippy/clippy_lints/README.md
src/tools/clippy/clippy_lints/src/approx_const.rs
src/tools/clippy/clippy_lints/src/arithmetic.rs
src/tools/clippy/clippy_lints/src/as_conversions.rs
src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
src/tools/clippy/clippy_lints/src/assign_ops.rs
src/tools/clippy/clippy_lints/src/atomic_ordering.rs
src/tools/clippy/clippy_lints/src/attrs.rs
src/tools/clippy/clippy_lints/src/await_holding_lock.rs
src/tools/clippy/clippy_lints/src/bit_mask.rs
src/tools/clippy/clippy_lints/src/blacklisted_name.rs
src/tools/clippy/clippy_lints/src/block_in_if_condition.rs
src/tools/clippy/clippy_lints/src/booleans.rs
src/tools/clippy/clippy_lints/src/bytecount.rs
src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs
src/tools/clippy/clippy_lints/src/checked_conversions.rs
src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
src/tools/clippy/clippy_lints/src/collapsible_if.rs
src/tools/clippy/clippy_lints/src/comparison_chain.rs
src/tools/clippy/clippy_lints/src/consts.rs
src/tools/clippy/clippy_lints/src/copies.rs
src/tools/clippy/clippy_lints/src/copy_iterator.rs
src/tools/clippy/clippy_lints/src/dbg_macro.rs
src/tools/clippy/clippy_lints/src/default_trait_access.rs
src/tools/clippy/clippy_lints/src/deprecated_lints.rs
src/tools/clippy/clippy_lints/src/dereference.rs
src/tools/clippy/clippy_lints/src/derive.rs
src/tools/clippy/clippy_lints/src/doc.rs
src/tools/clippy/clippy_lints/src/double_comparison.rs
src/tools/clippy/clippy_lints/src/double_parens.rs
src/tools/clippy/clippy_lints/src/drop_bounds.rs
src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
src/tools/clippy/clippy_lints/src/duration_subsec.rs
src/tools/clippy/clippy_lints/src/else_if_without_else.rs
src/tools/clippy/clippy_lints/src/empty_enum.rs
src/tools/clippy/clippy_lints/src/entry.rs
src/tools/clippy/clippy_lints/src/enum_clike.rs
src/tools/clippy/clippy_lints/src/enum_variants.rs
src/tools/clippy/clippy_lints/src/eq_op.rs
src/tools/clippy/clippy_lints/src/erasing_op.rs
src/tools/clippy/clippy_lints/src/escape.rs
src/tools/clippy/clippy_lints/src/eta_reduction.rs
src/tools/clippy/clippy_lints/src/eval_order_dependence.rs
src/tools/clippy/clippy_lints/src/excessive_bools.rs
src/tools/clippy/clippy_lints/src/exit.rs
src/tools/clippy/clippy_lints/src/explicit_write.rs
src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
src/tools/clippy/clippy_lints/src/float_literal.rs
src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
src/tools/clippy/clippy_lints/src/format.rs
src/tools/clippy/clippy_lints/src/formatting.rs
src/tools/clippy/clippy_lints/src/functions.rs
src/tools/clippy/clippy_lints/src/future_not_send.rs
src/tools/clippy/clippy_lints/src/get_last_with_len.rs
src/tools/clippy/clippy_lints/src/identity_conversion.rs
src/tools/clippy/clippy_lints/src/identity_op.rs
src/tools/clippy/clippy_lints/src/if_let_mutex.rs
src/tools/clippy/clippy_lints/src/if_let_some_result.rs
src/tools/clippy/clippy_lints/src/if_not_else.rs
src/tools/clippy/clippy_lints/src/implicit_return.rs
src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
src/tools/clippy/clippy_lints/src/indexing_slicing.rs
src/tools/clippy/clippy_lints/src/infinite_iter.rs
src/tools/clippy/clippy_lints/src/inherent_impl.rs
src/tools/clippy/clippy_lints/src/inherent_to_string.rs
src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
src/tools/clippy/clippy_lints/src/int_plus_one.rs
src/tools/clippy/clippy_lints/src/integer_division.rs
src/tools/clippy/clippy_lints/src/items_after_statements.rs
src/tools/clippy/clippy_lints/src/large_const_arrays.rs
src/tools/clippy/clippy_lints/src/large_enum_variant.rs
src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
src/tools/clippy/clippy_lints/src/len_zero.rs
src/tools/clippy/clippy_lints/src/let_if_seq.rs
src/tools/clippy/clippy_lints/src/let_underscore.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/lifetimes.rs
src/tools/clippy/clippy_lints/src/literal_representation.rs
src/tools/clippy/clippy_lints/src/loops.rs
src/tools/clippy/clippy_lints/src/macro_use.rs
src/tools/clippy/clippy_lints/src/main_recursion.rs
src/tools/clippy/clippy_lints/src/map_clone.rs
src/tools/clippy/clippy_lints/src/map_unit_fn.rs
src/tools/clippy/clippy_lints/src/match_on_vec_items.rs
src/tools/clippy/clippy_lints/src/matches.rs
src/tools/clippy/clippy_lints/src/mem_discriminant.rs
src/tools/clippy/clippy_lints/src/mem_forget.rs
src/tools/clippy/clippy_lints/src/mem_replace.rs
src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs
src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
src/tools/clippy/clippy_lints/src/minmax.rs
src/tools/clippy/clippy_lints/src/misc.rs
src/tools/clippy/clippy_lints/src/misc_early.rs
src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
src/tools/clippy/clippy_lints/src/missing_doc.rs
src/tools/clippy/clippy_lints/src/missing_inline.rs
src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs
src/tools/clippy/clippy_lints/src/multiple_crate_versions.rs
src/tools/clippy/clippy_lints/src/mut_key.rs
src/tools/clippy/clippy_lints/src/mut_mut.rs
src/tools/clippy/clippy_lints/src/mut_reference.rs
src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs
src/tools/clippy/clippy_lints/src/mutex_atomic.rs
src/tools/clippy/clippy_lints/src/needless_bool.rs
src/tools/clippy/clippy_lints/src/needless_borrow.rs
src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
src/tools/clippy/clippy_lints/src/needless_continue.rs
src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
src/tools/clippy/clippy_lints/src/needless_update.rs
src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs
src/tools/clippy/clippy_lints/src/neg_multiply.rs
src/tools/clippy/clippy_lints/src/new_without_default.rs
src/tools/clippy/clippy_lints/src/no_effect.rs
src/tools/clippy/clippy_lints/src/non_copy_const.rs
src/tools/clippy/clippy_lints/src/non_expressive_names.rs
src/tools/clippy/clippy_lints/src/open_options.rs
src/tools/clippy/clippy_lints/src/option_env_unwrap.rs
src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs
src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs
src/tools/clippy/clippy_lints/src/precedence.rs
src/tools/clippy/clippy_lints/src/ptr.rs
src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
src/tools/clippy/clippy_lints/src/question_mark.rs
src/tools/clippy/clippy_lints/src/ranges.rs
src/tools/clippy/clippy_lints/src/redundant_clone.rs
src/tools/clippy/clippy_lints/src/redundant_field_names.rs
src/tools/clippy/clippy_lints/src/redundant_pattern_matching.rs
src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
src/tools/clippy/clippy_lints/src/reference.rs
src/tools/clippy/clippy_lints/src/regex.rs
src/tools/clippy/clippy_lints/src/returns.rs
src/tools/clippy/clippy_lints/src/serde_api.rs
src/tools/clippy/clippy_lints/src/shadow.rs
src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
src/tools/clippy/clippy_lints/src/strings.rs
src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs
src/tools/clippy/clippy_lints/src/swap.rs
src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
src/tools/clippy/clippy_lints/src/temporary_assignment.rs
src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
src/tools/clippy/clippy_lints/src/trait_bounds.rs
src/tools/clippy/clippy_lints/src/transmute.rs
src/tools/clippy/clippy_lints/src/transmuting_null.rs
src/tools/clippy/clippy_lints/src/trivially_copy_pass_by_ref.rs
src/tools/clippy/clippy_lints/src/try_err.rs
src/tools/clippy/clippy_lints/src/types.rs
src/tools/clippy/clippy_lints/src/unicode.rs
src/tools/clippy/clippy_lints/src/unnamed_address.rs
src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs
src/tools/clippy/clippy_lints/src/unused_io_amount.rs
src/tools/clippy/clippy_lints/src/unused_self.rs
src/tools/clippy/clippy_lints/src/unwrap.rs
src/tools/clippy/clippy_lints/src/use_self.rs
src/tools/clippy/clippy_lints/src/utils/attrs.rs
src/tools/clippy/clippy_lints/src/utils/author.rs
src/tools/clippy/clippy_lints/src/utils/camel_case.rs
src/tools/clippy/clippy_lints/src/utils/comparisons.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/utils/constants.rs
src/tools/clippy/clippy_lints/src/utils/diagnostics.rs
src/tools/clippy/clippy_lints/src/utils/higher.rs
src/tools/clippy/clippy_lints/src/utils/hir_utils.rs
src/tools/clippy/clippy_lints/src/utils/inspector.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_lints/src/utils/mod.rs
src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs
src/tools/clippy/clippy_lints/src/utils/paths.rs
src/tools/clippy/clippy_lints/src/utils/ptr.rs
src/tools/clippy/clippy_lints/src/utils/sugg.rs
src/tools/clippy/clippy_lints/src/utils/sym.rs
src/tools/clippy/clippy_lints/src/utils/usage.rs
src/tools/clippy/clippy_lints/src/vec.rs
src/tools/clippy/clippy_lints/src/verbose_file_reads.rs
src/tools/clippy/clippy_lints/src/wildcard_dependencies.rs
src/tools/clippy/clippy_lints/src/wildcard_imports.rs
src/tools/clippy/clippy_lints/src/write.rs
src/tools/clippy/clippy_lints/src/zero_div_zero.rs
src/tools/clippy/clippy_workspace_tests/Cargo.toml
src/tools/clippy/clippy_workspace_tests/src/main.rs
src/tools/clippy/clippy_workspace_tests/subcrate/Cargo.toml
src/tools/clippy/clippy_workspace_tests/subcrate/src/lib.rs
src/tools/clippy/doc/adding_lints.md
src/tools/clippy/doc/backport.md
src/tools/clippy/doc/changelog_update.md
src/tools/clippy/doc/release.md
src/tools/clippy/etc/relicense/RELICENSE_DOCUMENTATION.md
src/tools/clippy/etc/relicense/contributors.txt
src/tools/clippy/etc/relicense/relicense_comments.txt
src/tools/clippy/mini-macro/Cargo.toml
src/tools/clippy/mini-macro/src/lib.rs
src/tools/clippy/rust-toolchain
src/tools/clippy/rustc_tools_util/Cargo.toml
src/tools/clippy/rustc_tools_util/README.md
src/tools/clippy/rustc_tools_util/src/lib.rs
src/tools/clippy/rustfmt.toml
src/tools/clippy/setup-toolchain.sh
src/tools/clippy/src/driver.rs
src/tools/clippy/src/lintlist/lint.rs
src/tools/clippy/src/lintlist/mod.rs
src/tools/clippy/src/main.rs
src/tools/clippy/tests/auxiliary/test_macro.rs
src/tools/clippy/tests/cargo/mod.rs
src/tools/clippy/tests/compile-test.rs
src/tools/clippy/tests/dogfood.rs
src/tools/clippy/tests/fmt.rs
src/tools/clippy/tests/integration.rs
src/tools/clippy/tests/missing-test-files.rs
src/tools/clippy/tests/ui-toml/bad_toml/clippy.toml
src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.rs
src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.stderr
src/tools/clippy/tests/ui-toml/bad_toml_type/clippy.toml
src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.rs
src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr
src/tools/clippy/tests/ui-toml/conf_deprecated_key/clippy.toml
src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs
src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr
src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/clippy.toml
src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/test.rs
src/tools/clippy/tests/ui-toml/fn_params_excessive_bools/test.stderr
src/tools/clippy/tests/ui-toml/functions_maxlines/clippy.toml
src/tools/clippy/tests/ui-toml/functions_maxlines/test.rs
src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr
src/tools/clippy/tests/ui-toml/good_toml_no_false_negatives/clippy.toml
src/tools/clippy/tests/ui-toml/good_toml_no_false_negatives/conf_no_false_negatives.rs
src/tools/clippy/tests/ui-toml/struct_excessive_bools/clippy.toml
src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.rs
src/tools/clippy/tests/ui-toml/struct_excessive_bools/test.stderr
src/tools/clippy/tests/ui-toml/toml_blacklist/clippy.toml
src/tools/clippy/tests/ui-toml/toml_blacklist/conf_french_blacklisted_name.rs
src/tools/clippy/tests/ui-toml/toml_blacklist/conf_french_blacklisted_name.stderr
src/tools/clippy/tests/ui-toml/toml_trivially_copy/clippy.toml
src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.rs
src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.stderr
src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml
src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs
src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
src/tools/clippy/tests/ui-toml/update-all-references.sh
src/tools/clippy/tests/ui-toml/update-references.sh
src/tools/clippy/tests/ui-toml/vec_box_sized/clippy.toml
src/tools/clippy/tests/ui-toml/vec_box_sized/test.rs
src/tools/clippy/tests/ui-toml/vec_box_sized/test.stderr
src/tools/clippy/tests/ui-toml/zero_single_char_names/clippy.toml
src/tools/clippy/tests/ui-toml/zero_single_char_names/zero_single_char_names.rs
src/tools/clippy/tests/ui/absurd-extreme-comparisons.rs
src/tools/clippy/tests/ui/absurd-extreme-comparisons.stderr
src/tools/clippy/tests/ui/approx_const.rs
src/tools/clippy/tests/ui/approx_const.stderr
src/tools/clippy/tests/ui/as_conversions.rs
src/tools/clippy/tests/ui/as_conversions.stderr
src/tools/clippy/tests/ui/assertions_on_constants.rs
src/tools/clippy/tests/ui/assertions_on_constants.stderr
src/tools/clippy/tests/ui/assign_ops.fixed
src/tools/clippy/tests/ui/assign_ops.rs
src/tools/clippy/tests/ui/assign_ops.stderr
src/tools/clippy/tests/ui/assign_ops2.rs
src/tools/clippy/tests/ui/assign_ops2.stderr
src/tools/clippy/tests/ui/atomic_ordering_bool.rs
src/tools/clippy/tests/ui/atomic_ordering_bool.stderr
src/tools/clippy/tests/ui/atomic_ordering_fence.rs
src/tools/clippy/tests/ui/atomic_ordering_fence.stderr
src/tools/clippy/tests/ui/atomic_ordering_int.rs
src/tools/clippy/tests/ui/atomic_ordering_int.stderr
src/tools/clippy/tests/ui/atomic_ordering_ptr.rs
src/tools/clippy/tests/ui/atomic_ordering_ptr.stderr
src/tools/clippy/tests/ui/atomic_ordering_uint.rs
src/tools/clippy/tests/ui/atomic_ordering_uint.stderr
src/tools/clippy/tests/ui/attrs.rs
src/tools/clippy/tests/ui/attrs.stderr
src/tools/clippy/tests/ui/author.rs
src/tools/clippy/tests/ui/author.stdout
src/tools/clippy/tests/ui/author/blocks.rs
src/tools/clippy/tests/ui/author/blocks.stdout
src/tools/clippy/tests/ui/author/call.rs
src/tools/clippy/tests/ui/author/call.stdout
src/tools/clippy/tests/ui/author/for_loop.rs
src/tools/clippy/tests/ui/author/for_loop.stdout
src/tools/clippy/tests/ui/author/if.rs
src/tools/clippy/tests/ui/author/if.stdout
src/tools/clippy/tests/ui/author/issue_3849.rs
src/tools/clippy/tests/ui/author/issue_3849.stdout
src/tools/clippy/tests/ui/author/matches.rs
src/tools/clippy/tests/ui/author/matches.stdout
src/tools/clippy/tests/ui/auxiliary/doc_unsafe_macros.rs
src/tools/clippy/tests/ui/auxiliary/implicit_hasher_macros.rs
src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
src/tools/clippy/tests/ui/auxiliary/option_helpers.rs
src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
src/tools/clippy/tests/ui/auxiliary/use_self_macro.rs
src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs
src/tools/clippy/tests/ui/await_holding_lock.rs
src/tools/clippy/tests/ui/await_holding_lock.stderr
src/tools/clippy/tests/ui/bit_masks.rs
src/tools/clippy/tests/ui/bit_masks.stderr
src/tools/clippy/tests/ui/blacklisted_name.rs
src/tools/clippy/tests/ui/blacklisted_name.stderr
src/tools/clippy/tests/ui/block_in_if_condition.fixed
src/tools/clippy/tests/ui/block_in_if_condition.rs
src/tools/clippy/tests/ui/block_in_if_condition.stderr
src/tools/clippy/tests/ui/block_in_if_condition_closure.rs
src/tools/clippy/tests/ui/block_in_if_condition_closure.stderr
src/tools/clippy/tests/ui/bool_comparison.fixed
src/tools/clippy/tests/ui/bool_comparison.rs
src/tools/clippy/tests/ui/bool_comparison.stderr
src/tools/clippy/tests/ui/borrow_box.rs
src/tools/clippy/tests/ui/borrow_box.stderr
src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs
src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr
src/tools/clippy/tests/ui/box_vec.rs
src/tools/clippy/tests/ui/box_vec.stderr
src/tools/clippy/tests/ui/builtin-type-shadow.rs
src/tools/clippy/tests/ui/builtin-type-shadow.stderr
src/tools/clippy/tests/ui/bytecount.rs
src/tools/clippy/tests/ui/bytecount.stderr
src/tools/clippy/tests/ui/cast.rs
src/tools/clippy/tests/ui/cast.stderr
src/tools/clippy/tests/ui/cast_alignment.rs
src/tools/clippy/tests/ui/cast_alignment.stderr
src/tools/clippy/tests/ui/cast_lossless_float.fixed
src/tools/clippy/tests/ui/cast_lossless_float.rs
src/tools/clippy/tests/ui/cast_lossless_float.stderr
src/tools/clippy/tests/ui/cast_lossless_integer.fixed
src/tools/clippy/tests/ui/cast_lossless_integer.rs
src/tools/clippy/tests/ui/cast_lossless_integer.stderr
src/tools/clippy/tests/ui/cast_ref_to_mut.rs
src/tools/clippy/tests/ui/cast_ref_to_mut.stderr
src/tools/clippy/tests/ui/cast_size.rs
src/tools/clippy/tests/ui/cast_size.stderr
src/tools/clippy/tests/ui/cast_size_32bit.rs
src/tools/clippy/tests/ui/cast_size_32bit.stderr
src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed
src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs
src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr
src/tools/clippy/tests/ui/char_lit_as_u8.rs
src/tools/clippy/tests/ui/char_lit_as_u8.stderr
src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.fixed
src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.rs
src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.stderr
src/tools/clippy/tests/ui/checked_conversions.fixed
src/tools/clippy/tests/ui/checked_conversions.rs
src/tools/clippy/tests/ui/checked_conversions.stderr
src/tools/clippy/tests/ui/checked_conversions.stdout
src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs
src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr
src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs
src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr
src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
src/tools/clippy/tests/ui/clone_on_copy_impl.rs
src/tools/clippy/tests/ui/clone_on_copy_mut.rs
src/tools/clippy/tests/ui/cmp_nan.rs
src/tools/clippy/tests/ui/cmp_nan.stderr
src/tools/clippy/tests/ui/cmp_null.rs
src/tools/clippy/tests/ui/cmp_null.stderr
src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed
src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs
src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr
src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs
src/tools/clippy/tests/ui/cmp_owned/without_suggestion.stderr
src/tools/clippy/tests/ui/cognitive_complexity.rs
src/tools/clippy/tests/ui/cognitive_complexity.stderr
src/tools/clippy/tests/ui/cognitive_complexity_attr_used.rs
src/tools/clippy/tests/ui/cognitive_complexity_attr_used.stderr
src/tools/clippy/tests/ui/collapsible_else_if.fixed
src/tools/clippy/tests/ui/collapsible_else_if.rs
src/tools/clippy/tests/ui/collapsible_else_if.stderr
src/tools/clippy/tests/ui/collapsible_if.fixed
src/tools/clippy/tests/ui/collapsible_if.rs
src/tools/clippy/tests/ui/collapsible_if.stderr
src/tools/clippy/tests/ui/collapsible_span_lint_calls.fixed
src/tools/clippy/tests/ui/collapsible_span_lint_calls.rs
src/tools/clippy/tests/ui/collapsible_span_lint_calls.stderr
src/tools/clippy/tests/ui/comparison_chain.rs
src/tools/clippy/tests/ui/comparison_chain.stderr
src/tools/clippy/tests/ui/complex_types.rs
src/tools/clippy/tests/ui/complex_types.stderr
src/tools/clippy/tests/ui/copy_iterator.rs
src/tools/clippy/tests/ui/copy_iterator.stderr
src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs
src/tools/clippy/tests/ui/crashes/auxiliary/ice-4727-aux.rs
src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs
src/tools/clippy/tests/ui/crashes/auxiliary/use_self_macro.rs
src/tools/clippy/tests/ui/crashes/cc_seme.rs
src/tools/clippy/tests/ui/crashes/enum-glob-import-crate.rs
src/tools/clippy/tests/ui/crashes/ice-1588.rs
src/tools/clippy/tests/ui/crashes/ice-1782.rs
src/tools/clippy/tests/ui/crashes/ice-1969.rs
src/tools/clippy/tests/ui/crashes/ice-2499.rs
src/tools/clippy/tests/ui/crashes/ice-2594.rs
src/tools/clippy/tests/ui/crashes/ice-2636.rs
src/tools/clippy/tests/ui/crashes/ice-2636.stderr
src/tools/clippy/tests/ui/crashes/ice-2727.rs
src/tools/clippy/tests/ui/crashes/ice-2760.rs
src/tools/clippy/tests/ui/crashes/ice-2774.rs
src/tools/clippy/tests/ui/crashes/ice-2862.rs
src/tools/clippy/tests/ui/crashes/ice-2865.rs
src/tools/clippy/tests/ui/crashes/ice-3151.rs
src/tools/clippy/tests/ui/crashes/ice-3462.rs
src/tools/clippy/tests/ui/crashes/ice-360.rs
src/tools/clippy/tests/ui/crashes/ice-360.stderr
src/tools/clippy/tests/ui/crashes/ice-3717.rs
src/tools/clippy/tests/ui/crashes/ice-3717.stderr
src/tools/clippy/tests/ui/crashes/ice-3741.rs
src/tools/clippy/tests/ui/crashes/ice-3747.rs
src/tools/clippy/tests/ui/crashes/ice-3891.rs
src/tools/clippy/tests/ui/crashes/ice-3891.stderr
src/tools/clippy/tests/ui/crashes/ice-4121.rs
src/tools/clippy/tests/ui/crashes/ice-4545.rs
src/tools/clippy/tests/ui/crashes/ice-4579.rs
src/tools/clippy/tests/ui/crashes/ice-4671.rs
src/tools/clippy/tests/ui/crashes/ice-4727.rs
src/tools/clippy/tests/ui/crashes/ice-4760.rs
src/tools/clippy/tests/ui/crashes/ice-4775.rs
src/tools/clippy/tests/ui/crashes/ice-4968.rs
src/tools/clippy/tests/ui/crashes/ice-5207.rs
src/tools/clippy/tests/ui/crashes/ice-5223.rs
src/tools/clippy/tests/ui/crashes/ice-5238.rs
src/tools/clippy/tests/ui/crashes/ice-5497.rs
src/tools/clippy/tests/ui/crashes/ice-700.rs
src/tools/clippy/tests/ui/crashes/ice_exacte_size.rs
src/tools/clippy/tests/ui/crashes/if_same_then_else.rs
src/tools/clippy/tests/ui/crashes/inherent_impl.rs
src/tools/clippy/tests/ui/crashes/issue-825.rs
src/tools/clippy/tests/ui/crashes/issues_loop_mut_cond.rs
src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs
src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs
src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs
src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.rs
src/tools/clippy/tests/ui/crashes/procedural_macro.rs
src/tools/clippy/tests/ui/crashes/regressions.rs
src/tools/clippy/tests/ui/crashes/returns.rs
src/tools/clippy/tests/ui/crashes/shadow.rs
src/tools/clippy/tests/ui/crashes/single-match-else.rs
src/tools/clippy/tests/ui/crashes/trivial_bounds.rs
src/tools/clippy/tests/ui/crashes/used_underscore_binding_macro.rs
src/tools/clippy/tests/ui/crashes/whitelist/clippy.toml
src/tools/clippy/tests/ui/crashes/whitelist/conf_whitelisted.rs
src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs
src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.stderr
src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs
src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.rs
src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.stderr
src/tools/clippy/tests/ui/cstring.rs
src/tools/clippy/tests/ui/cstring.stderr
src/tools/clippy/tests/ui/custom_ice_message.rs
src/tools/clippy/tests/ui/custom_ice_message.stderr
src/tools/clippy/tests/ui/dbg_macro.rs
src/tools/clippy/tests/ui/dbg_macro.stderr
src/tools/clippy/tests/ui/debug_assert_with_mut_call.rs
src/tools/clippy/tests/ui/debug_assert_with_mut_call.stderr
src/tools/clippy/tests/ui/decimal_literal_representation.fixed
src/tools/clippy/tests/ui/decimal_literal_representation.rs
src/tools/clippy/tests/ui/decimal_literal_representation.stderr
src/tools/clippy/tests/ui/declare_interior_mutable_const.rs
src/tools/clippy/tests/ui/declare_interior_mutable_const.stderr
src/tools/clippy/tests/ui/def_id_nocore.rs
src/tools/clippy/tests/ui/def_id_nocore.stderr
src/tools/clippy/tests/ui/default_lint.rs
src/tools/clippy/tests/ui/default_lint.stderr
src/tools/clippy/tests/ui/default_trait_access.rs
src/tools/clippy/tests/ui/default_trait_access.stderr
src/tools/clippy/tests/ui/deprecated.rs
src/tools/clippy/tests/ui/deprecated.stderr
src/tools/clippy/tests/ui/deprecated_old.rs
src/tools/clippy/tests/ui/deprecated_old.stderr
src/tools/clippy/tests/ui/deref_addrof.fixed
src/tools/clippy/tests/ui/deref_addrof.rs
src/tools/clippy/tests/ui/deref_addrof.stderr
src/tools/clippy/tests/ui/deref_addrof_double_trigger.rs
src/tools/clippy/tests/ui/deref_addrof_double_trigger.stderr
src/tools/clippy/tests/ui/deref_addrof_macro.rs
src/tools/clippy/tests/ui/dereference.fixed
src/tools/clippy/tests/ui/dereference.rs
src/tools/clippy/tests/ui/dereference.stderr
src/tools/clippy/tests/ui/derive.rs
src/tools/clippy/tests/ui/derive.stderr
src/tools/clippy/tests/ui/derive_hash_xor_eq.rs
src/tools/clippy/tests/ui/derive_hash_xor_eq.stderr
src/tools/clippy/tests/ui/diverging_sub_expression.rs
src/tools/clippy/tests/ui/diverging_sub_expression.stderr
src/tools/clippy/tests/ui/dlist.rs
src/tools/clippy/tests/ui/dlist.stderr
src/tools/clippy/tests/ui/doc.rs
src/tools/clippy/tests/ui/doc.stderr
src/tools/clippy/tests/ui/doc_errors.rs
src/tools/clippy/tests/ui/doc_errors.stderr
src/tools/clippy/tests/ui/doc_unsafe.rs
src/tools/clippy/tests/ui/doc_unsafe.stderr
src/tools/clippy/tests/ui/double_comparison.fixed
src/tools/clippy/tests/ui/double_comparison.rs
src/tools/clippy/tests/ui/double_comparison.stderr
src/tools/clippy/tests/ui/double_must_use.rs
src/tools/clippy/tests/ui/double_must_use.stderr
src/tools/clippy/tests/ui/double_neg.rs
src/tools/clippy/tests/ui/double_neg.stderr
src/tools/clippy/tests/ui/double_parens.rs
src/tools/clippy/tests/ui/double_parens.stderr
src/tools/clippy/tests/ui/drop_bounds.rs
src/tools/clippy/tests/ui/drop_bounds.stderr
src/tools/clippy/tests/ui/drop_forget_copy.rs
src/tools/clippy/tests/ui/drop_forget_copy.stderr
src/tools/clippy/tests/ui/drop_ref.rs
src/tools/clippy/tests/ui/drop_ref.stderr
src/tools/clippy/tests/ui/duplicate_underscore_argument.rs
src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr
src/tools/clippy/tests/ui/duration_subsec.fixed
src/tools/clippy/tests/ui/duration_subsec.rs
src/tools/clippy/tests/ui/duration_subsec.stderr
src/tools/clippy/tests/ui/else_if_without_else.rs
src/tools/clippy/tests/ui/else_if_without_else.stderr
src/tools/clippy/tests/ui/empty_enum.rs
src/tools/clippy/tests/ui/empty_enum.stderr
src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs
src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr
src/tools/clippy/tests/ui/empty_loop.rs
src/tools/clippy/tests/ui/empty_loop.stderr
src/tools/clippy/tests/ui/entry_fixable.fixed
src/tools/clippy/tests/ui/entry_fixable.rs
src/tools/clippy/tests/ui/entry_fixable.stderr
src/tools/clippy/tests/ui/entry_unfixable.rs
src/tools/clippy/tests/ui/entry_unfixable.stderr
src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs
src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr
src/tools/clippy/tests/ui/enum_glob_use.fixed
src/tools/clippy/tests/ui/enum_glob_use.rs
src/tools/clippy/tests/ui/enum_glob_use.stderr
src/tools/clippy/tests/ui/enum_variants.rs
src/tools/clippy/tests/ui/enum_variants.stderr
src/tools/clippy/tests/ui/eq_op.rs
src/tools/clippy/tests/ui/eq_op.stderr
src/tools/clippy/tests/ui/erasing_op.rs
src/tools/clippy/tests/ui/erasing_op.stderr
src/tools/clippy/tests/ui/escape_analysis.rs
src/tools/clippy/tests/ui/escape_analysis.stderr
src/tools/clippy/tests/ui/eta.fixed
src/tools/clippy/tests/ui/eta.rs
src/tools/clippy/tests/ui/eta.stderr
src/tools/clippy/tests/ui/eval_order_dependence.rs
src/tools/clippy/tests/ui/eval_order_dependence.stderr
src/tools/clippy/tests/ui/excessive_precision.fixed
src/tools/clippy/tests/ui/excessive_precision.rs
src/tools/clippy/tests/ui/excessive_precision.stderr
src/tools/clippy/tests/ui/exit1.rs
src/tools/clippy/tests/ui/exit1.stderr
src/tools/clippy/tests/ui/exit2.rs
src/tools/clippy/tests/ui/exit2.stderr
src/tools/clippy/tests/ui/exit3.rs
src/tools/clippy/tests/ui/expect.rs
src/tools/clippy/tests/ui/expect.stderr
src/tools/clippy/tests/ui/expect_fun_call.fixed
src/tools/clippy/tests/ui/expect_fun_call.rs
src/tools/clippy/tests/ui/expect_fun_call.stderr
src/tools/clippy/tests/ui/explicit_counter_loop.rs
src/tools/clippy/tests/ui/explicit_counter_loop.stderr
src/tools/clippy/tests/ui/explicit_write.fixed
src/tools/clippy/tests/ui/explicit_write.rs
src/tools/clippy/tests/ui/explicit_write.stderr
src/tools/clippy/tests/ui/explicit_write_non_rustfix.rs
src/tools/clippy/tests/ui/explicit_write_non_rustfix.stderr
src/tools/clippy/tests/ui/extra_unused_lifetimes.rs
src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr
src/tools/clippy/tests/ui/fallible_impl_from.rs
src/tools/clippy/tests/ui/fallible_impl_from.stderr
src/tools/clippy/tests/ui/filetype_is_file.rs
src/tools/clippy/tests/ui/filetype_is_file.stderr
src/tools/clippy/tests/ui/filter_map_next.rs
src/tools/clippy/tests/ui/filter_map_next.stderr
src/tools/clippy/tests/ui/filter_methods.rs
src/tools/clippy/tests/ui/filter_methods.stderr
src/tools/clippy/tests/ui/find_map.rs
src/tools/clippy/tests/ui/find_map.stderr
src/tools/clippy/tests/ui/float_arithmetic.rs
src/tools/clippy/tests/ui/float_arithmetic.stderr
src/tools/clippy/tests/ui/float_cmp.rs
src/tools/clippy/tests/ui/float_cmp.stderr
src/tools/clippy/tests/ui/float_cmp_const.rs
src/tools/clippy/tests/ui/float_cmp_const.stderr
src/tools/clippy/tests/ui/floating_point_abs.fixed
src/tools/clippy/tests/ui/floating_point_abs.rs
src/tools/clippy/tests/ui/floating_point_abs.stderr
src/tools/clippy/tests/ui/floating_point_exp.fixed
src/tools/clippy/tests/ui/floating_point_exp.rs
src/tools/clippy/tests/ui/floating_point_exp.stderr
src/tools/clippy/tests/ui/floating_point_log.fixed
src/tools/clippy/tests/ui/floating_point_log.rs
src/tools/clippy/tests/ui/floating_point_log.stderr
src/tools/clippy/tests/ui/floating_point_mul_add.fixed
src/tools/clippy/tests/ui/floating_point_mul_add.rs
src/tools/clippy/tests/ui/floating_point_mul_add.stderr
src/tools/clippy/tests/ui/floating_point_powf.fixed
src/tools/clippy/tests/ui/floating_point_powf.rs
src/tools/clippy/tests/ui/floating_point_powf.stderr
src/tools/clippy/tests/ui/fn_address_comparisons.rs
src/tools/clippy/tests/ui/fn_address_comparisons.stderr
src/tools/clippy/tests/ui/fn_params_excessive_bools.rs
src/tools/clippy/tests/ui/fn_params_excessive_bools.stderr
src/tools/clippy/tests/ui/fn_to_numeric_cast.rs
src/tools/clippy/tests/ui/fn_to_numeric_cast.stderr
src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs
src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.stderr
src/tools/clippy/tests/ui/for_kv_map.rs
src/tools/clippy/tests/ui/for_kv_map.stderr
src/tools/clippy/tests/ui/for_loop_fixable.fixed
src/tools/clippy/tests/ui/for_loop_fixable.rs
src/tools/clippy/tests/ui/for_loop_fixable.stderr
src/tools/clippy/tests/ui/for_loop_over_option_result.rs
src/tools/clippy/tests/ui/for_loop_over_option_result.stderr
src/tools/clippy/tests/ui/for_loop_unfixable.rs
src/tools/clippy/tests/ui/for_loop_unfixable.stderr
src/tools/clippy/tests/ui/forget_ref.rs
src/tools/clippy/tests/ui/forget_ref.stderr
src/tools/clippy/tests/ui/format.fixed
src/tools/clippy/tests/ui/format.rs
src/tools/clippy/tests/ui/format.stderr
src/tools/clippy/tests/ui/formatting.rs
src/tools/clippy/tests/ui/formatting.stderr
src/tools/clippy/tests/ui/functions.rs
src/tools/clippy/tests/ui/functions.stderr
src/tools/clippy/tests/ui/functions_maxlines.rs
src/tools/clippy/tests/ui/functions_maxlines.stderr
src/tools/clippy/tests/ui/future_not_send.rs
src/tools/clippy/tests/ui/future_not_send.stderr
src/tools/clippy/tests/ui/get_last_with_len.fixed
src/tools/clippy/tests/ui/get_last_with_len.rs
src/tools/clippy/tests/ui/get_last_with_len.stderr
src/tools/clippy/tests/ui/get_unwrap.fixed
src/tools/clippy/tests/ui/get_unwrap.rs
src/tools/clippy/tests/ui/get_unwrap.stderr
src/tools/clippy/tests/ui/identity_conversion.fixed
src/tools/clippy/tests/ui/identity_conversion.rs
src/tools/clippy/tests/ui/identity_conversion.stderr
src/tools/clippy/tests/ui/identity_op.rs
src/tools/clippy/tests/ui/identity_op.stderr
src/tools/clippy/tests/ui/if_let_mutex.rs
src/tools/clippy/tests/ui/if_let_mutex.stderr
src/tools/clippy/tests/ui/if_let_some_result.fixed
src/tools/clippy/tests/ui/if_let_some_result.rs
src/tools/clippy/tests/ui/if_let_some_result.stderr
src/tools/clippy/tests/ui/if_not_else.rs
src/tools/clippy/tests/ui/if_not_else.stderr
src/tools/clippy/tests/ui/if_same_then_else.rs
src/tools/clippy/tests/ui/if_same_then_else.stderr
src/tools/clippy/tests/ui/if_same_then_else2.rs
src/tools/clippy/tests/ui/if_same_then_else2.stderr
src/tools/clippy/tests/ui/ifs_same_cond.rs
src/tools/clippy/tests/ui/ifs_same_cond.stderr
src/tools/clippy/tests/ui/impl.rs
src/tools/clippy/tests/ui/impl.stderr
src/tools/clippy/tests/ui/implicit_hasher.rs
src/tools/clippy/tests/ui/implicit_hasher.stderr
src/tools/clippy/tests/ui/implicit_return.fixed
src/tools/clippy/tests/ui/implicit_return.rs
src/tools/clippy/tests/ui/implicit_return.stderr
src/tools/clippy/tests/ui/implicit_saturating_sub.fixed
src/tools/clippy/tests/ui/implicit_saturating_sub.rs
src/tools/clippy/tests/ui/implicit_saturating_sub.stderr
src/tools/clippy/tests/ui/inconsistent_digit_grouping.fixed
src/tools/clippy/tests/ui/inconsistent_digit_grouping.rs
src/tools/clippy/tests/ui/inconsistent_digit_grouping.stderr
src/tools/clippy/tests/ui/indexing_slicing_index.rs
src/tools/clippy/tests/ui/indexing_slicing_index.stderr
src/tools/clippy/tests/ui/indexing_slicing_slice.rs
src/tools/clippy/tests/ui/indexing_slicing_slice.stderr
src/tools/clippy/tests/ui/inefficient_to_string.fixed
src/tools/clippy/tests/ui/inefficient_to_string.rs
src/tools/clippy/tests/ui/inefficient_to_string.stderr
src/tools/clippy/tests/ui/infallible_destructuring_match.fixed
src/tools/clippy/tests/ui/infallible_destructuring_match.rs
src/tools/clippy/tests/ui/infallible_destructuring_match.stderr
src/tools/clippy/tests/ui/infinite_iter.rs
src/tools/clippy/tests/ui/infinite_iter.stderr
src/tools/clippy/tests/ui/infinite_loop.rs
src/tools/clippy/tests/ui/infinite_loop.stderr
src/tools/clippy/tests/ui/inherent_to_string.rs
src/tools/clippy/tests/ui/inherent_to_string.stderr
src/tools/clippy/tests/ui/inline_fn_without_body.fixed
src/tools/clippy/tests/ui/inline_fn_without_body.rs
src/tools/clippy/tests/ui/inline_fn_without_body.stderr
src/tools/clippy/tests/ui/int_plus_one.fixed
src/tools/clippy/tests/ui/int_plus_one.rs
src/tools/clippy/tests/ui/int_plus_one.stderr
src/tools/clippy/tests/ui/integer_arithmetic.rs
src/tools/clippy/tests/ui/integer_arithmetic.stderr
src/tools/clippy/tests/ui/integer_division.rs
src/tools/clippy/tests/ui/integer_division.stderr
src/tools/clippy/tests/ui/into_iter_on_ref.fixed
src/tools/clippy/tests/ui/into_iter_on_ref.rs
src/tools/clippy/tests/ui/into_iter_on_ref.stderr
src/tools/clippy/tests/ui/invalid_upcast_comparisons.rs
src/tools/clippy/tests/ui/invalid_upcast_comparisons.stderr
src/tools/clippy/tests/ui/issue-3145.rs
src/tools/clippy/tests/ui/issue-3145.stderr
src/tools/clippy/tests/ui/issue-3746.rs
src/tools/clippy/tests/ui/issue_2356.rs
src/tools/clippy/tests/ui/issue_2356.stderr
src/tools/clippy/tests/ui/issue_4266.rs
src/tools/clippy/tests/ui/issue_4266.stderr
src/tools/clippy/tests/ui/item_after_statement.rs
src/tools/clippy/tests/ui/item_after_statement.stderr
src/tools/clippy/tests/ui/iter_cloned_collect.fixed
src/tools/clippy/tests/ui/iter_cloned_collect.rs
src/tools/clippy/tests/ui/iter_cloned_collect.stderr
src/tools/clippy/tests/ui/iter_nth.rs
src/tools/clippy/tests/ui/iter_nth.stderr
src/tools/clippy/tests/ui/iter_nth_zero.fixed
src/tools/clippy/tests/ui/iter_nth_zero.rs
src/tools/clippy/tests/ui/iter_nth_zero.stderr
src/tools/clippy/tests/ui/iter_skip_next.rs
src/tools/clippy/tests/ui/iter_skip_next.stderr
src/tools/clippy/tests/ui/iterator_step_by_zero.rs
src/tools/clippy/tests/ui/iterator_step_by_zero.stderr
src/tools/clippy/tests/ui/large_const_arrays.fixed
src/tools/clippy/tests/ui/large_const_arrays.rs
src/tools/clippy/tests/ui/large_const_arrays.stderr
src/tools/clippy/tests/ui/large_digit_groups.fixed
src/tools/clippy/tests/ui/large_digit_groups.rs
src/tools/clippy/tests/ui/large_digit_groups.stderr
src/tools/clippy/tests/ui/large_enum_variant.rs
src/tools/clippy/tests/ui/large_enum_variant.stderr
src/tools/clippy/tests/ui/large_stack_arrays.rs
src/tools/clippy/tests/ui/large_stack_arrays.stderr
src/tools/clippy/tests/ui/len_without_is_empty.rs
src/tools/clippy/tests/ui/len_without_is_empty.stderr
src/tools/clippy/tests/ui/len_zero.fixed
src/tools/clippy/tests/ui/len_zero.rs
src/tools/clippy/tests/ui/len_zero.stderr
src/tools/clippy/tests/ui/let_if_seq.rs
src/tools/clippy/tests/ui/let_if_seq.stderr
src/tools/clippy/tests/ui/let_return.rs
src/tools/clippy/tests/ui/let_return.stderr
src/tools/clippy/tests/ui/let_underscore_lock.rs
src/tools/clippy/tests/ui/let_underscore_lock.stderr
src/tools/clippy/tests/ui/let_underscore_must_use.rs
src/tools/clippy/tests/ui/let_underscore_must_use.stderr
src/tools/clippy/tests/ui/let_unit.fixed
src/tools/clippy/tests/ui/let_unit.rs
src/tools/clippy/tests/ui/let_unit.stderr
src/tools/clippy/tests/ui/lint_without_lint_pass.rs
src/tools/clippy/tests/ui/lint_without_lint_pass.stderr
src/tools/clippy/tests/ui/literals.rs
src/tools/clippy/tests/ui/literals.stderr
src/tools/clippy/tests/ui/logic_bug.rs
src/tools/clippy/tests/ui/logic_bug.stderr
src/tools/clippy/tests/ui/lossy_float_literal.fixed
src/tools/clippy/tests/ui/lossy_float_literal.rs
src/tools/clippy/tests/ui/lossy_float_literal.stderr
src/tools/clippy/tests/ui/macro_use_imports.rs
src/tools/clippy/tests/ui/macro_use_imports.stderr
src/tools/clippy/tests/ui/manual_memcpy.rs
src/tools/clippy/tests/ui/manual_memcpy.stderr
src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed
src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs
src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr
src/tools/clippy/tests/ui/many_single_char_names.rs
src/tools/clippy/tests/ui/many_single_char_names.stderr
src/tools/clippy/tests/ui/map_clone.fixed
src/tools/clippy/tests/ui/map_clone.rs
src/tools/clippy/tests/ui/map_clone.stderr
src/tools/clippy/tests/ui/map_flatten.fixed
src/tools/clippy/tests/ui/map_flatten.rs
src/tools/clippy/tests/ui/map_flatten.stderr
src/tools/clippy/tests/ui/map_unit_fn.rs
src/tools/clippy/tests/ui/match_as_ref.fixed
src/tools/clippy/tests/ui/match_as_ref.rs
src/tools/clippy/tests/ui/match_as_ref.stderr
src/tools/clippy/tests/ui/match_bool.rs
src/tools/clippy/tests/ui/match_bool.stderr
src/tools/clippy/tests/ui/match_on_vec_items.rs
src/tools/clippy/tests/ui/match_on_vec_items.stderr
src/tools/clippy/tests/ui/match_overlapping_arm.rs
src/tools/clippy/tests/ui/match_overlapping_arm.stderr
src/tools/clippy/tests/ui/match_ref_pats.rs
src/tools/clippy/tests/ui/match_ref_pats.stderr
src/tools/clippy/tests/ui/match_same_arms.rs
src/tools/clippy/tests/ui/match_same_arms.stderr
src/tools/clippy/tests/ui/match_same_arms2.rs
src/tools/clippy/tests/ui/match_same_arms2.stderr
src/tools/clippy/tests/ui/match_single_binding.fixed
src/tools/clippy/tests/ui/match_single_binding.rs
src/tools/clippy/tests/ui/match_single_binding.stderr
src/tools/clippy/tests/ui/match_wild_err_arm.rs
src/tools/clippy/tests/ui/match_wild_err_arm.stderr
src/tools/clippy/tests/ui/mem_discriminant.fixed
src/tools/clippy/tests/ui/mem_discriminant.rs
src/tools/clippy/tests/ui/mem_discriminant.stderr
src/tools/clippy/tests/ui/mem_discriminant_unfixable.rs
src/tools/clippy/tests/ui/mem_discriminant_unfixable.stderr
src/tools/clippy/tests/ui/mem_forget.rs
src/tools/clippy/tests/ui/mem_forget.stderr
src/tools/clippy/tests/ui/mem_replace.fixed
src/tools/clippy/tests/ui/mem_replace.rs
src/tools/clippy/tests/ui/mem_replace.stderr
src/tools/clippy/tests/ui/mem_replace_macro.rs
src/tools/clippy/tests/ui/mem_replace_macro.stderr
src/tools/clippy/tests/ui/methods.rs
src/tools/clippy/tests/ui/methods.stderr
src/tools/clippy/tests/ui/min_max.rs
src/tools/clippy/tests/ui/min_max.stderr
src/tools/clippy/tests/ui/mismatched_target_os_non_unix.fixed
src/tools/clippy/tests/ui/mismatched_target_os_non_unix.rs
src/tools/clippy/tests/ui/mismatched_target_os_non_unix.stderr
src/tools/clippy/tests/ui/mismatched_target_os_unix.fixed
src/tools/clippy/tests/ui/mismatched_target_os_unix.rs
src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr
src/tools/clippy/tests/ui/missing-doc-crate-missing.rs
src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr
src/tools/clippy/tests/ui/missing-doc-crate.rs
src/tools/clippy/tests/ui/missing-doc-crate.stderr
src/tools/clippy/tests/ui/missing-doc-impl.rs
src/tools/clippy/tests/ui/missing-doc-impl.stderr
src/tools/clippy/tests/ui/missing-doc.rs
src/tools/clippy/tests/ui/missing-doc.stderr
src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.stderr
src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
src/tools/clippy/tests/ui/missing_inline.rs
src/tools/clippy/tests/ui/missing_inline.stderr
src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed
src/tools/clippy/tests/ui/mistyped_literal_suffix.rs
src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr
src/tools/clippy/tests/ui/module_inception.rs
src/tools/clippy/tests/ui/module_inception.stderr
src/tools/clippy/tests/ui/module_name_repetitions.rs
src/tools/clippy/tests/ui/module_name_repetitions.stderr
src/tools/clippy/tests/ui/modulo_arithmetic_float.rs
src/tools/clippy/tests/ui/modulo_arithmetic_float.stderr
src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs
src/tools/clippy/tests/ui/modulo_arithmetic_integral.stderr
src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs
src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr
src/tools/clippy/tests/ui/modulo_one.rs
src/tools/clippy/tests/ui/modulo_one.stderr
src/tools/clippy/tests/ui/must_use_candidates.fixed
src/tools/clippy/tests/ui/must_use_candidates.rs
src/tools/clippy/tests/ui/must_use_candidates.stderr
src/tools/clippy/tests/ui/must_use_unit.fixed
src/tools/clippy/tests/ui/must_use_unit.rs
src/tools/clippy/tests/ui/must_use_unit.stderr
src/tools/clippy/tests/ui/mut_from_ref.rs
src/tools/clippy/tests/ui/mut_from_ref.stderr
src/tools/clippy/tests/ui/mut_key.rs
src/tools/clippy/tests/ui/mut_key.stderr
src/tools/clippy/tests/ui/mut_mut.rs
src/tools/clippy/tests/ui/mut_mut.stderr
src/tools/clippy/tests/ui/mut_range_bound.rs
src/tools/clippy/tests/ui/mut_range_bound.stderr
src/tools/clippy/tests/ui/mut_reference.rs
src/tools/clippy/tests/ui/mut_reference.stderr
src/tools/clippy/tests/ui/mutex_atomic.rs
src/tools/clippy/tests/ui/mutex_atomic.stderr
src/tools/clippy/tests/ui/needless_bool/fixable.fixed
src/tools/clippy/tests/ui/needless_bool/fixable.rs
src/tools/clippy/tests/ui/needless_bool/fixable.stderr
src/tools/clippy/tests/ui/needless_bool/simple.rs
src/tools/clippy/tests/ui/needless_bool/simple.stderr
src/tools/clippy/tests/ui/needless_borrow.fixed
src/tools/clippy/tests/ui/needless_borrow.rs
src/tools/clippy/tests/ui/needless_borrow.stderr
src/tools/clippy/tests/ui/needless_borrowed_ref.fixed
src/tools/clippy/tests/ui/needless_borrowed_ref.rs
src/tools/clippy/tests/ui/needless_borrowed_ref.stderr
src/tools/clippy/tests/ui/needless_collect.fixed
src/tools/clippy/tests/ui/needless_collect.rs
src/tools/clippy/tests/ui/needless_collect.stderr
src/tools/clippy/tests/ui/needless_continue.rs
src/tools/clippy/tests/ui/needless_continue.stderr
src/tools/clippy/tests/ui/needless_doc_main.rs
src/tools/clippy/tests/ui/needless_doc_main.stderr
src/tools/clippy/tests/ui/needless_lifetimes.rs
src/tools/clippy/tests/ui/needless_lifetimes.stderr
src/tools/clippy/tests/ui/needless_pass_by_value.rs
src/tools/clippy/tests/ui/needless_pass_by_value.stderr
src/tools/clippy/tests/ui/needless_pass_by_value_proc_macro.rs
src/tools/clippy/tests/ui/needless_range_loop.rs
src/tools/clippy/tests/ui/needless_range_loop.stderr
src/tools/clippy/tests/ui/needless_range_loop2.rs
src/tools/clippy/tests/ui/needless_range_loop2.stderr
src/tools/clippy/tests/ui/needless_return.fixed
src/tools/clippy/tests/ui/needless_return.rs
src/tools/clippy/tests/ui/needless_return.stderr
src/tools/clippy/tests/ui/needless_update.rs
src/tools/clippy/tests/ui/needless_update.stderr
src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.rs
src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.stderr
src/tools/clippy/tests/ui/neg_multiply.rs
src/tools/clippy/tests/ui/neg_multiply.stderr
src/tools/clippy/tests/ui/never_loop.rs
src/tools/clippy/tests/ui/never_loop.stderr
src/tools/clippy/tests/ui/new_ret_no_self.rs
src/tools/clippy/tests/ui/new_ret_no_self.stderr
src/tools/clippy/tests/ui/new_without_default.rs
src/tools/clippy/tests/ui/new_without_default.stderr
src/tools/clippy/tests/ui/no_effect.rs
src/tools/clippy/tests/ui/no_effect.stderr
src/tools/clippy/tests/ui/non_expressive_names.rs
src/tools/clippy/tests/ui/non_expressive_names.stderr
src/tools/clippy/tests/ui/non_expressive_names.stdout
src/tools/clippy/tests/ui/nonminimal_bool.rs
src/tools/clippy/tests/ui/nonminimal_bool.stderr
src/tools/clippy/tests/ui/nonminimal_bool_methods.rs
src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr
src/tools/clippy/tests/ui/ok_expect.rs
src/tools/clippy/tests/ui/ok_expect.stderr
src/tools/clippy/tests/ui/op_ref.rs
src/tools/clippy/tests/ui/op_ref.stderr
src/tools/clippy/tests/ui/open_options.rs
src/tools/clippy/tests/ui/open_options.stderr
src/tools/clippy/tests/ui/option_and_then_some.fixed
src/tools/clippy/tests/ui/option_and_then_some.rs
src/tools/clippy/tests/ui/option_and_then_some.stderr
src/tools/clippy/tests/ui/option_as_ref_deref.fixed
src/tools/clippy/tests/ui/option_as_ref_deref.rs
src/tools/clippy/tests/ui/option_as_ref_deref.stderr
src/tools/clippy/tests/ui/option_env_unwrap.rs
src/tools/clippy/tests/ui/option_env_unwrap.stderr
src/tools/clippy/tests/ui/option_map_or_none.fixed
src/tools/clippy/tests/ui/option_map_or_none.rs
src/tools/clippy/tests/ui/option_map_or_none.stderr
src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed
src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs
src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr
src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.rs
src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.stderr
src/tools/clippy/tests/ui/option_map_unwrap_or.rs
src/tools/clippy/tests/ui/option_map_unwrap_or.stderr
src/tools/clippy/tests/ui/option_option.rs
src/tools/clippy/tests/ui/option_option.stderr
src/tools/clippy/tests/ui/or_fun_call.fixed
src/tools/clippy/tests/ui/or_fun_call.rs
src/tools/clippy/tests/ui/or_fun_call.stderr
src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.rs
src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.stderr
src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.rs
src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.stderr
src/tools/clippy/tests/ui/outer_expn_data.fixed
src/tools/clippy/tests/ui/outer_expn_data.rs
src/tools/clippy/tests/ui/outer_expn_data.stderr
src/tools/clippy/tests/ui/overflow_check_conditional.rs
src/tools/clippy/tests/ui/overflow_check_conditional.stderr
src/tools/clippy/tests/ui/panic.rs
src/tools/clippy/tests/ui/panic.stderr
src/tools/clippy/tests/ui/panicking_macros.rs
src/tools/clippy/tests/ui/panicking_macros.stderr
src/tools/clippy/tests/ui/partialeq_ne_impl.rs
src/tools/clippy/tests/ui/partialeq_ne_impl.stderr
src/tools/clippy/tests/ui/path_buf_push_overwrite.fixed
src/tools/clippy/tests/ui/path_buf_push_overwrite.rs
src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr
src/tools/clippy/tests/ui/patterns.fixed
src/tools/clippy/tests/ui/patterns.rs
src/tools/clippy/tests/ui/patterns.stderr
src/tools/clippy/tests/ui/precedence.fixed
src/tools/clippy/tests/ui/precedence.rs
src/tools/clippy/tests/ui/precedence.stderr
src/tools/clippy/tests/ui/print.rs
src/tools/clippy/tests/ui/print.stderr
src/tools/clippy/tests/ui/print_literal.rs
src/tools/clippy/tests/ui/print_literal.stderr
src/tools/clippy/tests/ui/print_with_newline.rs
src/tools/clippy/tests/ui/print_with_newline.stderr
src/tools/clippy/tests/ui/println_empty_string.fixed
src/tools/clippy/tests/ui/println_empty_string.rs
src/tools/clippy/tests/ui/println_empty_string.stderr
src/tools/clippy/tests/ui/proc_macro.rs
src/tools/clippy/tests/ui/proc_macro.stderr
src/tools/clippy/tests/ui/ptr_arg.rs
src/tools/clippy/tests/ui/ptr_arg.stderr
src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed
src/tools/clippy/tests/ui/ptr_offset_with_cast.rs
src/tools/clippy/tests/ui/ptr_offset_with_cast.stderr
src/tools/clippy/tests/ui/question_mark.fixed
src/tools/clippy/tests/ui/question_mark.rs
src/tools/clippy/tests/ui/question_mark.stderr
src/tools/clippy/tests/ui/range.rs
src/tools/clippy/tests/ui/range.stderr
src/tools/clippy/tests/ui/range_plus_minus_one.fixed
src/tools/clippy/tests/ui/range_plus_minus_one.rs
src/tools/clippy/tests/ui/range_plus_minus_one.stderr
src/tools/clippy/tests/ui/redundant_allocation.fixed
src/tools/clippy/tests/ui/redundant_allocation.rs
src/tools/clippy/tests/ui/redundant_allocation.stderr
src/tools/clippy/tests/ui/redundant_clone.fixed
src/tools/clippy/tests/ui/redundant_clone.rs
src/tools/clippy/tests/ui/redundant_clone.stderr
src/tools/clippy/tests/ui/redundant_closure_call.rs
src/tools/clippy/tests/ui/redundant_closure_call.stderr
src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
src/tools/clippy/tests/ui/redundant_field_names.fixed
src/tools/clippy/tests/ui/redundant_field_names.rs
src/tools/clippy/tests/ui/redundant_field_names.stderr
src/tools/clippy/tests/ui/redundant_pattern_matching.fixed
src/tools/clippy/tests/ui/redundant_pattern_matching.rs
src/tools/clippy/tests/ui/redundant_pattern_matching.stderr
src/tools/clippy/tests/ui/redundant_pub_crate.fixed
src/tools/clippy/tests/ui/redundant_pub_crate.rs
src/tools/clippy/tests/ui/redundant_pub_crate.stderr
src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed
src/tools/clippy/tests/ui/redundant_static_lifetimes.rs
src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr
src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.rs
src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr
src/tools/clippy/tests/ui/regex.rs
src/tools/clippy/tests/ui/regex.stderr
src/tools/clippy/tests/ui/rename.fixed
src/tools/clippy/tests/ui/rename.rs
src/tools/clippy/tests/ui/rename.stderr
src/tools/clippy/tests/ui/renamed_builtin_attr.fixed
src/tools/clippy/tests/ui/renamed_builtin_attr.rs
src/tools/clippy/tests/ui/renamed_builtin_attr.stderr
src/tools/clippy/tests/ui/repl_uninit.rs
src/tools/clippy/tests/ui/repl_uninit.stderr
src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs
src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr
src/tools/clippy/tests/ui/result_map_or_into_option.fixed
src/tools/clippy/tests/ui/result_map_or_into_option.rs
src/tools/clippy/tests/ui/result_map_or_into_option.stderr
src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed
src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs
src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr
src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.rs
src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr
src/tools/clippy/tests/ui/result_map_unwrap_or_else.rs
src/tools/clippy/tests/ui/result_map_unwrap_or_else.stderr
src/tools/clippy/tests/ui/same_functions_in_if_condition.rs
src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr
src/tools/clippy/tests/ui/serde.rs
src/tools/clippy/tests/ui/serde.stderr
src/tools/clippy/tests/ui/shadow.rs
src/tools/clippy/tests/ui/shadow.stderr
src/tools/clippy/tests/ui/short_circuit_statement.fixed
src/tools/clippy/tests/ui/short_circuit_statement.rs
src/tools/clippy/tests/ui/short_circuit_statement.stderr
src/tools/clippy/tests/ui/similar_names.rs
src/tools/clippy/tests/ui/similar_names.stderr
src/tools/clippy/tests/ui/single_char_pattern.fixed
src/tools/clippy/tests/ui/single_char_pattern.rs
src/tools/clippy/tests/ui/single_char_pattern.stderr
src/tools/clippy/tests/ui/single_component_path_imports.fixed
src/tools/clippy/tests/ui/single_component_path_imports.rs
src/tools/clippy/tests/ui/single_component_path_imports.stderr
src/tools/clippy/tests/ui/single_match.rs
src/tools/clippy/tests/ui/single_match.stderr
src/tools/clippy/tests/ui/single_match_else.rs
src/tools/clippy/tests/ui/single_match_else.stderr
src/tools/clippy/tests/ui/skip_while_next.rs
src/tools/clippy/tests/ui/skip_while_next.stderr
src/tools/clippy/tests/ui/slow_vector_initialization.rs
src/tools/clippy/tests/ui/slow_vector_initialization.stderr
src/tools/clippy/tests/ui/starts_ends_with.fixed
src/tools/clippy/tests/ui/starts_ends_with.rs
src/tools/clippy/tests/ui/starts_ends_with.stderr
src/tools/clippy/tests/ui/string_add.rs
src/tools/clippy/tests/ui/string_add.stderr
src/tools/clippy/tests/ui/string_add_assign.fixed
src/tools/clippy/tests/ui/string_add_assign.rs
src/tools/clippy/tests/ui/string_add_assign.stderr
src/tools/clippy/tests/ui/string_extend.fixed
src/tools/clippy/tests/ui/string_extend.rs
src/tools/clippy/tests/ui/string_extend.stderr
src/tools/clippy/tests/ui/string_lit_as_bytes.fixed
src/tools/clippy/tests/ui/string_lit_as_bytes.rs
src/tools/clippy/tests/ui/string_lit_as_bytes.stderr
src/tools/clippy/tests/ui/struct_excessive_bools.rs
src/tools/clippy/tests/ui/struct_excessive_bools.stderr
src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs
src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr
src/tools/clippy/tests/ui/suspicious_map.rs
src/tools/clippy/tests/ui/suspicious_map.stderr
src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs
src/tools/clippy/tests/ui/suspicious_unary_op_formatting.stderr
src/tools/clippy/tests/ui/swap.fixed
src/tools/clippy/tests/ui/swap.rs
src/tools/clippy/tests/ui/swap.stderr
src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed
src/tools/clippy/tests/ui/tabs_in_doc_comments.rs
src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr
src/tools/clippy/tests/ui/temporary_assignment.rs
src/tools/clippy/tests/ui/temporary_assignment.stderr
src/tools/clippy/tests/ui/to_digit_is_some.fixed
src/tools/clippy/tests/ui/to_digit_is_some.rs
src/tools/clippy/tests/ui/to_digit_is_some.stderr
src/tools/clippy/tests/ui/toplevel_ref_arg.fixed
src/tools/clippy/tests/ui/toplevel_ref_arg.rs
src/tools/clippy/tests/ui/toplevel_ref_arg.stderr
src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs
src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr
src/tools/clippy/tests/ui/trailing_zeros.rs
src/tools/clippy/tests/ui/trailing_zeros.stderr
src/tools/clippy/tests/ui/transmute.rs
src/tools/clippy/tests/ui/transmute.stderr
src/tools/clippy/tests/ui/transmute_32bit.rs
src/tools/clippy/tests/ui/transmute_32bit.stderr
src/tools/clippy/tests/ui/transmute_64bit.rs
src/tools/clippy/tests/ui/transmute_64bit.stderr
src/tools/clippy/tests/ui/transmute_collection.rs
src/tools/clippy/tests/ui/transmute_collection.stderr
src/tools/clippy/tests/ui/transmute_float_to_int.rs
src/tools/clippy/tests/ui/transmute_float_to_int.stderr
src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs
src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr
src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs
src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr
src/tools/clippy/tests/ui/transmuting_null.rs
src/tools/clippy/tests/ui/transmuting_null.stderr
src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs
src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr
src/tools/clippy/tests/ui/try_err.fixed
src/tools/clippy/tests/ui/try_err.rs
src/tools/clippy/tests/ui/try_err.stderr
src/tools/clippy/tests/ui/ty_fn_sig.rs
src/tools/clippy/tests/ui/type_repetition_in_bounds.rs
src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr
src/tools/clippy/tests/ui/types.fixed
src/tools/clippy/tests/ui/types.rs
src/tools/clippy/tests/ui/types.stderr
src/tools/clippy/tests/ui/unicode.rs
src/tools/clippy/tests/ui/unicode.stderr
src/tools/clippy/tests/ui/uninit.rs
src/tools/clippy/tests/ui/uninit.stderr
src/tools/clippy/tests/ui/unit_arg.fixed
src/tools/clippy/tests/ui/unit_arg.rs
src/tools/clippy/tests/ui/unit_arg.stderr
src/tools/clippy/tests/ui/unit_cmp.rs
src/tools/clippy/tests/ui/unit_cmp.stderr
src/tools/clippy/tests/ui/unknown_attribute.rs
src/tools/clippy/tests/ui/unknown_attribute.stderr
src/tools/clippy/tests/ui/unknown_clippy_lints.fixed
src/tools/clippy/tests/ui/unknown_clippy_lints.rs
src/tools/clippy/tests/ui/unknown_clippy_lints.stderr
src/tools/clippy/tests/ui/unnecessary_cast.rs
src/tools/clippy/tests/ui/unnecessary_cast.stderr
src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed
src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs
src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr
src/tools/clippy/tests/ui/unnecessary_clone.rs
src/tools/clippy/tests/ui/unnecessary_clone.stderr
src/tools/clippy/tests/ui/unnecessary_filter_map.rs
src/tools/clippy/tests/ui/unnecessary_filter_map.stderr
src/tools/clippy/tests/ui/unnecessary_flat_map.fixed
src/tools/clippy/tests/ui/unnecessary_flat_map.rs
src/tools/clippy/tests/ui/unnecessary_flat_map.stderr
src/tools/clippy/tests/ui/unnecessary_fold.fixed
src/tools/clippy/tests/ui/unnecessary_fold.rs
src/tools/clippy/tests/ui/unnecessary_fold.stderr
src/tools/clippy/tests/ui/unnecessary_operation.fixed
src/tools/clippy/tests/ui/unnecessary_operation.rs
src/tools/clippy/tests/ui/unnecessary_operation.stderr
src/tools/clippy/tests/ui/unnecessary_ref.fixed
src/tools/clippy/tests/ui/unnecessary_ref.rs
src/tools/clippy/tests/ui/unnecessary_ref.stderr
src/tools/clippy/tests/ui/unneeded_field_pattern.rs
src/tools/clippy/tests/ui/unneeded_field_pattern.stderr
src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed
src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs
src/tools/clippy/tests/ui/unneeded_wildcard_pattern.stderr
src/tools/clippy/tests/ui/unreadable_literal.fixed
src/tools/clippy/tests/ui/unreadable_literal.rs
src/tools/clippy/tests/ui/unreadable_literal.stderr
src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs
src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr
src/tools/clippy/tests/ui/unsafe_removed_from_name.rs
src/tools/clippy/tests/ui/unsafe_removed_from_name.stderr
src/tools/clippy/tests/ui/unseparated_prefix_literals.fixed
src/tools/clippy/tests/ui/unseparated_prefix_literals.rs
src/tools/clippy/tests/ui/unseparated_prefix_literals.stderr
src/tools/clippy/tests/ui/unused_io_amount.rs
src/tools/clippy/tests/ui/unused_io_amount.stderr
src/tools/clippy/tests/ui/unused_self.rs
src/tools/clippy/tests/ui/unused_self.stderr
src/tools/clippy/tests/ui/unused_unit.fixed
src/tools/clippy/tests/ui/unused_unit.rs
src/tools/clippy/tests/ui/unused_unit.stderr
src/tools/clippy/tests/ui/unwrap.rs
src/tools/clippy/tests/ui/unwrap.stderr
src/tools/clippy/tests/ui/unwrap_or.rs
src/tools/clippy/tests/ui/unwrap_or.stderr
src/tools/clippy/tests/ui/update-all-references.sh
src/tools/clippy/tests/ui/update-references.sh
src/tools/clippy/tests/ui/use_self.fixed
src/tools/clippy/tests/ui/use_self.rs
src/tools/clippy/tests/ui/use_self.stderr
src/tools/clippy/tests/ui/use_self_trait.fixed
src/tools/clippy/tests/ui/use_self_trait.rs
src/tools/clippy/tests/ui/use_self_trait.stderr
src/tools/clippy/tests/ui/used_underscore_binding.rs
src/tools/clippy/tests/ui/used_underscore_binding.stderr
src/tools/clippy/tests/ui/useful_asref.rs
src/tools/clippy/tests/ui/useless_asref.fixed
src/tools/clippy/tests/ui/useless_asref.rs
src/tools/clippy/tests/ui/useless_asref.stderr
src/tools/clippy/tests/ui/useless_attribute.fixed
src/tools/clippy/tests/ui/useless_attribute.rs
src/tools/clippy/tests/ui/useless_attribute.stderr
src/tools/clippy/tests/ui/vec.fixed
src/tools/clippy/tests/ui/vec.rs
src/tools/clippy/tests/ui/vec.stderr
src/tools/clippy/tests/ui/vec_box_sized.fixed
src/tools/clippy/tests/ui/vec_box_sized.rs
src/tools/clippy/tests/ui/vec_box_sized.stderr
src/tools/clippy/tests/ui/verbose_file_reads.rs
src/tools/clippy/tests/ui/verbose_file_reads.stderr
src/tools/clippy/tests/ui/vtable_address_comparisons.rs
src/tools/clippy/tests/ui/vtable_address_comparisons.stderr
src/tools/clippy/tests/ui/while_let_loop.rs
src/tools/clippy/tests/ui/while_let_loop.stderr
src/tools/clippy/tests/ui/while_let_on_iterator.fixed
src/tools/clippy/tests/ui/while_let_on_iterator.rs
src/tools/clippy/tests/ui/while_let_on_iterator.stderr
src/tools/clippy/tests/ui/wild_in_or_pats.rs
src/tools/clippy/tests/ui/wild_in_or_pats.stderr
src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed
src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs
src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr
src/tools/clippy/tests/ui/wildcard_imports.fixed
src/tools/clippy/tests/ui/wildcard_imports.rs
src/tools/clippy/tests/ui/wildcard_imports.stderr
src/tools/clippy/tests/ui/write_literal.rs
src/tools/clippy/tests/ui/write_literal.stderr
src/tools/clippy/tests/ui/write_with_newline.rs
src/tools/clippy/tests/ui/write_with_newline.stderr
src/tools/clippy/tests/ui/writeln_empty_string.fixed
src/tools/clippy/tests/ui/writeln_empty_string.rs
src/tools/clippy/tests/ui/writeln_empty_string.stderr
src/tools/clippy/tests/ui/wrong_self_convention.rs
src/tools/clippy/tests/ui/wrong_self_convention.stderr
src/tools/clippy/tests/ui/zero_div_zero.rs
src/tools/clippy/tests/ui/zero_div_zero.stderr
src/tools/clippy/tests/ui/zero_offset.rs
src/tools/clippy/tests/ui/zero_offset.stderr
src/tools/clippy/tests/ui/zero_ptr.fixed
src/tools/clippy/tests/ui/zero_ptr.rs
src/tools/clippy/tests/ui/zero_ptr.stderr
src/tools/clippy/tests/versioncheck.rs
src/tools/clippy/triagebot.toml
src/tools/clippy/util/cov.sh
src/tools/clippy/util/export.py
src/tools/clippy/util/fetch_prs_between.sh
src/tools/clippy/util/gh-pages/index.html
src/tools/clippy/util/gh-pages/versions.html
src/tools/clippy/util/lintlib.py
src/tools/clippy/util/versions.py

index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2bad3b9c57f0c95f05d5071bd0c45560f55c88a5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++[alias]
++uitest = "test --test compile-test"
++dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
++
++[build]
++rustflags = ["-Zunstable-options"]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a13173544d80a7e8b3fe5c57e85af494ae31ffc9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++# EditorConfig helps developers define and maintain consistent
++# coding styles between different editors and IDEs
++# editorconfig.org
++
++root = true
++
++[*]
++end_of_line = lf
++charset = utf-8
++trim_trailing_whitespace = true
++insert_final_newline = true
++indent_style = space
++indent_size = 4
++
++[*.md]
++trim_trailing_whitespace = false
++
++[*.yml]
++indent_size = 2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..90cf33053c77587326cfec3e29e0f6caa73db1ca
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++* text=auto eol=lf
++*.rs text eol=lf whitespace=tab-in-indent,trailing-space,tabwidth=4
++*.fixed linguist-language=Rust
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..15006a07b44f2b1831ea8a685c47aeb75634729f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++<!--
++Hi there! Whether you've come to make a suggestion for a new lint, an improvement to an existing lint or to report a bug or a false positive in Clippy, you've come to the right place.
++
++For bug reports and false positives, please include the output of `cargo clippy -V` in the report.
++
++Thank you for using Clippy!
++
++Write your comment below this line: -->
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..97aa220afea540e16a4a18ff05b67fc030f136c4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++Thank you for making Clippy better!
++
++We're collecting our changelog from pull request descriptions.
++If your PR only updates to the latest nightly, you can leave the
++`changelog` entry as `none`. Otherwise, please write a short comment
++explaining your change.
++
++If your PR fixes an issue, you can add "fixes #issue_number" into this
++PR description. This way the issue will be automatically closed when
++your PR is merged.
++
++If you added a new lint, here's a checklist for things that will be
++checked during review or continuous integration.
++
++- [ ] Followed [lint naming conventions][lint_naming]
++- [ ] Added passing UI tests (including committed `.stderr` file)
++- [ ] `cargo test` passes locally
++- [ ] Executed `cargo dev update_lints`
++- [ ] Added lint documentation
++- [ ] Run `cargo dev fmt`
++
++[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
++
++Note that you can skip the above if you are just opening a WIP PR in
++order to get feedback.
++
++Delete this line and everything above before opening your PR.
++
++---
++
++changelog: none
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3f425e5b7258d52580b162ba1b6aecac643d8191
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,57 @@@
++#!/bin/bash
++
++set -ex
++
++echo "Removing the current docs for master"
++rm -rf out/master/ || exit 0
++
++echo "Making the docs for master"
++mkdir out/master/
++cp util/gh-pages/index.html out/master
++python3 ./util/export.py out/master/lints.json
++
++if [[ -n $TAG_NAME ]]; then
++  echo "Save the doc for the current tag ($TAG_NAME) and point stable/ to it"
++  cp -r out/master "out/$TAG_NAME"
++  rm -f out/stable
++  ln -s "$TAG_NAME" out/stable
++fi
++
++if [[ $BETA = "true" ]]; then
++  echo "Update documentation for the beta release"
++  cp -r out/master out/beta
++fi
++
++# Generate version index that is shown as root index page
++cp util/gh-pages/versions.html out/index.html
++
++echo "Making the versions.json file"
++python3 ./util/versions.py out
++
++cd out
++# Now let's go have some fun with the cloned repo
++git config user.name "GHA CI"
++git config user.email "gha@ci.invalid"
++
++if git diff --exit-code --quiet; then
++  echo "No changes to the output on this push; exiting."
++  exit 0
++fi
++
++if [[ -n $TAG_NAME ]]; then
++  # Add the new dir
++  git add "$TAG_NAME"
++  # Update the symlink
++  git add stable
++  # Update versions file
++  git add versions.json
++  git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}"
++elif [[ $BETA = "true" ]]; then
++  git add beta
++  git commit -m "Automatic deploy to GitHub Pages (beta): ${SHA}"
++else
++  git add .
++  git commit -m "Automatic deploy to GitHub Pages: ${SHA}"
++fi
++
++git push "$SSH_REPO" "$TARGET_BRANCH"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a2e87f5eb3745837c4a71cc2dad41a3473677bd3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++#!/bin/bash
++
++set -ex
++
++# Check sysroot handling
++sysroot=$(./target/debug/clippy-driver --print sysroot)
++test "$sysroot" = "$(rustc --print sysroot)"
++
++if [[ ${OS} == "Windows" ]]; then
++  desired_sysroot=C:/tmp
++else
++  desired_sysroot=/tmp
++fi
++sysroot=$(./target/debug/clippy-driver --sysroot $desired_sysroot --print sysroot)
++test "$sysroot" = $desired_sysroot
++
++sysroot=$(SYSROOT=$desired_sysroot ./target/debug/clippy-driver --print sysroot)
++test "$sysroot" = $desired_sysroot
++
++# Make sure this isn't set - clippy-driver should cope without it
++unset CARGO_MANIFEST_DIR
++
++# Run a lint and make sure it produces the expected output. It's also expected to exit with code 1
++# FIXME: How to match the clippy invocation in compile-test.rs?
++./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/cstring.rs 2> cstring.stderr && exit 1
++sed -e "s,tests/ui,\$DIR," -e "/= help/d" cstring.stderr > normalized.stderr
++diff normalized.stderr tests/ui/cstring.stderr
++
++# TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8edf0c23860aa0915c860b9d0a3a255bf0f864eb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,99 @@@
++name: Clippy Test
++
++on:
++  push:
++    # Ignore bors branches, since they are covered by `clippy_bors.yml`
++    branches-ignore:
++      - auto
++      - try
++    # Don't run Clippy tests, when only textfiles were modified
++    paths-ignore:
++    - 'COPYRIGHT'
++    - 'LICENSE-*'
++    - '**.md'
++    - '**.txt'
++  pull_request:
++    # Don't run Clippy tests, when only textfiles were modified
++    paths-ignore:
++    - 'COPYRIGHT'
++    - 'LICENSE-*'
++    - '**.md'
++    - '**.txt'
++
++env:
++  RUST_BACKTRACE: 1
++  CARGO_TARGET_DIR: '${{ github.workspace }}/target'
++  NO_FMT_TEST: 1
++
++jobs:
++  base:
++    runs-on: ubuntu-latest
++
++    steps:
++    # Setup
++    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
++      with:
++        github_token: "${{ secrets.github_token }}"
++
++    - name: rust-toolchain
++      uses: actions-rs/toolchain@v1.0.3
++      with:
++        toolchain: nightly
++        target: x86_64-unknown-linux-gnu
++        profile: minimal
++
++    - name: Checkout
++      uses: actions/checkout@v2.0.0
++
++    - name: Run cargo update
++      run: cargo update
++
++    - name: Cache cargo dir
++      uses: actions/cache@v1
++      with:
++        path: ~/.cargo
++        key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
++        restore-keys: |
++          ${{ runner.os }}-x86_64-unknown-linux-gnu
++
++    - name: Master Toolchain Setup
++      run: bash setup-toolchain.sh
++
++    # Run
++    - name: Set LD_LIBRARY_PATH (Linux)
++      run: |
++        SYSROOT=$(rustc --print sysroot)
++        echo "::set-env name=LD_LIBRARY_PATH::${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}"
++
++    - name: Build
++      run: cargo build --features deny-warnings
++
++    - name: Test
++      run: cargo test --features deny-warnings
++
++    - name: Test clippy_lints
++      run: cargo test --features deny-warnings
++      working-directory: clippy_lints
++
++    - name: Test rustc_tools_util
++      run: cargo test --features deny-warnings
++      working-directory: rustc_tools_util
++
++    - name: Test clippy_dev
++      run: cargo test --features deny-warnings
++      working-directory: clippy_dev
++
++    - name: Test cargo-clippy
++      run: ../target/debug/cargo-clippy
++      working-directory: clippy_workspace_tests
++
++    - name: Test clippy-driver
++      run: bash .github/driver.sh
++      env:
++        OS: ${{ runner.os }}
++
++    # Cleanup
++    - name: Run cargo-cache --autoclean
++      run: |
++        cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
++        cargo cache
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6675a1029bbc8ad2cf7b86adbc6fc65c672530c2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,329 @@@
++name: Clippy Test (bors)
++
++on:
++  push:
++    branches:
++      - auto
++      - try
++
++env:
++  RUST_BACKTRACE: 1
++  CARGO_TARGET_DIR: '${{ github.workspace }}/target'
++  NO_FMT_TEST: 1
++
++jobs:
++  changelog:
++    runs-on: ubuntu-latest
++
++    steps:
++    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
++      with:
++        github_token: "${{ secrets.github_token }}"
++    - name: Checkout
++      uses: actions/checkout@v2.0.0
++      with:
++        ref: ${{ github.ref }}
++
++    # Run
++    - name: Check Changelog
++      run: |
++        MESSAGE=$(git log --format=%B -n 1)
++        PR=$(echo "$MESSAGE" | grep -o "#[0-9]*" | head -1 | sed -e 's/^#//')
++        output=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \
++          python -c "import sys, json; print(json.load(sys.stdin)['body'])" | \
++          grep "^changelog: " | \
++          sed "s/changelog: //g")
++        if [[ -z "$output" ]]; then
++          echo "ERROR: PR body must contain 'changelog: ...'"
++          exit 1
++        elif [[ "$output" = "none" ]]; then
++          echo "WARNING: changelog is 'none'"
++        fi
++      env:
++        PYTHONIOENCODING: 'utf-8'
++  base:
++    needs: changelog
++    strategy:
++      matrix:
++        os: [ubuntu-latest, windows-latest, macos-latest]
++        host: [x86_64-unknown-linux-gnu, i686-unknown-linux-gnu, x86_64-apple-darwin, x86_64-pc-windows-msvc]
++        exclude:
++        - os: ubuntu-latest
++          host: x86_64-apple-darwin
++        - os: ubuntu-latest
++          host: x86_64-pc-windows-msvc
++        - os: macos-latest
++          host: x86_64-unknown-linux-gnu
++        - os: macos-latest
++          host: i686-unknown-linux-gnu
++        - os: macos-latest
++          host: x86_64-pc-windows-msvc
++        - os: windows-latest
++          host: x86_64-unknown-linux-gnu
++        - os: windows-latest
++          host: i686-unknown-linux-gnu
++        - os: windows-latest
++          host: x86_64-apple-darwin
++
++    runs-on: ${{ matrix.os }}
++
++    steps:
++    # Setup
++    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
++      with:
++        github_token: "${{ secrets.github_token }}"
++
++    - name: Install dependencies (Linux-i686)
++      run: |
++        sudo dpkg --add-architecture i386
++        sudo apt-get update
++        sudo apt-get install gcc-multilib libssl-dev:i386 libgit2-dev:i386
++      if: matrix.host == 'i686-unknown-linux-gnu'
++
++    - name: rust-toolchain
++      uses: actions-rs/toolchain@v1.0.3
++      with:
++        toolchain: nightly
++        target: ${{ matrix.host }}
++        profile: minimal
++
++    - name: Checkout
++      uses: actions/checkout@v2.0.0
++
++    - name: Run cargo update
++      run: cargo update
++
++    - name: Cache cargo dir
++      uses: actions/cache@v1
++      with:
++        path: ~/.cargo
++        key: ${{ runner.os }}-${{ matrix.host }}-${{ hashFiles('Cargo.lock') }}
++        restore-keys: |
++          ${{ runner.os }}-${{ matrix.host }}
++
++    - name: Master Toolchain Setup
++      run: bash setup-toolchain.sh
++      env:
++        HOST_TOOLCHAIN: ${{ matrix.host }}
++      shell: bash
++
++    # Run
++    - name: Set LD_LIBRARY_PATH (Linux)
++      if: runner.os == 'Linux'
++      run: |
++        SYSROOT=$(rustc --print sysroot)
++        echo "::set-env name=LD_LIBRARY_PATH::${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}"
++    - name: Link rustc dylib (MacOS)
++      if: runner.os == 'macOS'
++      run: |
++        SYSROOT=$(rustc --print sysroot)
++        sudo mkdir -p /usr/local/lib
++        sudo find "${SYSROOT}/lib" -maxdepth 1 -name '*dylib' -exec ln -s {} /usr/local/lib \;
++    - name: Set PATH (Windows)
++      if: runner.os == 'Windows'
++      run: |
++        $sysroot = rustc --print sysroot
++        $env:PATH += ';' + $sysroot + '\bin'
++        echo "::set-env name=PATH::$env:PATH"
++
++    - name: Build
++      run: cargo build --features deny-warnings
++      shell: bash
++
++    - name: Test
++      run: cargo test --features deny-warnings
++      shell: bash
++
++    - name: Test clippy_lints
++      run: cargo test --features deny-warnings
++      shell: bash
++      working-directory: clippy_lints
++
++    - name: Test rustc_tools_util
++      run: cargo test --features deny-warnings
++      shell: bash
++      working-directory: rustc_tools_util
++
++    - name: Test clippy_dev
++      run: cargo test --features deny-warnings
++      shell: bash
++      working-directory: clippy_dev
++
++    - name: Test cargo-clippy
++      run: ../target/debug/cargo-clippy
++      shell: bash
++      working-directory: clippy_workspace_tests
++
++    - name: Test clippy-driver
++      run: bash .github/driver.sh
++      shell: bash
++      env:
++        OS: ${{ runner.os }}
++
++    # Cleanup
++    - name: Run cargo-cache --autoclean
++      run: |
++        cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
++        cargo cache
++      shell: bash
++  integration_build:
++    needs: changelog
++    runs-on: ubuntu-latest
++
++    steps:
++    # Setup
++    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
++      with:
++        github_token: "${{ secrets.github_token }}"
++
++    - name: rust-toolchain
++      uses: actions-rs/toolchain@v1.0.3
++      with:
++        toolchain: nightly
++        target: x86_64-unknown-linux-gnu
++        profile: minimal
++
++    - name: Checkout
++      uses: actions/checkout@v2.0.0
++
++    - name: Run cargo update
++      run: cargo update
++
++    - name: Cache cargo dir
++      uses: actions/cache@v1
++      with:
++        path: ~/.cargo
++        key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
++        restore-keys: |
++          ${{ runner.os }}-x86_64-unknown-linux-gnu
++
++    - name: Master Toolchain Setup
++      run: bash setup-toolchain.sh
++
++    # Run
++    - name: Build Integration Test
++      run: cargo test --test integration --features integration --no-run
++
++    # Upload
++    - name: Extract Binaries
++      run: |
++        DIR=$CARGO_TARGET_DIR/debug
++        rm $DIR/deps/integration-*.d
++        mv $DIR/deps/integration-* $DIR/integration
++        find $DIR ! -executable -o -type d ! -path $DIR | xargs rm -rf
++        rm -rf $CARGO_TARGET_DIR/release
++
++    - name: Upload Binaries
++      uses: actions/upload-artifact@v1
++      with:
++        name: target
++        path: target
++
++    # Cleanup
++    - name: Run cargo-cache --autoclean
++      run: |
++        cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
++        cargo cache
++  integration:
++    needs: integration_build
++    strategy:
++      fail-fast: false
++      max-parallel: 6
++      matrix:
++        integration:
++        - 'rust-lang/cargo'
++        - 'rust-lang/rls'
++        - 'rust-lang/chalk'
++        - 'rust-lang/rustfmt'
++        - 'Marwes/combine'
++        - 'Geal/nom'
++        - 'rust-lang/stdarch'
++        - 'serde-rs/serde'
++        - 'chronotope/chrono'
++        - 'hyperium/hyper'
++        - 'rust-random/rand'
++        - 'rust-lang/futures-rs'
++        - 'rust-itertools/itertools'
++        - 'rust-lang-nursery/failure'
++        - 'rust-lang/log'
++
++    runs-on: ubuntu-latest
++
++    steps:
++    # Setup
++    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
++      with:
++        github_token: "${{ secrets.github_token }}"
++
++    - name: rust-toolchain
++      uses: actions-rs/toolchain@v1.0.3
++      with:
++        toolchain: nightly
++        target: x86_64-unknown-linux-gnu
++        profile: minimal
++
++    - name: Checkout
++      uses: actions/checkout@v2.0.0
++
++    - name: Run cargo update
++      run: cargo update
++
++    - name: Cache cargo dir
++      uses: actions/cache@v1
++      with:
++        path: ~/.cargo
++        key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
++        restore-keys: |
++          ${{ runner.os }}-x86_64-unknown-linux-gnu
++
++    - name: Master Toolchain Setup
++      run: bash setup-toolchain.sh
++
++    # Download
++    - name: Download target dir
++      uses: actions/download-artifact@v1
++      with:
++        name: target
++        path: target
++
++    - name: Make Binaries Executable
++      run: chmod +x $CARGO_TARGET_DIR/debug/*
++
++    # Run
++    - name: Test ${{ matrix.integration }}
++      run: $CARGO_TARGET_DIR/debug/integration
++      env:
++        INTEGRATION: ${{ matrix.integration }}
++        RUSTUP_TOOLCHAIN: master
++
++    # Cleanup
++    - name: Run cargo-cache --autoclean
++      run: |
++        cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
++        cargo cache
++
++  # These jobs doesn't actually test anything, but they're only used to tell
++  # bors the build completed, as there is no practical way to detect when a
++  # workflow is successful listening to webhooks only.
++  #
++  # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
++
++  end-success:
++    name: bors test finished
++    if: github.event.pusher.name == 'bors' && success()
++    runs-on: ubuntu-latest
++    needs: [base, integration]
++
++    steps:
++      - name: Mark the job as successful
++        run: exit 0
++
++  end-failure:
++    name: bors test finished
++    if: github.event.pusher.name == 'bors' && (failure() || cancelled())
++    runs-on: ubuntu-latest
++    needs: [base, integration]
++
++    steps:
++      - name: Mark the job as a failure
++        run: exit 1
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ec3b43c2f43bc950ee5d70a1a102161a9ecd8c7b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,74 @@@
++name: Clippy Dev Test
++
++on:
++  push:
++    branches:
++      - auto
++      - try
++  pull_request:
++    # Only run on paths, that get checked by the clippy_dev tool
++    paths:
++    - 'CHANGELOG.md'
++    - 'README.md'
++    - '**.stderr'
++    - '**.rs'
++
++env:
++  RUST_BACKTRACE: 1
++
++jobs:
++  clippy_dev:
++    runs-on: ubuntu-latest
++
++    steps:
++    # Setup
++    - name: rust-toolchain
++      uses: actions-rs/toolchain@v1.0.3
++      with:
++        toolchain: nightly
++        target: x86_64-unknown-linux-gnu
++        profile: minimal
++        components: rustfmt
++
++    - name: Checkout
++      uses: actions/checkout@v2.0.0
++
++    # Run
++    - name: Build
++      run: cargo build --features deny-warnings
++      working-directory: clippy_dev
++
++    - name: Test limit_stderr_length
++      run: cargo dev limit_stderr_length
++
++    - name: Test update_lints
++      run: cargo dev update_lints --check
++
++    - name: Test fmt
++      run: cargo dev fmt --check
++
++  # These jobs doesn't actually test anything, but they're only used to tell
++  # bors the build completed, as there is no practical way to detect when a
++  # workflow is successful listening to webhooks only.
++  #
++  # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
++
++  end-success:
++    name: bors dev test finished
++    if: github.event.pusher.name == 'bors' && success()
++    runs-on: ubuntu-latest
++    needs: [clippy_dev]
++
++    steps:
++      - name: Mark the job as successful
++        run: exit 0
++
++  end-failure:
++    name: bors dev test finished
++    if: github.event.pusher.name == 'bors' && (failure() || cancelled())
++    runs-on: ubuntu-latest
++    needs: [clippy_dev]
++
++    steps:
++      - name: Mark the job as a failure
++        run: exit 1
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f542f9b02c17b3d16903329df6d9d2fb2348462a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++name: Deploy
++
++on:
++  push:
++    branches:
++      - master
++      - beta
++    tags:
++      - rust-1.**
++
++env:
++  TARGET_BRANCH: 'gh-pages'
++  SHA: '${{ github.sha }}'
++  SSH_REPO: 'git@github.com:${{ github.repository }}.git'
++
++jobs:
++  deploy:
++    runs-on: ubuntu-latest
++    if: github.repository == 'rust-lang/rust-clippy'
++
++    steps:
++    # Setup
++    - name: Checkout
++      uses: actions/checkout@v2.0.0
++
++    - name: Checkout
++      uses: actions/checkout@v2.0.0
++      with:
++        ref: ${{ env.TARGET_BRANCH }}
++        path: 'out'
++
++    # Run
++    - name: Set tag name
++      if: startswith(github.ref, 'refs/tags/')
++      run: |
++        TAG=$(basename ${{ github.ref }})
++        echo "::set-env name=TAG_NAME::$TAG"
++    - name: Set beta to true
++      if: github.ref == 'refs/heads/beta'
++      run: echo "::set-env name=BETA::true"
++
++    - name: Use scripts and templates from master branch
++      run: |
++        git fetch --no-tags --prune --depth=1 origin master
++        git checkout origin/master -- .github/deploy.sh util/gh-pages/ util/*.py
++
++    - name: Deploy
++      run: |
++        eval "$(ssh-agent -s)"
++        ssh-add - <<< "${{ secrets.DEPLOY_KEY }}"
++        bash .github/deploy.sh
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cc175e8bf247f89b721b98fc5bd6e18f1dbd11c4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++name: Remark
++
++on:
++  push:
++    branches:
++      - auto
++      - try
++  pull_request:
++    paths:
++    - '**.md'
++
++jobs:
++  remark:
++    runs-on: ubuntu-latest
++
++    steps:
++    # Setup
++    - name: Checkout
++      uses: actions/checkout@v2.0.0
++
++    - name: Setup Node.js
++      uses: actions/setup-node@v1.1.0
++
++    - name: Install remark
++      run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended
++
++    # Run
++    - name: Check *.md files
++      run: git ls-files -z '*.md' | xargs -0 -n 1 -I {} ./node_modules/.bin/remark {} -u lint -f > /dev/null
++
++  # These jobs doesn't actually test anything, but they're only used to tell
++  # bors the build completed, as there is no practical way to detect when a
++  # workflow is successful listening to webhooks only.
++  #
++  # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
++
++  end-success:
++    name: bors remark test finished
++    if: github.event.pusher.name == 'bors' && success()
++    runs-on: ubuntu-latest
++    needs: [remark]
++
++    steps:
++      - name: Mark the job as successful
++        run: exit 0
++
++  end-failure:
++    name: bors remark test finished
++    if: github.event.pusher.name == 'bors' && (failure() || cancelled())
++    runs-on: ubuntu-latest
++    needs: [remark]
++
++    steps:
++      - name: Mark the job as a failure
++        run: exit 1
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..adf5e8feddf4c507efc627259e53cb146bdb45ea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++# Used by CI to be able to push:
++/.github/deploy_key
++out
++
++# Compiled files
++*.o
++*.d
++*.so
++*.rlib
++*.dll
++*.pyc
++*.rmeta
++
++# Executables
++*.exe
++
++# Generated by Cargo
++*Cargo.lock
++/target
++/clippy_lints/target
++/clippy_workspace_tests/target
++/clippy_dev/target
++/rustc_tools_util/target
++
++# Generated by dogfood
++/target_recur/
++
++# gh pages docs
++util/gh-pages/lints.json
++
++# rustfmt backups
++*.rs.bk
++
++helper.txt
++*.iml
++.vscode
++.idea
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0ede7ac75cb6f98d73d8a4efa0cc2cad09b55fa3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++{
++  "plugins": [
++    "remark-preset-lint-recommended",
++    ["remark-lint-list-item-indent", false],
++    ["remark-lint-no-literal-urls", false],
++    ["remark-lint-no-shortcut-reference-link", false],
++    ["remark-lint-maximum-line-length", 120]
++  ],
++  "settings": {
++    "commonmark": true
++  }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..575773580c0be25ee59792a3e2d2e9a3aa0ebc6b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1657 @@@
++# Change Log
++
++All notable changes to this project will be documented in this file.
++See [Changelog Update](doc/changelog_update.md) if you want to update this
++document.
++
++## Unreleased / In Rust Nightly
++
++[891e1a8...master](https://github.com/rust-lang/rust-clippy/compare/891e1a8...master)
++
++## Rust 1.44
++
++Current beta, release 2020-06-04
++
++[204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8)
++
++### New lints
++
++* [`explicit_deref_methods`] [#5226](https://github.com/rust-lang/rust-clippy/pull/5226)
++* [`implicit_saturating_sub`] [#5427](https://github.com/rust-lang/rust-clippy/pull/5427)
++* [`macro_use_imports`] [#5230](https://github.com/rust-lang/rust-clippy/pull/5230)
++* [`verbose_file_reads`] [#5272](https://github.com/rust-lang/rust-clippy/pull/5272)
++* [`future_not_send`] [#5423](https://github.com/rust-lang/rust-clippy/pull/5423)
++* [`redundant_pub_crate`] [#5319](https://github.com/rust-lang/rust-clippy/pull/5319)
++* [`large_const_arrays`] [#5248](https://github.com/rust-lang/rust-clippy/pull/5248)
++* [`result_map_or_into_option`] [#5415](https://github.com/rust-lang/rust-clippy/pull/5415)
++* [`redundant_allocation`] [#5349](https://github.com/rust-lang/rust-clippy/pull/5349)
++* [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
++* [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
++
++
++### Moves and Deprecations
++
++* Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380)
++* Move [`cognitive_complexity`] to nursery [#5428](https://github.com/rust-lang/rust-clippy/pull/5428)
++* Move [`useless_transmute`] to nursery [#5364](https://github.com/rust-lang/rust-clippy/pull/5364)
++* Downgrade [`inefficient_to_string`] to pedantic [#5412](https://github.com/rust-lang/rust-clippy/pull/5412)
++* Downgrade [`option_option`] to pedantic [#5401](https://github.com/rust-lang/rust-clippy/pull/5401)
++* Downgrade [`unreadable_literal`] to pedantic [#5419](https://github.com/rust-lang/rust-clippy/pull/5419)
++* Downgrade [`let_unit_value`] to pedantic [#5409](https://github.com/rust-lang/rust-clippy/pull/5409)
++* Downgrade [`trivially_copy_pass_by_ref`] to pedantic [#5410](https://github.com/rust-lang/rust-clippy/pull/5410)
++* Downgrade [`implicit_hasher`] to pedantic [#5411](https://github.com/rust-lang/rust-clippy/pull/5411)
++
++### Enhancements
++
++* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to
++  auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363)
++* Make [`redundant_clone`] also trigger on cases where the cloned value is not
++  consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304)
++* Expand [`integer_arithmetic`] to also disallow bit-shifting [#5430](https://github.com/rust-lang/rust-clippy/pull/5430)
++* [`option_as_ref_deref`] now detects more deref cases [#5425](https://github.com/rust-lang/rust-clippy/pull/5425)
++* [`large_enum_variant`] now report the sizes of the largest and second-largest variants [#5466](https://github.com/rust-lang/rust-clippy/pull/5466)
++* [`bool_comparison`] now also checks for inequality comparisons that can be
++  written more concisely [#5365](https://github.com/rust-lang/rust-clippy/pull/5365)
++* Expand [`clone_on_copy`] to work in method call arguments as well [#5441](https://github.com/rust-lang/rust-clippy/pull/5441)
++* [`redundant_pattern_matching`] now also handles `while let` [#5483](https://github.com/rust-lang/rust-clippy/pull/5483)
++* [`integer_arithmetic`] now also lints references of integers [#5329](https://github.com/rust-lang/rust-clippy/pull/5329)
++* Expand [`float_cmp_const`] to also work on arrays [#5345](https://github.com/rust-lang/rust-clippy/pull/5345)
++* Trigger [`map_flatten`] when map is called on an `Option` [#5473](https://github.com/rust-lang/rust-clippy/pull/5473)
++
++### False Positive Fixes
++
++* [`many_single_char_names`] [#5468](https://github.com/rust-lang/rust-clippy/pull/5468)
++* [`should_implement_trait`] [#5437](https://github.com/rust-lang/rust-clippy/pull/5437)
++* [`unused_self`] [#5387](https://github.com/rust-lang/rust-clippy/pull/5387)
++* [`redundant_clone`] [#5453](https://github.com/rust-lang/rust-clippy/pull/5453)
++* [`precedence`] [#5445](https://github.com/rust-lang/rust-clippy/pull/5445)
++* [`suspicious_op_assign_impl`] [#5424](https://github.com/rust-lang/rust-clippy/pull/5424)
++* [`needless_lifetimes`] [#5293](https://github.com/rust-lang/rust-clippy/pull/5293)
++* [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287)
++* [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451)
++
++
++### Suggestion Improvements
++
++* Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481)
++* Improve the suggested placeholder in [`option_map_unit_fn`] [#5292](https://github.com/rust-lang/rust-clippy/pull/5292)
++* Improve suggestion for [`match_single_binding`] when triggered inside a closure [#5350](https://github.com/rust-lang/rust-clippy/pull/5350)
++
++### ICE Fixes
++
++* Handle the unstable `trivial_bounds` feature [#5296](https://github.com/rust-lang/rust-clippy/pull/5296)
++* `shadow_*` lints [#5297](https://github.com/rust-lang/rust-clippy/pull/5297)
++
++### Documentation
++
++* Fix documentation generation for configurable lints [#5353](https://github.com/rust-lang/rust-clippy/pull/5353)
++* Update documentation for [`new_ret_no_self`] [#5448](https://github.com/rust-lang/rust-clippy/pull/5448)
++* The documentation for [`option_option`] now suggest using a tri-state enum [#5403](https://github.com/rust-lang/rust-clippy/pull/5403)
++* Fix bit mask example in [`verbose_bit_mask`] documentation [#5454](https://github.com/rust-lang/rust-clippy/pull/5454)
++* [`wildcard_imports`] documentation now mentions that `use ...::prelude::*` is
++  not linted [#5312](https://github.com/rust-lang/rust-clippy/pull/5312)
++
++## Rust 1.43
++
++Current stable, released 2020-04-23
++
++[4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b)
++
++### New lints
++
++* [`imprecise_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
++* [`suboptimal_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
++* [`wildcard_imports`] [#5029](https://github.com/rust-lang/rust-clippy/pull/5029)
++* [`single_component_path_imports`] [#5058](https://github.com/rust-lang/rust-clippy/pull/5058)
++* [`match_single_binding`] [#5061](https://github.com/rust-lang/rust-clippy/pull/5061)
++* [`let_underscore_lock`] [#5101](https://github.com/rust-lang/rust-clippy/pull/5101)
++* [`struct_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
++* [`fn_params_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
++* [`option_env_unwrap`] [#5148](https://github.com/rust-lang/rust-clippy/pull/5148)
++* [`lossy_float_literal`] [#5202](https://github.com/rust-lang/rust-clippy/pull/5202)
++* [`rest_pat_in_fully_bound_structs`] [#5258](https://github.com/rust-lang/rust-clippy/pull/5258)
++
++### Moves and Deprecations
++
++* Move [`unneeded_field_pattern`] to pedantic group [#5200](https://github.com/rust-lang/rust-clippy/pull/5200)
++
++### Enhancements
++
++* Make [`missing_errors_doc`] lint also trigger on `async` functions
++  [#5181](https://github.com/rust-lang/rust-clippy/pull/5181)
++* Add more constants to [`approx_constant`] [#5193](https://github.com/rust-lang/rust-clippy/pull/5193)
++* Extend [`question_mark`] lint [#5266](https://github.com/rust-lang/rust-clippy/pull/5266)
++
++### False Positive Fixes
++
++* [`use_debug`] [#5047](https://github.com/rust-lang/rust-clippy/pull/5047)
++* [`unnecessary_unwrap`] [#5132](https://github.com/rust-lang/rust-clippy/pull/5132)
++* [`zero_prefixed_literal`] [#5170](https://github.com/rust-lang/rust-clippy/pull/5170)
++* [`missing_const_for_fn`] [#5216](https://github.com/rust-lang/rust-clippy/pull/5216)
++
++### Suggestion Improvements
++
++* Improve suggestion when blocks of code are suggested [#5134](https://github.com/rust-lang/rust-clippy/pull/5134)
++
++### ICE Fixes
++
++* `misc_early` lints [#5129](https://github.com/rust-lang/rust-clippy/pull/5129)
++* [`missing_errors_doc`] [#5213](https://github.com/rust-lang/rust-clippy/pull/5213)
++* Fix ICE when evaluating `usize`s [#5256](https://github.com/rust-lang/rust-clippy/pull/5256)
++
++### Documentation
++
++* Improve documentation of [`iter_nth_zero`]
++* Add documentation pages for stable releases [#5171](https://github.com/rust-lang/rust-clippy/pull/5171)
++
++### Others
++
++* Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190)
++
++
++## Rust 1.42
++
++Released 2020-03-12
++
++[69f99e7...4ee1206](https://github.com/rust-lang/rust-clippy/compare/69f99e7...4ee1206)
++
++### New lints
++
++* [`filetype_is_file`] [#4543](https://github.com/rust-lang/rust-clippy/pull/4543)
++* [`let_underscore_must_use`] [#4823](https://github.com/rust-lang/rust-clippy/pull/4823)
++* [`modulo_arithmetic`] [#4867](https://github.com/rust-lang/rust-clippy/pull/4867)
++* [`mem_replace_with_default`] [#4881](https://github.com/rust-lang/rust-clippy/pull/4881)
++* [`mutable_key_type`] [#4885](https://github.com/rust-lang/rust-clippy/pull/4885)
++* [`option_as_ref_deref`] [#4945](https://github.com/rust-lang/rust-clippy/pull/4945)
++* [`wildcard_in_or_patterns`] [#4960](https://github.com/rust-lang/rust-clippy/pull/4960)
++* [`iter_nth_zero`] [#4966](https://github.com/rust-lang/rust-clippy/pull/4966)
++* [`invalid_atomic_ordering`] [#4999](https://github.com/rust-lang/rust-clippy/pull/4999)
++* [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067)
++
++### Moves and Deprecations
++
++* Move [`transmute_float_to_int`] from nursery to complexity group
++  [#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
++* Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
++* Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
++* Deprecate [`unused_label`] [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
++
++### Enhancements
++
++* Lint vectored IO in [`unused_io_amount`] [#5027](https://github.com/rust-lang/rust-clippy/pull/5027)
++* Make [`vec_box`] configurable by adding a size threshold [#5081](https://github.com/rust-lang/rust-clippy/pull/5081)
++* Also lint constants in [`cmp_nan`] [#4910](https://github.com/rust-lang/rust-clippy/pull/4910)
++* Fix false negative in [`expect_fun_call`] [#4915](https://github.com/rust-lang/rust-clippy/pull/4915)
++* Fix false negative in [`redundant_clone`] [#5017](https://github.com/rust-lang/rust-clippy/pull/5017)
++
++### False Positive Fixes
++
++* [`map_clone`] [#4937](https://github.com/rust-lang/rust-clippy/pull/4937)
++* [`replace_consts`] [#4977](https://github.com/rust-lang/rust-clippy/pull/4977)
++* [`let_and_return`] [#5008](https://github.com/rust-lang/rust-clippy/pull/5008)
++* [`eq_op`] [#5079](https://github.com/rust-lang/rust-clippy/pull/5079)
++* [`possible_missing_comma`] [#5083](https://github.com/rust-lang/rust-clippy/pull/5083)
++* [`debug_assert_with_mut_call`] [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
++* Don't trigger [`let_underscore_must_use`] in external macros
++  [#5082](https://github.com/rust-lang/rust-clippy/pull/5082)
++* Don't trigger [`empty_loop`] in `no_std` crates [#5086](https://github.com/rust-lang/rust-clippy/pull/5086)
++
++### Suggestion Improvements
++
++* [`option_map_unwrap_or`] [#4634](https://github.com/rust-lang/rust-clippy/pull/4634)
++* [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
++* [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
++* [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
++* [`unknown_clippy_lints`] [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
++* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
++* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
++* [`if_let_some_result`] [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
++
++### ICE fixes
++
++* [`unsound_collection_transmute`] [#4975](https://github.com/rust-lang/rust-clippy/pull/4975)
++
++### Documentation
++
++* Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`]
++
++
++## Rust 1.41
++
++Released 2020-01-30
++
++[c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7)
++
++* New Lints:
++  * [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697)
++  * [`to_digit_is_some`] [#4801](https://github.com/rust-lang/rust-clippy/pull/4801)
++  * [`tabs_in_doc_comments`] [#4806](https://github.com/rust-lang/rust-clippy/pull/4806)
++  * [`large_stack_arrays`] [#4807](https://github.com/rust-lang/rust-clippy/pull/4807)
++  * [`same_functions_in_if_condition`] [#4814](https://github.com/rust-lang/rust-clippy/pull/4814)
++  * [`zst_offset`] [#4816](https://github.com/rust-lang/rust-clippy/pull/4816)
++  * [`as_conversions`] [#4821](https://github.com/rust-lang/rust-clippy/pull/4821)
++  * [`missing_errors_doc`] [#4884](https://github.com/rust-lang/rust-clippy/pull/4884)
++  * [`transmute_float_to_int`] [#4889](https://github.com/rust-lang/rust-clippy/pull/4889)
++* Remove plugin interface, see
++  [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
++  details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
++* Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
++* Deprecate [`into_iter_on_array`] [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
++* Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
++  [#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
++* Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
++* Fix false positive in `while_immutable_condition` [#4730](https://github.com/rust-lang/rust-clippy/pull/4730)
++* Fix false positive in `explicit_counter_loop` [#4803](https://github.com/rust-lang/rust-clippy/pull/4803)
++* Fix false positive in `must_use_candidate` [#4794](https://github.com/rust-lang/rust-clippy/pull/4794)
++* Fix false positive in `print_with_newline` and `write_with_newline`
++  [#4769](https://github.com/rust-lang/rust-clippy/pull/4769)
++* Fix false positive in `derive_hash_xor_eq` [#4766](https://github.com/rust-lang/rust-clippy/pull/4766)
++* Fix false positive in `missing_inline_in_public_items` [#4870](https://github.com/rust-lang/rust-clippy/pull/4870)
++* Fix false positive in `string_add` [#4880](https://github.com/rust-lang/rust-clippy/pull/4880)
++* Fix false positive in `float_arithmetic` [#4851](https://github.com/rust-lang/rust-clippy/pull/4851)
++* Fix false positive in `cast_sign_loss` [#4883](https://github.com/rust-lang/rust-clippy/pull/4883)
++* Fix false positive in `manual_swap` [#4877](https://github.com/rust-lang/rust-clippy/pull/4877)
++* Fix ICEs occurring while checking some block expressions [#4772](https://github.com/rust-lang/rust-clippy/pull/4772)
++* Fix ICE in `use_self` [#4776](https://github.com/rust-lang/rust-clippy/pull/4776)
++* Fix ICEs related to `const_generics` [#4780](https://github.com/rust-lang/rust-clippy/pull/4780)
++* Display help when running `clippy-driver` without arguments, instead of ICEing
++  [#4810](https://github.com/rust-lang/rust-clippy/pull/4810)
++* Clippy has its own ICE message now [#4588](https://github.com/rust-lang/rust-clippy/pull/4588)
++* Show deprecated lints in the documentation again [#4757](https://github.com/rust-lang/rust-clippy/pull/4757)
++* Improve Documentation by adding positive examples to some lints
++  [#4832](https://github.com/rust-lang/rust-clippy/pull/4832)
++
++## Rust 1.40
++
++Released 2019-12-19
++
++[4e7e71b...c8e3cfb](https://github.com/rust-lang/rust-clippy/compare/4e7e71b...c8e3cfb)
++
++* New Lints:
++  * [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537)
++  * [`needless_doctest_main`] [#4603](https://github.com/rust-lang/rust-clippy/pull/4603)
++  * [`suspicious_unary_op_formatting`] [#4615](https://github.com/rust-lang/rust-clippy/pull/4615)
++  * [`debug_assert_with_mut_call`] [#4680](https://github.com/rust-lang/rust-clippy/pull/4680)
++  * [`unused_self`] [#4619](https://github.com/rust-lang/rust-clippy/pull/4619)
++  * [`inefficient_to_string`] [#4683](https://github.com/rust-lang/rust-clippy/pull/4683)
++  * [`must_use_unit`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
++  * [`must_use_candidate`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
++  * [`double_must_use`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
++  * [`comparison_chain`] [#4569](https://github.com/rust-lang/rust-clippy/pull/4569)
++  * [`unsound_collection_transmute`] [#4592](https://github.com/rust-lang/rust-clippy/pull/4592)
++  * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
++  * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
++  * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
++  * [`option_expect_used`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
++  * [`result_expect_used`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
++* Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
++* Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736)
++* Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613)
++* Expand `integer_arithmetic` to also detect mutating arithmetic like `+=` [#4585](https://github.com/rust-lang/rust-clippy/pull/4585)
++* Fix false positive in `nonminimal_bool` [#4568](https://github.com/rust-lang/rust-clippy/pull/4568)
++* Fix false positive in `missing_safety_doc` [#4611](https://github.com/rust-lang/rust-clippy/pull/4611)
++* Fix false positive in `cast_sign_loss` [#4614](https://github.com/rust-lang/rust-clippy/pull/4614)
++* Fix false positive in `redundant_clone` [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
++* Fix false positive in `try_err` [#4721](https://github.com/rust-lang/rust-clippy/pull/4721)
++* Fix false positive in `toplevel_ref_arg` [#4570](https://github.com/rust-lang/rust-clippy/pull/4570)
++* Fix false positive in `multiple_inherent_impl` [#4593](https://github.com/rust-lang/rust-clippy/pull/4593)
++* Improve more suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4575](https://github.com/rust-lang/rust-clippy/pull/4575)
++* Improve suggestion for `zero_ptr` [#4599](https://github.com/rust-lang/rust-clippy/pull/4599)
++* Improve suggestion for `explicit_counter_loop` [#4691](https://github.com/rust-lang/rust-clippy/pull/4691)
++* Improve suggestion for `mul_add` [#4602](https://github.com/rust-lang/rust-clippy/pull/4602)
++* Improve suggestion for `assertions_on_constants` [#4635](https://github.com/rust-lang/rust-clippy/pull/4635)
++* Fix ICE in `use_self` [#4671](https://github.com/rust-lang/rust-clippy/pull/4671)
++* Fix ICE when encountering const casts [#4590](https://github.com/rust-lang/rust-clippy/pull/4590)
++
++## Rust 1.39
++
++Released 2019-11-07
++
++[3aea860...4e7e71b](https://github.com/rust-lang/rust-clippy/compare/3aea860...4e7e71b)
++
++* New Lints:
++  * [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479)
++  * [`flat_map_identity`] [#4231](https://github.com/rust-lang/rust-clippy/pull/4231)
++  * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535)
++  * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511)
++  * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394)
++  * [`option_and_then_some`] [#4386](https://github.com/rust-lang/rust-clippy/pull/4386)
++  * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498)
++* Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348)
++* Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403)
++* Move `cast_lossless` to pedantic group [#4539](https://github.com/rust-lang/rust-clippy/pull/4539)
++* `temporary_cstring_as_ptr` now catches more cases [#4425](https://github.com/rust-lang/rust-clippy/pull/4425)
++* `use_self` now works in constructors, too [#4525](https://github.com/rust-lang/rust-clippy/pull/4525)
++* `cargo_common_metadata` now checks for license files [#4518](https://github.com/rust-lang/rust-clippy/pull/4518)
++* `cognitive_complexity` now includes the measured complexity in the warning message [#4469](https://github.com/rust-lang/rust-clippy/pull/4469)
++* Fix false positives in `block_in_if_*` lints [#4458](https://github.com/rust-lang/rust-clippy/pull/4458)
++* Fix false positive in `cast_lossless` [#4473](https://github.com/rust-lang/rust-clippy/pull/4473)
++* Fix false positive in `clone_on_copy` [#4411](https://github.com/rust-lang/rust-clippy/pull/4411)
++* Fix false positive in `deref_addrof` [#4487](https://github.com/rust-lang/rust-clippy/pull/4487)
++* Fix false positive in `too_many_lines` [#4490](https://github.com/rust-lang/rust-clippy/pull/4490)
++* Fix false positive in `new_ret_no_self` [#4365](https://github.com/rust-lang/rust-clippy/pull/4365)
++* Fix false positive in `manual_swap` [#4478](https://github.com/rust-lang/rust-clippy/pull/4478)
++* Fix false positive in `missing_const_for_fn` [#4450](https://github.com/rust-lang/rust-clippy/pull/4450)
++* Fix false positive in `extra_unused_lifetimes` [#4477](https://github.com/rust-lang/rust-clippy/pull/4477)
++* Fix false positive in `inherent_to_string` [#4460](https://github.com/rust-lang/rust-clippy/pull/4460)
++* Fix false positive in `map_entry` [#4495](https://github.com/rust-lang/rust-clippy/pull/4495)
++* Fix false positive in `unused_unit` [#4445](https://github.com/rust-lang/rust-clippy/pull/4445)
++* Fix false positive in `redundant_pattern` [#4489](https://github.com/rust-lang/rust-clippy/pull/4489)
++* Fix false positive in `wrong_self_convention` [#4369](https://github.com/rust-lang/rust-clippy/pull/4369)
++* Improve various suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4558](https://github.com/rust-lang/rust-clippy/pull/4558)
++* Improve suggestions for `redundant_pattern_matching` [#4352](https://github.com/rust-lang/rust-clippy/pull/4352)
++* Improve suggestions for `explicit_write` [#4544](https://github.com/rust-lang/rust-clippy/pull/4544)
++* Improve suggestion for `or_fun_call` [#4522](https://github.com/rust-lang/rust-clippy/pull/4522)
++* Improve suggestion for `match_as_ref` [#4446](https://github.com/rust-lang/rust-clippy/pull/4446)
++* Improve suggestion for `unnecessary_fold_span` [#4382](https://github.com/rust-lang/rust-clippy/pull/4382)
++* Add suggestions for `unseparated_literal_suffix` [#4401](https://github.com/rust-lang/rust-clippy/pull/4401)
++* Add suggestions for `char_lit_as_u8` [#4418](https://github.com/rust-lang/rust-clippy/pull/4418)
++
++## Rust 1.38
++
++Released 2019-09-26
++
++[e3cb40e...3aea860](https://github.com/rust-lang/rust-clippy/compare/e3cb40e...3aea860)
++
++* New Lints:
++  * [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203)
++  * [`inherent_to_string`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
++  * [`inherent_to_string_shadow_display`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
++  * [`type_repetition_in_bounds`] [#3766](https://github.com/rust-lang/rust-clippy/pull/3766)
++  * [`try_err`] [#4222](https://github.com/rust-lang/rust-clippy/pull/4222)
++* Move `{unnnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
++* Extend the `use_self` lint to suggest uses of `Self::Variant` [#4308](https://github.com/rust-lang/rust-clippy/pull/4308)
++* Improve suggestion for needless return [#4262](https://github.com/rust-lang/rust-clippy/pull/4262)
++* Add auto-fixable suggestion for `let_unit` [#4337](https://github.com/rust-lang/rust-clippy/pull/4337)
++* Fix false positive in `pub_enum_variant_names` and `enum_variant_names` [#4345](https://github.com/rust-lang/rust-clippy/pull/4345)
++* Fix false positive in `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
++* Fix false positive in `string_lit_as_bytes` [#4233](https://github.com/rust-lang/rust-clippy/pull/4233)
++* Fix false positive in `needless_lifetimes` [#4266](https://github.com/rust-lang/rust-clippy/pull/4266)
++* Fix false positive in `float_cmp` [#4275](https://github.com/rust-lang/rust-clippy/pull/4275)
++* Fix false positives in `needless_return` [#4274](https://github.com/rust-lang/rust-clippy/pull/4274)
++* Fix false negative in `match_same_arms` [#4246](https://github.com/rust-lang/rust-clippy/pull/4246)
++* Fix incorrect suggestion for `needless_bool` [#4335](https://github.com/rust-lang/rust-clippy/pull/4335)
++* Improve suggestion for `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
++* Improve suggestion for `single_char_literal` [#4361](https://github.com/rust-lang/rust-clippy/pull/4361)
++* Improve suggestion for `len_zero` [#4314](https://github.com/rust-lang/rust-clippy/pull/4314)
++* Fix ICE in `implicit_hasher` [#4268](https://github.com/rust-lang/rust-clippy/pull/4268)
++* Fix allow bug in `trivially_copy_pass_by_ref` [#4250](https://github.com/rust-lang/rust-clippy/pull/4250)
++
++## Rust 1.37
++
++Released 2019-08-15
++
++[082cfa7...e3cb40e](https://github.com/rust-lang/rust-clippy/compare/082cfa7...e3cb40e)
++
++* New Lints:
++  * [`checked_conversions`] [#4088](https://github.com/rust-lang/rust-clippy/pull/4088)
++  * [`get_last_with_len`] [#3832](https://github.com/rust-lang/rust-clippy/pull/3832)
++  * [`integer_division`] [#4195](https://github.com/rust-lang/rust-clippy/pull/4195)
++* Renamed Lint: `const_static_lifetime` is now called [`redundant_static_lifetimes`].
++  The lint now covers statics in addition to consts [#4162](https://github.com/rust-lang/rust-clippy/pull/4162)
++* [`match_same_arms`] now warns for all identical arms, instead of only the first one [#4102](https://github.com/rust-lang/rust-clippy/pull/4102)
++* [`needless_return`] now works with void functions [#4220](https://github.com/rust-lang/rust-clippy/pull/4220)
++* Fix false positive in [`redundant_closure`] [#4190](https://github.com/rust-lang/rust-clippy/pull/4190)
++* Fix false positive in [`useless_attribute`] [#4107](https://github.com/rust-lang/rust-clippy/pull/4107)
++* Fix incorrect suggestion for [`float_cmp`] [#4214](https://github.com/rust-lang/rust-clippy/pull/4214)
++* Add suggestions for [`print_with_newline`] and [`write_with_newline`] [#4136](https://github.com/rust-lang/rust-clippy/pull/4136)
++* Improve suggestions for [`option_map_unwrap_or_else`] and [`result_map_unwrap_or_else`] [#4164](https://github.com/rust-lang/rust-clippy/pull/4164)
++* Improve suggestions for [`non_ascii_literal`] [#4119](https://github.com/rust-lang/rust-clippy/pull/4119)
++* Improve diagnostics for [`let_and_return`] [#4137](https://github.com/rust-lang/rust-clippy/pull/4137)
++* Improve diagnostics for [`trivially_copy_pass_by_ref`] [#4071](https://github.com/rust-lang/rust-clippy/pull/4071)
++* Add macro check for [`unreadable_literal`] [#4099](https://github.com/rust-lang/rust-clippy/pull/4099)
++
++## Rust 1.36
++
++Released 2019-07-04
++
++[eb9f9b1...082cfa7](https://github.com/rust-lang/rust-clippy/compare/eb9f9b1...082cfa7)
++
++* New lints: [`find_map`], [`filter_map_next`] [#4039](https://github.com/rust-lang/rust-clippy/pull/4039)
++* New lint: [`path_buf_push_overwrite`] [#3954](https://github.com/rust-lang/rust-clippy/pull/3954)
++* Move `path_buf_push_overwrite` to the nursery [#4013](https://github.com/rust-lang/rust-clippy/pull/4013)
++* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
++* Allow allowing of [`toplevel_ref_arg`] lint [#4007](https://github.com/rust-lang/rust-clippy/pull/4007)
++* Fix false negative in [`or_fun_call`] pertaining to nested constructors [#4084](https://github.com/rust-lang/rust-clippy/pull/4084)
++* Fix false positive in [`or_fun_call`] pertaining to enum variant constructors [#4018](https://github.com/rust-lang/rust-clippy/pull/4018)
++* Fix false positive in [`useless_let_if_seq`] pertaining to interior mutability [#4035](https://github.com/rust-lang/rust-clippy/pull/4035)
++* Fix false positive in [`redundant_closure`] pertaining to non-function types [#4008](https://github.com/rust-lang/rust-clippy/pull/4008)
++* Fix false positive in [`let_and_return`] pertaining to attributes on `let`s [#4024](https://github.com/rust-lang/rust-clippy/pull/4024)
++* Fix false positive in [`module_name_repetitions`] lint pertaining to attributes [#4006](https://github.com/rust-lang/rust-clippy/pull/4006)
++* Fix false positive on [`assertions_on_constants`] pertaining to `debug_assert!` [#3989](https://github.com/rust-lang/rust-clippy/pull/3989)
++* Improve suggestion in [`map_clone`] to suggest `.copied()` where applicable  [#3970](https://github.com/rust-lang/rust-clippy/pull/3970) [#4043](https://github.com/rust-lang/rust-clippy/pull/4043)
++* Improve suggestion for [`search_is_some`] [#4049](https://github.com/rust-lang/rust-clippy/pull/4049)
++* Improve suggestion applicability for [`naive_bytecount`] [#3984](https://github.com/rust-lang/rust-clippy/pull/3984)
++* Improve suggestion applicability for [`while_let_loop`] [#3975](https://github.com/rust-lang/rust-clippy/pull/3975)
++* Improve diagnostics for [`too_many_arguments`] [#4053](https://github.com/rust-lang/rust-clippy/pull/4053)
++* Improve diagnostics for [`cast_lossless`] [#4021](https://github.com/rust-lang/rust-clippy/pull/4021)
++* Deal with macro checks in desugarings better [#4082](https://github.com/rust-lang/rust-clippy/pull/4082)
++* Add macro check for [`unnecessary_cast`]  [#4026](https://github.com/rust-lang/rust-clippy/pull/4026)
++* Remove [`approx_constant`]'s documentation's "Known problems" section. [#4027](https://github.com/rust-lang/rust-clippy/pull/4027)
++* Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960)
++* Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931)
++
++
++## Rust 1.35
++
++Released 2019-05-20
++
++[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
++
++* New lint: [`drop_bounds`] to detect `T: Drop` bounds
++* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
++* Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code
++* Move [`get_unwrap`] to the restriction category
++* Improve suggestions for [`iter_cloned_collect`]
++* Improve suggestions for [`cast_lossless`] to suggest suffixed literals
++* Fix false positives in [`print_with_newline`] and [`write_with_newline`] pertaining to raw strings
++* Fix false positive in [`needless_range_loop`] pertaining to structs without a `.iter()`
++* Fix false positive in [`bool_comparison`] pertaining to non-bool types
++* Fix false positive in [`redundant_closure`] pertaining to differences in borrows
++* Fix false positive in [`option_map_unwrap_or`] on non-copy types
++* Fix false positives in [`missing_const_for_fn`] pertaining to macros and trait method impls
++* Fix false positive in [`needless_pass_by_value`] pertaining to procedural macros
++* Fix false positive in [`needless_continue`] pertaining to loop labels
++* Fix false positive for [`boxed_local`] pertaining to arguments moved into closures
++* Fix false positive for [`use_self`] in nested functions
++* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846)
++* Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables
++* Fix suggestion for [`single_char_pattern`] to correctly escape single quotes
++* Avoid triggering [`redundant_closure`] in macros
++* ICE fixes: [#3805](https://github.com/rust-lang/rust-clippy/pull/3805), [#3772](https://github.com/rust-lang/rust-clippy/pull/3772), [#3741](https://github.com/rust-lang/rust-clippy/pull/3741)
++
++## Rust 1.34
++
++Released 2019-04-10
++
++[1b89724...1fac380](https://github.com/rust-lang/rust-clippy/compare/1b89724...1fac380)
++
++* New lint: [`assertions_on_constants`] to detect for example `assert!(true)`
++* New lint: [`dbg_macro`] to detect uses of the `dbg!` macro
++* New lint: [`missing_const_for_fn`] that can suggest functions to be made `const`
++* New lint: [`too_many_lines`] to detect functions with excessive LOC. It can be
++  configured using the `too-many-lines-threshold` configuration.
++* New lint: [`wildcard_enum_match_arm`] to check for wildcard enum matches using `_`
++* Expand `redundant_closure` to also work for methods (not only functions)
++* Fix ICEs in `vec_box`, `needless_pass_by_value` and `implicit_hasher`
++* Fix false positive in `cast_sign_loss`
++* Fix false positive in `integer_arithmetic`
++* Fix false positive in `unit_arg`
++* Fix false positives in `implicit_return`
++* Add suggestion to `explicit_write`
++* Improve suggestions for `question_mark` lint
++* Fix incorrect suggestion for `cast_lossless`
++* Fix incorrect suggestion for `expect_fun_call`
++* Fix incorrect suggestion for `needless_bool`
++* Fix incorrect suggestion for `needless_range_loop`
++* Fix incorrect suggestion for `use_self`
++* Fix incorrect suggestion for `while_let_on_iterator`
++* Clippy is now slightly easier to invoke in non-cargo contexts. See
++  [#3665][pull3665] for more details.
++* We now have [improved documentation][adding_lints] on how to add new lints
++
++## Rust 1.33
++
++Released 2019-02-26
++
++[b2601be...1b89724](https://github.com/rust-lang/rust-clippy/compare/b2601be...1b89724)
++
++* New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`]
++* The `rust-clippy` repository is now part of the `rust-lang` org.
++* Rename `stutter` to `module_name_repetitions`
++* Merge `new_without_default_derive` into `new_without_default` lint
++* Move `large_digit_groups` from `style` group to `pedantic`
++* Expand `bool_comparison` to check for `<`, `<=`, `>`, `>=`, and `!=`
++  comparisons against booleans
++* Expand `no_effect` to detect writes to constants such as `A_CONST.field = 2`
++* Expand `redundant_clone` to work on struct fields
++* Expand `suspicious_else_formatting` to detect `if .. {..} {..}`
++* Expand `use_self` to work on tuple structs and also in local macros
++* Fix ICE in `result_map_unit_fn` and `option_map_unit_fn`
++* Fix false positives in `implicit_return`
++* Fix false positives in `use_self`
++* Fix false negative in `clone_on_copy`
++* Fix false positive in `doc_markdown`
++* Fix false positive in `empty_loop`
++* Fix false positive in `if_same_then_else`
++* Fix false positive in `infinite_iter`
++* Fix false positive in `question_mark`
++* Fix false positive in `useless_asref`
++* Fix false positive in `wildcard_dependencies`
++* Fix false positive in `write_with_newline`
++* Add suggestion to `explicit_write`
++* Improve suggestions for `question_mark` lint
++* Fix incorrect suggestion for `get_unwrap`
++
++## Rust 1.32
++
++Released 2019-01-17
++
++[2e26fdc2...b2601be](https://github.com/rust-lang/rust-clippy/compare/2e26fdc2...b2601be)
++
++* New lints: [`slow_vector_initialization`], [`mem_discriminant_non_enum`],
++  [`redundant_clone`], [`wildcard_dependencies`],
++  [`into_iter_on_ref`], [`into_iter_on_array`], [`deprecated_cfg_attr`],
++  [`mem_discriminant_non_enum`], [`cargo_common_metadata`]
++* Add support for `u128` and `i128` to integer related lints
++* Add float support to `mistyped_literal_suffixes`
++* Fix false positives in `use_self`
++* Fix false positives in `missing_comma`
++* Fix false positives in `new_ret_no_self`
++* Fix false positives in `possible_missing_comma`
++* Fix false positive in `integer_arithmetic` in constant items
++* Fix false positive in `needless_borrow`
++* Fix false positive in `out_of_bounds_indexing`
++* Fix false positive in `new_without_default_derive`
++* Fix false positive in `string_lit_as_bytes`
++* Fix false negative in `out_of_bounds_indexing`
++* Fix false negative in `use_self`. It will now also check existential types
++* Fix incorrect suggestion for `redundant_closure_call`
++* Fix various suggestions that contained expanded macros
++* Fix `bool_comparison` triggering 3 times on on on the same code
++* Expand `trivially_copy_pass_by_ref` to work on trait methods
++* Improve suggestion for `needless_range_loop`
++* Move `needless_pass_by_value` from `pedantic` group to `style`
++
++## Rust 1.31
++
++Released 2018-12-06
++
++[125907ad..2e26fdc2](https://github.com/rust-lang/rust-clippy/compare/125907ad..2e26fdc2)
++
++* Clippy has been relicensed under a dual MIT / Apache license.
++  See [#3093](https://github.com/rust-lang/rust-clippy/issues/3093) for more
++  information.
++* With Rust 1.31, Clippy is no longer available via crates.io. The recommended
++  installation method is via `rustup component add clippy`.
++* New lints: [`redundant_pattern_matching`], [`unnecessary_filter_map`],
++  [`unused_unit`], [`map_flatten`], [`mem_replace_option_with_none`]
++* Fix ICE in `if_let_redundant_pattern_matching`
++* Fix ICE in `needless_pass_by_value` when encountering a generic function
++  argument with a lifetime parameter
++* Fix ICE in `needless_range_loop`
++* Fix ICE in `single_char_pattern` when encountering a constant value
++* Fix false positive in `assign_op_pattern`
++* Fix false positive in `boxed_local` on trait implementations
++* Fix false positive in `cmp_owned`
++* Fix false positive in `collapsible_if` when conditionals have comments
++* Fix false positive in `double_parens`
++* Fix false positive in `excessive_precision`
++* Fix false positive in `explicit_counter_loop`
++* Fix false positive in `fn_to_numeric_cast_with_truncation`
++* Fix false positive in `map_clone`
++* Fix false positive in `new_ret_no_self`
++* Fix false positive in `new_without_default` when `new` is unsafe
++* Fix false positive in `type_complexity` when using extern types
++* Fix false positive in `useless_format`
++* Fix false positive in `wrong_self_convention`
++* Fix incorrect suggestion for `excessive_precision`
++* Fix incorrect suggestion for `expect_fun_call`
++* Fix incorrect suggestion for `get_unwrap`
++* Fix incorrect suggestion for `useless_format`
++* `fn_to_numeric_cast_with_truncation` lint can be disabled again
++* Improve suggestions for `manual_memcpy`
++* Improve help message for `needless_lifetimes`
++
++## Rust 1.30
++
++Released 2018-10-25
++
++[14207503...125907ad](https://github.com/rust-lang/rust-clippy/compare/14207503...125907ad)
++
++* Deprecate `assign_ops` lint
++* New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`],
++  [`needless_collect`], [`copy_iterator`]
++* `cargo clippy -V` now includes the Clippy commit hash of the Rust
++  Clippy component
++* Fix ICE in `implicit_hasher`
++* Fix ICE when encountering `println!("{}" a);`
++* Fix ICE when encountering a macro call in match statements
++* Fix false positive in `default_trait_access`
++* Fix false positive in `trivially_copy_pass_by_ref`
++* Fix false positive in `similar_names`
++* Fix false positive in `redundant_field_name`
++* Fix false positive in `expect_fun_call`
++* Fix false negative in `identity_conversion`
++* Fix false negative in `explicit_counter_loop`
++* Fix `range_plus_one` suggestion and false negative
++* `print_with_newline` / `write_with_newline`: don't warn about string with several `\n`s in them
++* Fix `useless_attribute` to also whitelist `unused_extern_crates`
++* Fix incorrect suggestion for `single_char_pattern`
++* Improve suggestion for `identity_conversion` lint
++* Move `explicit_iter_loop` and `explicit_into_iter_loop` from `style` group to `pedantic`
++* Move `range_plus_one` and `range_minus_one` from `nursery` group to `complexity`
++* Move `shadow_unrelated` from `restriction` group to `pedantic`
++* Move `indexing_slicing` from `pedantic` group to `restriction`
++
++## Rust 1.29
++
++Released 2018-09-13
++
++[v0.0.212...14207503](https://github.com/rust-lang/rust-clippy/compare/v0.0.212...14207503)
++
++* :tada: :tada: **Rust 1.29 is the first stable Rust that includes a bundled Clippy** :tada:
++  :tada:
++  You can now run `rustup component add clippy-preview` and then `cargo
++  clippy` to run Clippy. This should put an end to the continuous nightly
++  upgrades for Clippy users.
++* Clippy now follows the Rust versioning scheme instead of its own
++* Fix ICE when encountering a `while let (..) = x.iter()` construct
++* Fix false positives in `use_self`
++* Fix false positive in `trivially_copy_pass_by_ref`
++* Fix false positive in `useless_attribute` lint
++* Fix false positive in `print_literal`
++* Fix `use_self` regressions
++* Improve lint message for `neg_cmp_op_on_partial_ord`
++* Improve suggestion highlight for `single_char_pattern`
++* Improve suggestions for various print/write macro lints
++* Improve website header
++
++## 0.0.212 (2018-07-10)
++* Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)*
++
++## 0.0.211
++* Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)*
++
++## 0.0.210
++* Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)*
++
++## 0.0.209
++* Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)*
++
++## 0.0.208
++* Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)*
++
++## 0.0.207
++* Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)*
++
++## 0.0.206
++* Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)*
++
++## 0.0.205
++* Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)*
++* Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint
++
++## 0.0.204
++* Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)*
++
++## 0.0.203
++* Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)*
++* Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)`
++
++## 0.0.202
++* Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)*
++
++## 0.0.201
++* Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)*
++
++## 0.0.200
++* Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)*
++
++## 0.0.199
++* Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)*
++
++## 0.0.198
++* Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)*
++
++## 0.0.197
++* Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)*
++
++## 0.0.196
++* Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)*
++
++## 0.0.195
++* Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)*
++
++## 0.0.194
++* Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)*
++* New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`]
++
++## 0.0.193
++* Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)*
++
++## 0.0.192
++* Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)*
++* New lint: [`print_literal`]
++
++## 0.0.191
++* Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)*
++* Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction.
++
++## 0.0.190
++* Fix a bunch of intermittent cargo bugs
++
++## 0.0.189
++* Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)*
++
++## 0.0.188
++* Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)*
++* New lint: [`while_immutable_condition`]
++
++## 0.0.187
++* Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)*
++* New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`]
++
++## 0.0.186
++* Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)*
++* Various false positive fixes
++
++## 0.0.185
++* Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)*
++* New lint: [`question_mark`]
++
++## 0.0.184
++* Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)*
++* New lints: [`double_comparisons`], [`empty_line_after_outer_attr`]
++
++## 0.0.183
++* Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)*
++* New lint: [`misaligned_transmute`]
++
++## 0.0.182
++* Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)*
++* New lint: [`decimal_literal_representation`]
++
++## 0.0.181
++* Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)*
++* New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`]
++* Removed `unit_expr`
++* Various false positive fixes for [`needless_pass_by_value`]
++
++## 0.0.180
++* Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)*
++
++## 0.0.179
++* Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)*
++
++## 0.0.178
++* Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)*
++
++## 0.0.177
++* Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)*
++* New lint: [`match_as_ref`]
++
++## 0.0.176
++* Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)*
++
++## 0.0.175
++* Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)*
++
++## 0.0.174
++* Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)*
++
++## 0.0.173
++* Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)*
++
++## 0.0.172
++* Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)*
++
++## 0.0.171
++* Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)*
++
++## 0.0.170
++* Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)*
++
++## 0.0.169
++* Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)*
++* New lints: [`just_underscores_and_digits`], [`result_map_unwrap_or_else`], [`transmute_bytes_to_str`]
++
++## 0.0.168
++* Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)*
++
++## 0.0.167
++* Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)*
++* New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`]
++
++## 0.0.166
++* Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)*
++* New lints: [`explicit_write`], [`identity_conversion`], [`implicit_hasher`], [`invalid_ref`], [`option_map_or_none`],
++  [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`],
++  [`transmute_int_to_float`]
++
++## 0.0.165
++* Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27)
++* New lint: [`mut_range_bound`]
++
++## 0.0.164
++* Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)*
++* New lint: [`int_plus_one`]
++
++## 0.0.163
++* Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)*
++
++## 0.0.162
++* Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)*
++* New lint: [`chars_last_cmp`]
++* Improved suggestions for [`needless_borrow`], [`ptr_arg`],
++
++## 0.0.161
++* Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)*
++
++## 0.0.160
++* Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)*
++
++## 0.0.159
++* Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)*
++* New lint: [`clone_on_ref_ptr`]
++
++## 0.0.158
++* New lint: [`manual_memcpy`]
++* [`cast_lossless`] no longer has redundant parentheses in its suggestions
++* Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)*
++
++## 0.0.157 - 2017-09-04
++* Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)*
++* New lint: `unit_expr`
++
++## 0.0.156 - 2017-09-03
++* Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)*
++
++## 0.0.155
++* Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)*
++* New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`]
++
++## 0.0.154
++* Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)*
++* Fix [`use_self`] triggering inside derives
++* Add support for linting an entire workspace with `cargo clippy --all`
++* New lint: [`naive_bytecount`]
++
++## 0.0.153
++* Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)*
++* New lint: [`use_self`]
++
++## 0.0.152
++* Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)*
++
++## 0.0.151
++* Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)*
++
++## 0.0.150
++* Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)*
++
++## 0.0.148
++* Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)*
++* New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`]
++
++## 0.0.147
++* Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)*
++
++## 0.0.146
++* Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)*
++* Fixes false positives in `inline_always`
++* Fixes false negatives in `panic_params`
++
++## 0.0.145
++* Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)*
++
++## 0.0.144
++* Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)*
++
++## 0.0.143
++* Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)*
++* Fix `cargo clippy` crashing on `dylib` projects
++* Fix false positives around `nested_while_let` and `never_loop`
++
++## 0.0.142
++* Update to *rustc 1.20.0-nightly (067971139 2017-07-02)*
++
++## 0.0.141
++* Rewrite of the `doc_markdown` lint.
++* Deprecated [`range_step_by_zero`]
++* New lint: [`iterator_step_by_zero`]
++* New lint: [`needless_borrowed_reference`]
++* Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)*
++
++## 0.0.140 - 2017-06-16
++* Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)*
++
++## 0.0.139 — 2017-06-10
++* Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)*
++* Fix bugs with for loop desugaring
++* Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`]
++
++## 0.0.138 — 2017-06-05
++* Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)*
++
++## 0.0.137 — 2017-06-05
++* Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)*
++
++## 0.0.136 — 2017—05—26
++* Update to *rustc 1.19.0-nightly (557967766 2017-05-26)*
++
++## 0.0.135 — 2017—05—24
++* Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)*
++
++## 0.0.134 — 2017—05—19
++* Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)*
++
++## 0.0.133 — 2017—05—14
++* Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)*
++
++## 0.0.132 — 2017—05—05
++* Fix various bugs and some ices
++
++## 0.0.131 — 2017—05—04
++* Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)*
++
++## 0.0.130 — 2017—05—03
++* Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)*
++
++## 0.0.129 — 2017-05-01
++* Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)*
++
++## 0.0.128 — 2017-04-28
++* Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)*
++
++## 0.0.127 — 2017-04-27
++* Update to *rustc 1.18.0-nightly (036983201 2017-04-26)*
++* New lint: [`needless_continue`]
++
++## 0.0.126 — 2017-04-24
++* Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)*
++
++## 0.0.125 — 2017-04-19
++* Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)*
++
++## 0.0.124 — 2017-04-16
++* Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)*
++
++## 0.0.123 — 2017-04-07
++* Fix various false positives
++
++## 0.0.122 — 2017-04-07
++* Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)*
++* New lint: [`op_ref`]
++
++## 0.0.121 — 2017-03-21
++* Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)*
++
++## 0.0.120 — 2017-03-17
++* Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)*
++
++## 0.0.119 — 2017-03-13
++* Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)*
++
++## 0.0.118 — 2017-03-05
++* Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)*
++
++## 0.0.117 — 2017-03-01
++* Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)*
++
++## 0.0.116 — 2017-02-28
++* Fix `cargo clippy` on 64 bit windows systems
++
++## 0.0.115 — 2017-02-27
++* Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)*
++* New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`]
++
++## 0.0.114 — 2017-02-08
++* Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)*
++* Tests are now ui tests (testing the exact output of rustc)
++
++## 0.0.113 — 2017-02-04
++* Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)*
++* New lint: [`large_enum_variant`]
++* `explicit_into_iter_loop` provides suggestions
++
++## 0.0.112 — 2017-01-27
++* Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)*
++
++## 0.0.111 — 2017-01-21
++* Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)*
++
++## 0.0.110 — 2017-01-20
++* Add badges and categories to `Cargo.toml`
++
++## 0.0.109 — 2017-01-19
++* Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)*
++
++## 0.0.108 — 2017-01-12
++* Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)*
++
++## 0.0.107 — 2017-01-11
++* Update regex dependency
++* Fix FP when matching `&&mut` by `&ref`
++* Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()`
++* New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`]
++
++## 0.0.106 — 2017-01-04
++* Fix FP introduced by rustup in [`wrong_self_convention`]
++
++## 0.0.105 — 2017-01-04
++* Update to *rustc 1.16.0-nightly (468227129 2017-01-03)*
++* New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`]
++* Fix suggestion in [`new_without_default`]
++* FP fix in [`absurd_extreme_comparisons`]
++
++## 0.0.104 — 2016-12-15
++* Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)*
++
++## 0.0.103 — 2016-11-25
++* Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)*
++
++## 0.0.102 — 2016-11-24
++* Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)*
++
++## 0.0.101 — 2016-11-23
++* Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)*
++* New lint: [`string_extend_chars`]
++
++## 0.0.100 — 2016-11-20
++* Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)*
++
++## 0.0.99 — 2016-11-18
++* Update to rustc 1.15.0-nightly (0ed951993 2016-11-14)
++* New lint: [`get_unwrap`]
++
++## 0.0.98 — 2016-11-08
++* Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy`
++
++## 0.0.97 — 2016-11-03
++* For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was
++  previously added for a short time under the name `clippy` but removed for
++  compatibility.
++* `cargo clippy --help` is more helping (and less helpful :smile:)
++* Rustup to *rustc 1.14.0-nightly (5665bdf3e 2016-11-02)*
++* New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`]
++
++## 0.0.96 — 2016-10-22
++* Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)*
++* New lint: [`iter_skip_next`]
++
++## 0.0.95 — 2016-10-06
++* Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)*
++
++## 0.0.94 — 2016-10-04
++* Fixes bustage on Windows due to forbidden directory name
++
++## 0.0.93 — 2016-10-03
++* Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)*
++* [`option_map_unwrap_or`] and [`option_map_unwrap_or_else`] are now
++  allowed by default.
++* New lint: [`explicit_into_iter_loop`]
++
++## 0.0.92 — 2016-09-30
++* Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)*
++
++## 0.0.91 — 2016-09-28
++* Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)*
++
++## 0.0.90 — 2016-09-09
++* Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)*
++
++## 0.0.89 — 2016-09-06
++* Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)*
++
++## 0.0.88 — 2016-09-04
++* Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)*
++* The following lints are not new but were only usable through the `clippy`
++  lint groups: [`filter_next`], [`for_loop_over_option`],
++  [`for_loop_over_result`] and [`match_overlapping_arm`]. You should now be
++  able to `#[allow/deny]` them individually and they are available directly
++  through `cargo clippy`.
++
++## 0.0.87 — 2016-08-31
++* Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)*
++* New lints: [`builtin_type_shadow`]
++* Fix FP in [`zero_prefixed_literal`] and `0b`/`0o`
++
++## 0.0.86 — 2016-08-28
++* Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)*
++* New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`]
++
++## 0.0.85 — 2016-08-19
++* Fix ICE with [`useless_attribute`]
++* [`useless_attribute`] ignores `unused_imports` on `use` statements
++
++## 0.0.84 — 2016-08-18
++* Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)*
++
++## 0.0.83 — 2016-08-17
++* Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)*
++* New lints: [`print_with_newline`], [`useless_attribute`]
++
++## 0.0.82 — 2016-08-17
++* Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)*
++* New lint: [`module_inception`]
++
++## 0.0.81 — 2016-08-14
++* Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)*
++* New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`]
++* False positive fix in [`too_many_arguments`]
++* Addition of functionality to [`needless_borrow`]
++* Suggestions for [`clone_on_copy`]
++* Bug fix in [`wrong_self_convention`]
++* Doc improvements
++
++## 0.0.80 — 2016-07-31
++* Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)*
++* New lints: [`misrefactored_assign_op`], [`serde_api_misuse`]
++
++## 0.0.79 — 2016-07-10
++* Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)*
++* Major suggestions refactoring
++
++## 0.0.78 — 2016-07-02
++* Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)*
++* New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`]
++* For compatibility, `cargo clippy` does not defines the `clippy` feature
++  introduced in 0.0.76 anymore
++* [`collapsible_if`] now considers `if let`
++
++## 0.0.77 — 2016-06-21
++* Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)*
++* New lints: `stutter` and [`iter_nth`]
++
++## 0.0.76 — 2016-06-10
++* Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)*
++* `cargo clippy` now automatically defines the `clippy` feature
++* New lint: [`not_unsafe_ptr_arg_deref`]
++
++## 0.0.75 — 2016-06-08
++* Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)*
++
++## 0.0.74 — 2016-06-07
++* Fix bug with `cargo-clippy` JSON parsing
++* Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the
++  “for further information visit *lint-link*” message.
++
++## 0.0.73 — 2016-06-05
++* Fix false positives in [`useless_let_if_seq`]
++
++## 0.0.72 — 2016-06-04
++* Fix false positives in [`useless_let_if_seq`]
++
++## 0.0.71 — 2016-05-31
++* Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)*
++* New lint: [`useless_let_if_seq`]
++
++## 0.0.70 — 2016-05-28
++* Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)*
++* [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`,
++  `RegexBuilder::new` and byte regexes
++
++## 0.0.69 — 2016-05-20
++* Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)*
++* [`used_underscore_binding`] has been made `Allow` temporarily
++
++## 0.0.68 — 2016-05-17
++* Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)*
++* New lint: [`unnecessary_operation`]
++
++## 0.0.67 — 2016-05-12
++* Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)*
++
++## 0.0.66 — 2016-05-11
++* New `cargo clippy` subcommand
++* New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`]
++
++## 0.0.65 — 2016-05-08
++* Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)*
++* New lints: [`float_arithmetic`], [`integer_arithmetic`]
++
++## 0.0.64 — 2016-04-26
++* Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)*
++* New lints: [`temporary_cstring_as_ptr`], [`unsafe_removed_from_name`], and [`mem_forget`]
++
++## 0.0.63 — 2016-04-08
++* Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)*
++
++## 0.0.62 — 2016-04-07
++* Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)*
++
++## 0.0.61 — 2016-04-03
++* Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)*
++* New lint: [`invalid_upcast_comparisons`]
++
++## 0.0.60 — 2016-04-01
++* Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)*
++
++## 0.0.59 — 2016-03-31
++* Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)*
++* New lints: [`logic_bug`], [`nonminimal_bool`]
++* Fixed: [`match_same_arms`] now ignores arms with guards
++* Improved: [`useless_vec`] now warns on `for … in vec![…]`
++
++## 0.0.58 — 2016-03-27
++* Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)*
++* New lint: [`doc_markdown`]
++
++## 0.0.57 — 2016-03-27
++* Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)*
++* Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`]
++* New lint: [`crosspointer_transmute`]
++
++## 0.0.56 — 2016-03-23
++* Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)*
++* New lints: [`many_single_char_names`] and [`similar_names`]
++
++## 0.0.55 — 2016-03-21
++* Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)*
++
++## 0.0.54 — 2016-03-16
++* Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)*
++
++## 0.0.53 — 2016-03-15
++* Add a [configuration file]
++
++## ~~0.0.52~~
++
++## 0.0.51 — 2016-03-13
++* Add `str` to types considered by [`len_zero`]
++* New lints: [`indexing_slicing`]
++
++## 0.0.50 — 2016-03-11
++* Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)*
++
++## 0.0.49 — 2016-03-09
++* Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)*
++* New lints: [`overflow_check_conditional`], [`unused_label`], [`new_without_default`]
++
++## 0.0.48 — 2016-03-07
++* Fixed: ICE in [`needless_range_loop`] with globals
++
++## 0.0.47 — 2016-03-07
++* Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)*
++* New lint: [`redundant_closure_call`]
++
++[`AsMut`]: https://doc.rust-lang.org/std/convert/trait.AsMut.html
++[`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
++[configuration file]: ./rust-clippy#configuration
++[pull3665]: https://github.com/rust-lang/rust-clippy/pull/3665
++[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
++
++<!-- lint disable no-unused-definitions -->
++<!-- begin autogenerated links to lint list -->
++[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
++[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
++[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
++[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
++[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
++[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
++[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
++[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
++[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
++[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
++[`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr
++[`block_in_if_condition_stmt`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_stmt
++[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
++[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
++[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
++[`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec
++[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
++[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
++[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
++[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
++[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
++[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
++[`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss
++[`cast_ptr_alignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ptr_alignment
++[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
++[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
++[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
++[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
++[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
++[`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions
++[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
++[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
++[`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
++[`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
++[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
++[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
++[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
++[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
++[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
++[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
++[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
++[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
++[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
++[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
++[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
++[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
++[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
++[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
++[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
++[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
++[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
++[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
++[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
++[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
++[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
++[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
++[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
++[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
++[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
++[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
++[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
++[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
++[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
++[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
++[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
++[`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant
++[`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use
++[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
++[`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op
++[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
++[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
++[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
++[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
++[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
++[`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy
++[`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop
++[`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods
++[`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop
++[`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop
++[`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write
++[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
++[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
++[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
++[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
++[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
++[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
++[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
++[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
++[`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
++[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
++[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
++[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
++[`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons
++[`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools
++[`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast
++[`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation
++[`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map
++[`for_loop_over_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_option
++[`for_loop_over_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_result
++[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
++[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
++[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
++[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
++[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
++[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion
++[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
++[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
++[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
++[`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
++[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
++[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
++[`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
++[`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
++[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
++[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
++[`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
++[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
++[`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
++[`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
++[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
++[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
++[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
++[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
++[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
++[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
++[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
++[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
++[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
++[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
++[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
++[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
++[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
++[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
++[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
++[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
++[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
++[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
++[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
++[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
++[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
++[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
++[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
++[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
++[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
++[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
++[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
++[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
++[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
++[`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock
++[`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
++[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
++[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
++[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
++[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
++[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
++[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
++[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
++[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
++[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
++[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
++[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
++[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
++[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
++[`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
++[`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
++[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
++[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
++[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
++[`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
++[`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
++[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
++[`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter
++[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
++[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
++[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
++[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default
++[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
++[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
++[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
++[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
++[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
++[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
++[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
++[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
++[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
++[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
++[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
++[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
++[`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
++[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
++[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
++[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
++[`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
++[`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl
++[`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate
++[`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit
++[`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref
++[`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut
++[`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound
++[`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type
++[`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic
++[`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
++[`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
++[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
++[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
++[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
++[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
++[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
++[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
++[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
++[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
++[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
++[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
++[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
++[`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
++[`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
++[`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop
++[`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self
++[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
++[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
++[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
++[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
++[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
++[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
++[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
++[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
++[`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some
++[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
++[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
++[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used
++[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
++[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
++[`option_map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or
++[`option_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or_else
++[`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
++[`option_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_used
++[`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call
++[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
++[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
++[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
++[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
++[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
++[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
++[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
++[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
++[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
++[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
++[`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
++[`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
++[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
++[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
++[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
++[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
++[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
++[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
++[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
++[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
++[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
++[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
++[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
++[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
++[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
++[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
++[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
++[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
++[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
++[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
++[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
++[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
++[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
++[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
++[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
++[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
++[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
++[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
++[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else
++[`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used
++[`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop
++[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
++[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
++[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
++[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
++[`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same
++[`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
++[`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement
++[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq
++[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
++[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
++[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
++[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
++[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
++[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
++[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
++[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
++[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
++[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
++[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
++[`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars
++[`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
++[`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
++[`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
++[`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops
++[`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
++[`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting
++[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
++[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
++[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
++[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
++[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
++[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
++[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
++[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
++[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
++[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
++[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
++[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
++[`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str
++[`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int
++[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
++[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
++[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
++[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
++[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
++[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
++[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
++[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
++[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
++[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
++[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
++[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
++[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
++[`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init
++[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
++[`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp
++[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
++[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
++[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
++[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
++[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
++[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
++[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
++[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
++[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
++[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable
++[`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal
++[`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize
++[`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name
++[`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization
++[`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix
++[`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute
++[`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice
++[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
++[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
++[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
++[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
++[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
++[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
++[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
++[`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
++[`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding
++[`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref
++[`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute
++[`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format
++[`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq
++[`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute
++[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
++[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box
++[`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask
++[`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads
++[`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons
++[`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition
++[`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop
++[`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator
++[`wildcard_dependencies`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_dependencies
++[`wildcard_enum_match_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_enum_match_arm
++[`wildcard_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports
++[`wildcard_in_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_in_or_patterns
++[`write_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_literal
++[`write_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_with_newline
++[`writeln_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#writeln_empty_string
++[`wrong_pub_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_pub_self_convention
++[`wrong_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention
++[`wrong_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_transmute
++[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
++[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
++[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
++[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
++[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
++<!-- end autogenerated links to lint list -->
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dec13e44a17f88a7ff734f3b6b4492d9e86cfd21
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,70 @@@
++# The Rust Code of Conduct
++
++A version of this document [can be found online](https://www.rust-lang.org/conduct.html).
++
++## Conduct
++
++**Contact**: [rust-mods@rust-lang.org](mailto:rust-mods@rust-lang.org)
++
++* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience,
++  gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
++  religion, nationality, or other similar characteristic.
++* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and
++  welcoming environment for all.
++* Please be kind and courteous. There's no need to be mean or rude.
++* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and
++  numerous costs. There is seldom a right answer.
++* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and
++  see how it works.
++* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We
++  interpret the term "harassment" as including the definition in the <a href="http://citizencodeofconduct.org/">Citizen
++  Code of Conduct</a>; if you have any lack of clarity about what might be included in that concept, please read their
++  definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups.
++* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or
++  made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation
++  team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a
++  safe place for you and we've got your back.
++* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
++
++## Moderation
++
++
++These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation,
++please contact the [Rust moderation team][mod_team].
++
++1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks,
++   are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.)
++2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.
++3. Moderators will first respond to such remarks with a warning.
++4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off.
++5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
++6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended
++   party a genuine apology.
++7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a
++   different moderator, **in private**. Complaints about bans in-channel are not allowed.
++8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate
++   situation, they should expect less leeway than others.
++
++In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically
++unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly
++if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can
++drive people away from the community entirely.
++
++And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was
++they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good
++there was something you could've communicated better — remember that it's your responsibility to make your fellow
++Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about
++cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their
++trust.
++
++The enforcement policies listed above apply to all official Rust venues; including official IRC channels (#rust,
++#rust-internals, #rust-tools, #rust-libs, #rustc, #rust-beginners, #rust-docs, #rust-community, #rust-lang, and #cargo);
++GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org
++(users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the
++maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider
++explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion.
++
++*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the
++[Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).*
++
++[mod_team]: https://www.rust-lang.org/team.html#Moderation-team
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..50a5ee8bbf3c83bc0e583927d97ace463b3a3794
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,243 @@@
++# Contributing to Clippy
++
++Hello fellow Rustacean! Great to see your interest in compiler internals and lints!
++
++**First**: if you're unsure or afraid of _anything_, just ask or submit the issue or pull request anyway. You won't be
++yelled at for giving it your best effort. The worst that can happen is that you'll be politely asked to change
++something. We appreciate any sort of contributions, and don't want a wall of rules to get in the way of that.
++
++Clippy welcomes contributions from everyone. There are many ways to contribute to Clippy and the following document
++explains how you can contribute and how to get started.  If you have any questions about contributing or need help with
++anything, feel free to ask questions on issues or visit the `#clippy` on [Discord].
++
++All contributors are expected to follow the [Rust Code of Conduct].
++
++* [Getting started](#getting-started)
++  * [Finding something to fix/improve](#finding-something-to-fiximprove)
++* [Writing code](#writing-code)
++* [How Clippy works](#how-clippy-works)
++* [Fixing nightly build failures](#fixing-build-failures-caused-by-rust)
++* [Issue and PR Triage](#issue-and-pr-triage)
++* [Bors and Homu](#bors-and-homu)
++* [Contributions](#contributions)
++
++[Discord]: https://discord.gg/rust-lang
++[Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct
++
++## Getting started
++
++High level approach:
++
++1. Find something to fix/improve
++2. Change code (likely some file in `clippy_lints/src/`)
++3. Follow the instructions in the [docs for writing lints](doc/adding_lints.md) such as running the `setup-toolchain.sh` script
++4. Run `cargo test` in the root directory and wiggle code until it passes
++5. Open a PR (also can be done after 2. if you run into problems)
++
++### Finding something to fix/improve
++
++All issues on Clippy are mentored, if you want help with a bug just ask
++@Manishearth, @flip1995, @phansch or @yaahc.
++
++Some issues are easier than others. The [`good first issue`] label can be used to find the easy issues.
++If you want to work on an issue, please leave a comment so that we can assign it to you!
++
++There are also some abandoned PRs, marked with [`S-inactive-closed`].
++Pretty often these PRs are nearly completed and just need some extra steps
++(formatting, addressing review comments, ...) to be merged. If you want to
++complete such a PR, please leave a comment in the PR and open a new one based
++on it.
++
++Issues marked [`T-AST`] involve simple matching of the syntax tree structure,
++and are generally easier than [`T-middle`] issues, which involve types
++and resolved paths.
++
++[`T-AST`] issues will generally need you to match against a predefined syntax structure.
++To figure out how this syntax structure is encoded in the AST, it is recommended to run
++`rustc -Z ast-json` on an example of the structure and compare with the [nodes in the AST docs].
++Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting].
++But we can make it nest-less by using [if_chain] macro, [like this][nest-less].
++
++[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an E-easy issue first.
++They are mostly classified as [`E-medium`], since they might be somewhat involved code wise,
++but not difficult per-se.
++
++[`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a
++lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of
++an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful.
++
++[`good first issue`]: https://github.com/rust-lang/rust-clippy/labels/good%20first%20issue
++[`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed
++[`T-AST`]: https://github.com/rust-lang/rust-clippy/labels/T-AST
++[`T-middle`]: https://github.com/rust-lang/rust-clippy/labels/T-middle
++[`E-medium`]: https://github.com/rust-lang/rust-clippy/labels/E-medium
++[`ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty
++[nodes in the AST docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/
++[deep-nesting]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/mem_forget.rs#L29-L43
++[if_chain]: https://docs.rs/if_chain/*/if_chain
++[nest-less]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/bit_mask.rs#L124-L150
++
++## Writing code
++
++Have a look at the [docs for writing lints][adding_lints] for more details.
++
++If you want to add a new lint or change existing ones apart from bugfixing, it's
++also a good idea to give the [stability guarantees][rfc_stability] and
++[lint categories][rfc_lint_cats] sections of the [Clippy 1.0 RFC][clippy_rfc] a
++quick read.
++
++[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
++[clippy_rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md
++[rfc_stability]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#stability-guarantees
++[rfc_lint_cats]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories
++
++## How Clippy works
++
++[`clippy_lints/src/lib.rs`][lint_crate_entry] imports all the different lint modules and registers in the [`LintStore`].
++For example, the [`else_if_without_else`][else_if_without_else] lint is registered like this:
++
++```rust
++// ./clippy_lints/src/lib.rs
++
++// ...
++pub mod else_if_without_else;
++// ...
++
++pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
++    // ...
++    store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse);
++    // ...
++
++    store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
++        // ...
++        LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
++        // ...
++    ]);
++}
++```
++
++The [`rustc_lint::LintStore`][`LintStore`] provides two methods to register lints:
++[register_early_pass][reg_early_pass] and [register_late_pass][reg_late_pass]. Both take an object
++that implements an [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass] respectively. This is done in
++every single lint. It's worth noting that the majority of `clippy_lints/src/lib.rs` is autogenerated by `cargo dev
++update_lints`. When you are writing your own lint, you can use that script to save you some time.
++
++```rust
++// ./clippy_lints/src/else_if_without_else.rs
++
++use rustc_lint::{EarlyLintPass, EarlyContext};
++
++// ...
++
++pub struct ElseIfWithoutElse;
++
++// ...
++
++impl EarlyLintPass for ElseIfWithoutElse {
++    // ... the functions needed, to make the lint work
++}
++```
++
++The difference between `EarlyLintPass` and `LateLintPass` is that the methods of the `EarlyLintPass` trait only provide
++AST information. The methods of the `LateLintPass` trait are executed after type checking and contain type information
++via the `LateContext` parameter.
++
++That's why the `else_if_without_else` example uses the `register_early_pass` function. Because the
++[actual lint logic][else_if_without_else] does not depend on any type information.
++
++[lint_crate_entry]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs
++[else_if_without_else]: https://github.com/rust-lang/rust-clippy/blob/4253aa7137cb7378acc96133c787e49a345c2b3c/clippy_lints/src/else_if_without_else.rs
++[`LintStore`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html
++[reg_early_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_early_pass
++[reg_late_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_late_pass
++[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
++[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
++
++## Fixing build failures caused by Rust
++
++Clippy will sometimes fail to build from source because building it depends on unstable internal Rust features. Most of
++the times we have to adapt to the changes and only very rarely there's an actual bug in Rust. Fixing build failures
++caused by Rust updates, can be a good way to learn about Rust internals.
++
++In order to find out why Clippy does not work properly with a new Rust commit, you can use the [rust-toolstate commit
++history][toolstate_commit_history]. You will then have to look for the last commit that contains
++`test-pass -> build-fail` or `test-pass -> test-fail` for the `clippy-driver` component.
++[Here][toolstate_commit] is an example.
++
++The commit message contains a link to the PR. The PRs are usually small enough to discover the breaking API change and
++if they are bigger, they likely include some discussion that may help you to fix Clippy.
++
++To check if Clippy is available for a specific target platform, you can check
++the [rustup component history][rustup_component_history].
++
++If you decide to make Clippy work again with a Rust commit that breaks it,
++you probably want to install the latest Rust from master locally and run Clippy
++using that version of Rust.
++
++You can set up the master toolchain by running `./setup-toolchain.sh`. That script will install
++[rustup-toolchain-install-master][rtim] and master toolchain, then run `rustup override set master`.
++
++After fixing the build failure on this repository, we can submit a pull request
++to [`rust-lang/rust`] to fix the toolstate.
++
++To submit a pull request, you should follow these steps:
++
++```bash
++# Assuming you already cloned the rust-lang/rust repo and you're in the correct directory
++git submodule update --remote src/tools/clippy
++cargo update -p clippy
++git add -u
++git commit -m "Update Clippy"
++./x.py test -i --stage 1 src/tools/clippy # This is optional and should succeed anyway
++# Open a PR in rust-lang/rust
++```
++
++[rustup_component_history]: https://rust-lang.github.io/rustup-components-history
++[toolstate_commit_history]: https://github.com/rust-lang-nursery/rust-toolstate/commits/master
++[toolstate_commit]: https://github.com/rust-lang-nursery/rust-toolstate/commit/aad74d8294e198a7cf8ac81a91aebb7f3bbcf727
++[rtim]: https://github.com/kennytm/rustup-toolchain-install-master
++[`rust-lang/rust`]: https://github.com/rust-lang/rust
++
++## Issue and PR triage
++
++Clippy is following the [Rust triage procedure][triage] for issues and pull
++requests.
++
++However, we are a smaller project with all contributors being volunteers
++currently. Between writing new lints, fixing issues, reviewing pull requests and
++responding to issues there may not always be enough time to stay on top of it
++all.
++
++Our highest priority is fixing [crashes][l-crash] and [bugs][l-bug]. We don't
++want Clippy to crash on your code and we want it to be as reliable as the
++suggestions from Rust compiler errors.
++
++## Bors and Homu
++
++We use a bot powered by [Homu][homu] to help automate testing and landing of pull
++requests in Clippy. The bot's username is @bors.
++
++You can find the Clippy bors queue [here][homu_queue].
++
++If you have @bors permissions, you can find an overview of the available
++commands [here][homu_instructions].
++
++[triage]: https://forge.rust-lang.org/release/triage-procedure.html
++[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash%20%3Aboom%3A
++[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug%20%3Abeetle%3A
++[homu]: https://github.com/rust-lang/homu
++[homu_instructions]: https://buildbot2.rust-lang.org/homu/
++[homu_queue]: https://buildbot2.rust-lang.org/homu/queue/clippy
++
++## Contributions
++
++Contributions to Clippy should be made in the form of GitHub pull requests. Each pull request will
++be reviewed by a core contributor (someone with permission to land patches) and either landed in the
++main tree or given feedback for changes that would be required.
++
++All code in this repository is under the [Apache-2.0] or the [MIT] license.
++
++<!-- adapted from https://github.com/servo/servo/blob/master/CONTRIBUTING.md -->
++
++[Apache-2.0]: https://www.apache.org/licenses/LICENSE-2.0
++[MIT]: https://opensource.org/licenses/MIT
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..80d64472c70a68f4d4f8cbbff6b028d33eab91cc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++Copyright 2014-2020 The Rust Project Developers
++
++Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++<LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++option. All files in the project carrying such notice may not be
++copied, modified, or distributed except according to those terms.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..63ce2cd8cad776ccf14c5da2da075358fbaf4660
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,59 @@@
++[package]
++name = "clippy"
++version = "0.0.212"
++authors = [
++      "Manish Goregaokar <manishsmail@gmail.com>",
++      "Andre Bogus <bogusandre@gmail.com>",
++      "Georg Brandl <georg@python.org>",
++      "Martin Carton <cartonmartin@gmail.com>",
++      "Oliver Schneider <clippy-iethah7aipeen8neex1a@oli-obk.de>"
++]
++description = "A bunch of helpful lints to avoid common pitfalls in Rust"
++repository = "https://github.com/rust-lang/rust-clippy"
++readme = "README.md"
++license = "MIT OR Apache-2.0"
++keywords = ["clippy", "lint", "plugin"]
++categories = ["development-tools", "development-tools::cargo-plugins"]
++build = "build.rs"
++edition = "2018"
++publish = false
++
++[[bin]]
++name = "cargo-clippy"
++test = false
++path = "src/main.rs"
++
++[[bin]]
++name = "clippy-driver"
++path = "src/driver.rs"
++
++[dependencies]
++# begin automatic update
++clippy_lints = { version = "0.0.212", path = "clippy_lints" }
++# end automatic update
++regex = "1"
++semver = "0.9"
++rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
++tempfile = { version = "3.1.0", optional = true }
++lazy_static = "1.0"
++
++[dev-dependencies]
++cargo_metadata = "0.9.0"
++compiletest_rs = { version = "0.5.0", features = ["tmp"] }
++tester = "0.7"
++lazy_static = "1.0"
++clippy-mini-macro-test = { version = "0.2", path = "mini-macro" }
++serde = { version = "1.0", features = ["derive"] }
++derive-new = "0.5"
++
++# 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`
++# for more information.
++rustc-workspace-hack = "1.0.0"
++
++[build-dependencies]
++rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
++
++[features]
++deny-warnings = []
++integration = ["tempfile"]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d821a4de2bed8ac8de70cc84f22ca07ef3aae661
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,201 @@@
++                              Apache License
++                        Version 2.0, January 2004
++                     http://www.apache.org/licenses/
++
++TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
++
++1. Definitions.
++
++   "License" shall mean the terms and conditions for use, reproduction,
++   and distribution as defined by Sections 1 through 9 of this document.
++
++   "Licensor" shall mean the copyright owner or entity authorized by
++   the copyright owner that is granting the License.
++
++   "Legal Entity" shall mean the union of the acting entity and all
++   other entities that control, are controlled by, or are under common
++   control with that entity. For the purposes of this definition,
++   "control" means (i) the power, direct or indirect, to cause the
++   direction or management of such entity, whether by contract or
++   otherwise, or (ii) ownership of fifty percent (50%) or more of the
++   outstanding shares, or (iii) beneficial ownership of such entity.
++
++   "You" (or "Your") shall mean an individual or Legal Entity
++   exercising permissions granted by this License.
++
++   "Source" form shall mean the preferred form for making modifications,
++   including but not limited to software source code, documentation
++   source, and configuration files.
++
++   "Object" form shall mean any form resulting from mechanical
++   transformation or translation of a Source form, including but
++   not limited to compiled object code, generated documentation,
++   and conversions to other media types.
++
++   "Work" shall mean the work of authorship, whether in Source or
++   Object form, made available under the License, as indicated by a
++   copyright notice that is included in or attached to the work
++   (an example is provided in the Appendix below).
++
++   "Derivative Works" shall mean any work, whether in Source or Object
++   form, that is based on (or derived from) the Work and for which the
++   editorial revisions, annotations, elaborations, or other modifications
++   represent, as a whole, an original work of authorship. For the purposes
++   of this License, Derivative Works shall not include works that remain
++   separable from, or merely link (or bind by name) to the interfaces of,
++   the Work and Derivative Works thereof.
++
++   "Contribution" shall mean any work of authorship, including
++   the original version of the Work and any modifications or additions
++   to that Work or Derivative Works thereof, that is intentionally
++   submitted to Licensor for inclusion in the Work by the copyright owner
++   or by an individual or Legal Entity authorized to submit on behalf of
++   the copyright owner. For the purposes of this definition, "submitted"
++   means any form of electronic, verbal, or written communication sent
++   to the Licensor or its representatives, including but not limited to
++   communication on electronic mailing lists, source code control systems,
++   and issue tracking systems that are managed by, or on behalf of, the
++   Licensor for the purpose of discussing and improving the Work, but
++   excluding communication that is conspicuously marked or otherwise
++   designated in writing by the copyright owner as "Not a Contribution."
++
++   "Contributor" shall mean Licensor and any individual or Legal Entity
++   on behalf of whom a Contribution has been received by Licensor and
++   subsequently incorporated within the Work.
++
++2. Grant of Copyright License. Subject to the terms and conditions of
++   this License, each Contributor hereby grants to You a perpetual,
++   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++   copyright license to reproduce, prepare Derivative Works of,
++   publicly display, publicly perform, sublicense, and distribute the
++   Work and such Derivative Works in Source or Object form.
++
++3. Grant of Patent License. Subject to the terms and conditions of
++   this License, each Contributor hereby grants to You a perpetual,
++   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++   (except as stated in this section) patent license to make, have made,
++   use, offer to sell, sell, import, and otherwise transfer the Work,
++   where such license applies only to those patent claims licensable
++   by such Contributor that are necessarily infringed by their
++   Contribution(s) alone or by combination of their Contribution(s)
++   with the Work to which such Contribution(s) was submitted. If You
++   institute patent litigation against any entity (including a
++   cross-claim or counterclaim in a lawsuit) alleging that the Work
++   or a Contribution incorporated within the Work constitutes direct
++   or contributory patent infringement, then any patent licenses
++   granted to You under this License for that Work shall terminate
++   as of the date such litigation is filed.
++
++4. Redistribution. You may reproduce and distribute copies of the
++   Work or Derivative Works thereof in any medium, with or without
++   modifications, and in Source or Object form, provided that You
++   meet the following conditions:
++
++   (a) You must give any other recipients of the Work or
++       Derivative Works a copy of this License; and
++
++   (b) You must cause any modified files to carry prominent notices
++       stating that You changed the files; and
++
++   (c) You must retain, in the Source form of any Derivative Works
++       that You distribute, all copyright, patent, trademark, and
++       attribution notices from the Source form of the Work,
++       excluding those notices that do not pertain to any part of
++       the Derivative Works; and
++
++   (d) If the Work includes a "NOTICE" text file as part of its
++       distribution, then any Derivative Works that You distribute must
++       include a readable copy of the attribution notices contained
++       within such NOTICE file, excluding those notices that do not
++       pertain to any part of the Derivative Works, in at least one
++       of the following places: within a NOTICE text file distributed
++       as part of the Derivative Works; within the Source form or
++       documentation, if provided along with the Derivative Works; or,
++       within a display generated by the Derivative Works, if and
++       wherever such third-party notices normally appear. The contents
++       of the NOTICE file are for informational purposes only and
++       do not modify the License. You may add Your own attribution
++       notices within Derivative Works that You distribute, alongside
++       or as an addendum to the NOTICE text from the Work, provided
++       that such additional attribution notices cannot be construed
++       as modifying the License.
++
++   You may add Your own copyright statement to Your modifications and
++   may provide additional or different license terms and conditions
++   for use, reproduction, or distribution of Your modifications, or
++   for any such Derivative Works as a whole, provided Your use,
++   reproduction, and distribution of the Work otherwise complies with
++   the conditions stated in this License.
++
++5. Submission of Contributions. Unless You explicitly state otherwise,
++   any Contribution intentionally submitted for inclusion in the Work
++   by You to the Licensor shall be under the terms and conditions of
++   this License, without any additional terms or conditions.
++   Notwithstanding the above, nothing herein shall supersede or modify
++   the terms of any separate license agreement you may have executed
++   with Licensor regarding such Contributions.
++
++6. Trademarks. This License does not grant permission to use the trade
++   names, trademarks, service marks, or product names of the Licensor,
++   except as required for reasonable and customary use in describing the
++   origin of the Work and reproducing the content of the NOTICE file.
++
++7. Disclaimer of Warranty. Unless required by applicable law or
++   agreed to in writing, Licensor provides the Work (and each
++   Contributor provides its Contributions) on an "AS IS" BASIS,
++   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
++   implied, including, without limitation, any warranties or conditions
++   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
++   PARTICULAR PURPOSE. You are solely responsible for determining the
++   appropriateness of using or redistributing the Work and assume any
++   risks associated with Your exercise of permissions under this License.
++
++8. Limitation of Liability. In no event and under no legal theory,
++   whether in tort (including negligence), contract, or otherwise,
++   unless required by applicable law (such as deliberate and grossly
++   negligent acts) or agreed to in writing, shall any Contributor be
++   liable to You for damages, including any direct, indirect, special,
++   incidental, or consequential damages of any character arising as a
++   result of this License or out of the use or inability to use the
++   Work (including but not limited to damages for loss of goodwill,
++   work stoppage, computer failure or malfunction, or any and all
++   other commercial damages or losses), even if such Contributor
++   has been advised of the possibility of such damages.
++
++9. Accepting Warranty or Additional Liability. While redistributing
++   the Work or Derivative Works thereof, You may choose to offer,
++   and charge a fee for, acceptance of support, warranty, indemnity,
++   or other liability obligations and/or rights consistent with this
++   License. However, in accepting such obligations, You may act only
++   on Your own behalf and on Your sole responsibility, not on behalf
++   of any other Contributor, and only if You agree to indemnify,
++   defend, and hold each Contributor harmless for any liability
++   incurred by, or claims asserted against, such Contributor by reason
++   of your accepting any such warranty or additional liability.
++
++END OF TERMS AND CONDITIONS
++
++APPENDIX: How to apply the Apache License to your work.
++
++   To apply the Apache License to your work, attach the following
++   boilerplate notice, with the fields enclosed by brackets "[]"
++   replaced with your own identifying information. (Don't include
++   the brackets!)  The text should be enclosed in the appropriate
++   comment syntax for the file format. We also recommend that a
++   file or class name and description of purpose be included on the
++   same "printed page" as the copyright notice for easier
++   identification within third-party archives.
++
++Copyright 2014-2020 The Rust Project Developers
++
++Licensed under the Apache License, Version 2.0 (the "License");
++you may not use this file except in compliance with the License.
++You may obtain a copy of the License at
++
++      http://www.apache.org/licenses/LICENSE-2.0
++
++Unless required by applicable law or agreed to in writing, software
++distributed under the License is distributed on an "AS IS" BASIS,
++WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++See the License for the specific language governing permissions and
++limitations under the License.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b7c70dd4026d9c96fdc5edd502b259bb0a83dc61
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++MIT License
++
++Copyright (c) 2014-2020 The Rust Project Developers
++
++Permission is hereby granted, free of charge, to any
++person obtaining a copy of this software and associated
++documentation files (the "Software"), to deal in the
++Software without restriction, including without
++limitation the rights to use, copy, modify, merge,
++publish, distribute, sublicense, and/or sell copies of
++the Software, and to permit persons to whom the Software
++is furnished to do so, subject to the following
++conditions:
++
++The above copyright notice and this permission notice
++shall be included in all copies or substantial portions
++of the Software.
++
++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
++ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
++TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
++PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
++SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
++CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
++IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++DEALINGS IN THE SOFTWARE.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..222b81023a7705aa87847ea6fd913d50a380036c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,193 @@@
++# Clippy
++
++[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test%22+event%3Apush+branch%3Aauto)
++[![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](#license)
++
++A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
++
++[There are over 350 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
++
++We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
++
++* `clippy::all` (everything that is on by default: all the categories below except for `nursery`, `pedantic`, and `cargo`)
++* `clippy::correctness` (code that is just **outright wrong** or **very very useless**, causes hard errors by default)
++* `clippy::style` (code that should be written in a more idiomatic way)
++* `clippy::complexity` (code that does something simple but in a complex way)
++* `clippy::perf` (code that can be written in a faster way)
++* `clippy::pedantic` (lints which are rather strict, off by default)
++* `clippy::nursery` (new lints that aren't quite ready yet, off by default)
++* `clippy::cargo` (checks against the cargo manifest, off by default)
++
++More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
++
++Only the following of those categories are enabled by default:
++
++* `clippy::style`
++* `clippy::correctness`
++* `clippy::complexity`
++* `clippy::perf`
++
++Other categories need to be enabled in order for their lints to be executed.
++
++The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also contains "restriction lints", which are
++for things which are usually not considered "bad", but may be useful to turn on in specific cases. These should be used
++very selectively, if at all.
++
++Table of contents:
++
++*   [Usage instructions](#usage)
++*   [Configuration](#configuration)
++*   [Contributing](#contributing)
++*   [License](#license)
++
++## Usage
++
++Since this is a tool for helping the developer of a library or application
++write better code, it is recommended not to include Clippy as a hard dependency.
++Options include using it as an optional dependency, as a cargo subcommand, or
++as an included feature during build. These options are detailed below.
++
++### As a cargo subcommand (`cargo clippy`)
++
++One way to use Clippy is by installing Clippy through rustup as a cargo
++subcommand.
++
++#### Step 1: Install rustup
++
++You can install [rustup](https://rustup.rs/) on supported platforms. This will help
++us install Clippy and its dependencies.
++
++If you already have rustup installed, update to ensure you have the latest
++rustup and compiler:
++
++```terminal
++rustup update
++```
++
++#### Step 2: Install Clippy
++
++Once you have rustup and the latest stable release (at least Rust 1.29) installed, run the following command:
++
++```terminal
++rustup component add clippy
++```
++If it says that it can't find the `clippy` component, please run `rustup self update`.
++
++#### Step 3: Run Clippy
++
++Now you can run Clippy by invoking the following command:
++
++```terminal
++cargo clippy
++```
++
++#### Automatically applying Clippy suggestions
++
++Clippy can automatically apply some lint suggestions.
++Note that this is still experimental and only supported on the nightly channel:
++
++```terminal
++cargo clippy --fix -Z unstable-options
++```
++
++### Running Clippy from the command line without installing it
++
++To have cargo compile your crate with Clippy without Clippy installation
++in your code, you can use:
++
++```terminal
++cargo run --bin cargo-clippy --manifest-path=path_to_clippys_Cargo.toml
++```
++
++*Note:* Be sure that Clippy was compiled with the same version of rustc that cargo invokes here!
++
++### Travis CI
++
++You can add Clippy to Travis CI in the same way you use it locally:
++
++```yml
++language: rust
++rust:
++  - stable
++  - beta
++before_script:
++  - rustup component add clippy
++script:
++  - cargo clippy
++  # if you want the build job to fail when encountering warnings, use
++  - cargo clippy -- -D warnings
++  # in order to also check tests and non-default crate features, use
++  - cargo clippy --all-targets --all-features -- -D warnings
++  - cargo test
++  # etc.
++```
++
++If you are on nightly, It might happen that Clippy is not available for a certain nightly release.
++In this case you can try to conditionally install Clippy from the Git repo.
++
++```yaml
++language: rust
++rust:
++  - nightly
++before_script:
++   - rustup component add clippy --toolchain=nightly || cargo install --git https://github.com/rust-lang/rust-clippy/ --force clippy
++   # etc.
++```
++
++Note that adding `-D warnings` will cause your build to fail if **any** warnings are found in your code.
++That includes warnings found by rustc (e.g. `dead_code`, etc.). If you want to avoid this and only cause
++an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command
++line. (You can swap `clippy::all` with the specific lint category you are targeting.)
++
++## Configuration
++
++Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable =
++value` mapping eg.
++
++```toml
++blacklisted-names = ["toto", "tata", "titi"]
++cognitive-complexity-threshold = 30
++```
++
++See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
++lints can be configured and the meaning of the variables.
++
++To deactivate the “for further information visit *lint-link*” message you can
++define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable.
++
++### Allowing/denying lints
++
++You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
++
++*   the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`)
++
++*   all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`,
++    `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive
++    lints prone to false positives.
++
++*   only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.)
++
++*   `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc.
++
++Note: `deny` produces errors instead of warnings.
++
++If you do not want to include your lint levels in your code, you can globally enable/disable lints by passing extra
++flags to Clippy during the run: `cargo clippy -- -A clippy::lint_name` will run Clippy with `lint_name` disabled and
++`cargo clippy -- -W clippy::lint_name` will run it with that enabled. This also works with lint groups. For example you
++can run Clippy with warnings for all lints enabled: `cargo clippy -- -W clippy::pedantic`
++If you care only about a single lint, you can allow all others and then explicitly reenable
++the lint(s) you are interested in: `cargo clippy -- -Aclippy::all -Wclippy::useless_format -Wclippy::...`
++
++## Contributing
++
++If you want to contribute to Clippy, you can find more information in [CONTRIBUTING.md](https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md).
++
++## License
++
++Copyright 2014-2020 The Rust Project Developers
++
++Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license
++<LICENSE-MIT or [https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT)>, at your
++option. Files in the project may not be
++copied, modified, or distributed except according to those terms.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..018375dbada874db7ddb8fc908222da41b83a1df
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++fn main() {
++    // Forward the profile to the main compilation
++    println!("cargo:rustc-env=PROFILE={}", std::env::var("PROFILE").unwrap());
++    // Don't rebuild even if nothing changed
++    println!("cargo:rerun-if-changed=build.rs");
++    // forward git repo hashes we build at
++    println!(
++        "cargo:rustc-env=GIT_HASH={}",
++        rustc_tools_util::get_commit_hash().unwrap_or_default()
++    );
++    println!(
++        "cargo:rustc-env=COMMIT_DATE={}",
++        rustc_tools_util::get_commit_date().unwrap_or_default()
++    );
++    println!(
++        "cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}",
++        rustc_tools_util::get_channel().unwrap_or_default()
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c861efc8afb5032e42c0a8592a0eb1ff26c5e6a6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++[package]
++name = "clippy_dev"
++version = "0.0.1"
++authors = ["Philipp Hansch <dev@phansch.net>"]
++edition = "2018"
++
++[dependencies]
++bytecount = "0.6"
++clap = "2.33"
++itertools = "0.9"
++regex = "1"
++lazy_static = "1.0"
++shell-escape = "0.1"
++walkdir = "2"
++
++[features]
++deny-warnings = []
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6ae3f58c1f2ada5a24893ee825872c9cfa1d8c2d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,175 @@@
++use crate::clippy_project_root;
++use shell_escape::escape;
++use std::ffi::OsStr;
++use std::io;
++use std::path::Path;
++use std::process::{self, Command};
++use walkdir::WalkDir;
++
++#[derive(Debug)]
++pub enum CliError {
++    CommandFailed(String),
++    IoError(io::Error),
++    RustfmtNotInstalled,
++    WalkDirError(walkdir::Error),
++}
++
++impl From<io::Error> for CliError {
++    fn from(error: io::Error) -> Self {
++        Self::IoError(error)
++    }
++}
++
++impl From<walkdir::Error> for CliError {
++    fn from(error: walkdir::Error) -> Self {
++        Self::WalkDirError(error)
++    }
++}
++
++struct FmtContext {
++    check: bool,
++    verbose: bool,
++}
++
++pub fn run(check: bool, verbose: bool) {
++    fn try_run(context: &FmtContext) -> Result<bool, CliError> {
++        let mut success = true;
++
++        let project_root = clippy_project_root();
++
++        rustfmt_test(context)?;
++
++        success &= cargo_fmt(context, project_root.as_path())?;
++        success &= cargo_fmt(context, &project_root.join("clippy_dev"))?;
++        success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?;
++
++        for entry in WalkDir::new(project_root.join("tests")) {
++            let entry = entry?;
++            let path = entry.path();
++
++            if path.extension() != Some("rs".as_ref())
++                || entry.file_name() == "ice-3891.rs"
++                // Avoid rustfmt bug rust-lang/rustfmt#1873
++                || cfg!(windows) && entry.file_name() == "implicit_hasher.rs"
++            {
++                continue;
++            }
++
++            success &= rustfmt(context, &path)?;
++        }
++
++        Ok(success)
++    }
++
++    fn output_err(err: CliError) {
++        match err {
++            CliError::CommandFailed(command) => {
++                eprintln!("error: A command failed! `{}`", command);
++            },
++            CliError::IoError(err) => {
++                eprintln!("error: {}", err);
++            },
++            CliError::RustfmtNotInstalled => {
++                eprintln!("error: rustfmt nightly is not installed.");
++            },
++            CliError::WalkDirError(err) => {
++                eprintln!("error: {}", err);
++            },
++        }
++    }
++
++    let context = FmtContext { check, verbose };
++    let result = try_run(&context);
++    let code = match result {
++        Ok(true) => 0,
++        Ok(false) => {
++            eprintln!();
++            eprintln!("Formatting check failed.");
++            eprintln!("Run `cargo dev fmt` to update formatting.");
++            1
++        },
++        Err(err) => {
++            output_err(err);
++            1
++        },
++    };
++    process::exit(code);
++}
++
++fn format_command(program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[impl AsRef<OsStr>]) -> String {
++    let arg_display: Vec<_> = args.iter().map(|a| escape(a.as_ref().to_string_lossy())).collect();
++
++    format!(
++        "cd {} && {} {}",
++        escape(dir.as_ref().to_string_lossy()),
++        escape(program.as_ref().to_string_lossy()),
++        arg_display.join(" ")
++    )
++}
++
++fn exec(
++    context: &FmtContext,
++    program: impl AsRef<OsStr>,
++    dir: impl AsRef<Path>,
++    args: &[impl AsRef<OsStr>],
++) -> Result<bool, CliError> {
++    if context.verbose {
++        println!("{}", format_command(&program, &dir, args));
++    }
++
++    let mut child = Command::new(&program).current_dir(&dir).args(args.iter()).spawn()?;
++    let code = child.wait()?;
++    let success = code.success();
++
++    if !context.check && !success {
++        return Err(CliError::CommandFailed(format_command(&program, &dir, args)));
++    }
++
++    Ok(success)
++}
++
++fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
++    let mut args = vec!["+nightly", "fmt", "--all"];
++    if context.check {
++        args.push("--");
++        args.push("--check");
++    }
++    let success = exec(context, "cargo", path, &args)?;
++
++    Ok(success)
++}
++
++fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
++    let program = "rustfmt";
++    let dir = std::env::current_dir()?;
++    let args = &["+nightly", "--version"];
++
++    if context.verbose {
++        println!("{}", format_command(&program, &dir, args));
++    }
++
++    let output = Command::new(&program).current_dir(&dir).args(args.iter()).output()?;
++
++    if output.status.success() {
++        Ok(())
++    } else if std::str::from_utf8(&output.stderr)
++        .unwrap_or("")
++        .starts_with("error: 'rustfmt' is not installed")
++    {
++        Err(CliError::RustfmtNotInstalled)
++    } else {
++        Err(CliError::CommandFailed(format_command(&program, &dir, args)))
++    }
++}
++
++fn rustfmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
++    let mut args = vec!["+nightly".as_ref(), path.as_os_str()];
++    if context.check {
++        args.push("--check".as_ref());
++    }
++    let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?;
++    if !success {
++        eprintln!("rustfmt failed on {}", path.display());
++    }
++    Ok(success)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6fdd282c6849e0a1ec511de5fae54ceb3f343102
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,524 @@@
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++
++use itertools::Itertools;
++use lazy_static::lazy_static;
++use regex::Regex;
++use std::collections::HashMap;
++use std::ffi::OsStr;
++use std::fs;
++use std::path::{Path, PathBuf};
++use walkdir::WalkDir;
++
++pub mod fmt;
++pub mod new_lint;
++pub mod stderr_length_check;
++pub mod update_lints;
++
++lazy_static! {
++    static ref DEC_CLIPPY_LINT_RE: Regex = Regex::new(
++        r#"(?x)
++        declare_clippy_lint!\s*[\{(]
++        (?:\s+///.*)*
++        \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
++        (?P<cat>[a-z_]+)\s*,\s*
++        "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
++    "#
++    )
++    .unwrap();
++    static ref DEC_DEPRECATED_LINT_RE: Regex = Regex::new(
++        r#"(?x)
++        declare_deprecated_lint!\s*[{(]\s*
++        (?:\s+///.*)*
++        \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
++        "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
++    "#
++    )
++    .unwrap();
++    static ref NL_ESCAPE_RE: Regex = Regex::new(r#"\\\n\s*"#).unwrap();
++}
++
++pub static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
++
++/// Lint data parsed from the Clippy source code.
++#[derive(Clone, PartialEq, Debug)]
++pub struct Lint {
++    pub name: String,
++    pub group: String,
++    pub desc: String,
++    pub deprecation: Option<String>,
++    pub module: String,
++}
++
++impl Lint {
++    #[must_use]
++    pub fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self {
++        Self {
++            name: name.to_lowercase(),
++            group: group.to_string(),
++            desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(),
++            deprecation: deprecation.map(ToString::to_string),
++            module: module.to_string(),
++        }
++    }
++
++    /// Returns all non-deprecated lints and non-internal lints
++    #[must_use]
++    pub fn usable_lints(lints: &[Self]) -> Vec<Self> {
++        lints
++            .iter()
++            .filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal"))
++            .cloned()
++            .collect()
++    }
++
++    /// Returns all internal lints (not `internal_warn` lints)
++    #[must_use]
++    pub fn internal_lints(lints: &[Self]) -> Vec<Self> {
++        lints.iter().filter(|l| l.group == "internal").cloned().collect()
++    }
++
++    /// Returns all deprecated lints
++    #[must_use]
++    pub fn deprecated_lints(lints: &[Self]) -> Vec<Self> {
++        lints.iter().filter(|l| l.deprecation.is_some()).cloned().collect()
++    }
++
++    /// Returns the lints in a `HashMap`, grouped by the different lint groups
++    #[must_use]
++    pub fn by_lint_group(lints: impl Iterator<Item = Self>) -> HashMap<String, Vec<Self>> {
++        lints.map(|lint| (lint.group.to_string(), lint)).into_group_map()
++    }
++}
++
++/// Generates the Vec items for `register_lint_group` calls in `clippy_lints/src/lib.rs`.
++#[must_use]
++pub fn gen_lint_group_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
++    lints
++        .map(|l| format!("        LintId::of(&{}::{}),", l.module, l.name.to_uppercase()))
++        .sorted()
++        .collect::<Vec<String>>()
++}
++
++/// Generates the `pub mod module_name` list in `clippy_lints/src/lib.rs`.
++#[must_use]
++pub fn gen_modules_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
++    lints
++        .map(|l| &l.module)
++        .unique()
++        .map(|module| format!("mod {};", module))
++        .sorted()
++        .collect::<Vec<String>>()
++}
++
++/// Generates the list of lint links at the bottom of the README
++#[must_use]
++pub fn gen_changelog_lint_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
++    lints
++        .sorted_by_key(|l| &l.name)
++        .map(|l| format!("[`{}`]: {}#{}", l.name, DOCS_LINK, l.name))
++        .collect()
++}
++
++/// Generates the `register_removed` code in `./clippy_lints/src/lib.rs`.
++#[must_use]
++pub fn gen_deprecated<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
++    lints
++        .flat_map(|l| {
++            l.deprecation
++                .clone()
++                .map(|depr_text| {
++                    vec![
++                        "    store.register_removed(".to_string(),
++                        format!("        \"clippy::{}\",", l.name),
++                        format!("        \"{}\",", depr_text),
++                        "    );".to_string(),
++                    ]
++                })
++                .expect("only deprecated lints should be passed")
++        })
++        .collect::<Vec<String>>()
++}
++
++#[must_use]
++pub fn gen_register_lint_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
++    let pre = "    store.register_lints(&[".to_string();
++    let post = "    ]);".to_string();
++    let mut inner = lints
++        .map(|l| format!("        &{}::{},", l.module, l.name.to_uppercase()))
++        .sorted()
++        .collect::<Vec<String>>();
++    inner.insert(0, pre);
++    inner.push(post);
++    inner
++}
++
++/// Gathers all files in `src/clippy_lints` and gathers all lints inside
++pub fn gather_all() -> impl Iterator<Item = Lint> {
++    lint_files().flat_map(|f| gather_from_file(&f))
++}
++
++fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator<Item = Lint> {
++    let content = fs::read_to_string(dir_entry.path()).unwrap();
++    let path = dir_entry.path();
++    let filename = path.file_stem().unwrap();
++    let path_buf = path.with_file_name(filename);
++    let mut rel_path = path_buf
++        .strip_prefix(clippy_project_root().join("clippy_lints/src"))
++        .expect("only files in `clippy_lints/src` should be looked at");
++    // If the lints are stored in mod.rs, we get the module name from
++    // the containing directory:
++    if filename == "mod" {
++        rel_path = rel_path.parent().unwrap();
++    }
++
++    let module = rel_path
++        .components()
++        .map(|c| c.as_os_str().to_str().unwrap())
++        .collect::<Vec<_>>()
++        .join("::");
++
++    parse_contents(&content, &module)
++}
++
++fn parse_contents(content: &str, module: &str) -> impl Iterator<Item = Lint> {
++    let lints = DEC_CLIPPY_LINT_RE
++        .captures_iter(content)
++        .map(|m| Lint::new(&m["name"], &m["cat"], &m["desc"], None, module));
++    let deprecated = DEC_DEPRECATED_LINT_RE
++        .captures_iter(content)
++        .map(|m| Lint::new(&m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), module));
++    // Removing the `.collect::<Vec<Lint>>().into_iter()` causes some lifetime issues due to the map
++    lints.chain(deprecated).collect::<Vec<Lint>>().into_iter()
++}
++
++/// Collects all .rs files in the `clippy_lints/src` directory
++fn lint_files() -> impl Iterator<Item = walkdir::DirEntry> {
++    // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
++    // Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`.
++    let path = clippy_project_root().join("clippy_lints/src");
++    WalkDir::new(path)
++        .into_iter()
++        .filter_map(Result::ok)
++        .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
++}
++
++/// Whether a file has had its text changed or not
++#[derive(PartialEq, Debug)]
++pub struct FileChange {
++    pub changed: bool,
++    pub new_lines: String,
++}
++
++/// Replaces a region in a file delimited by two lines matching regexes.
++///
++/// `path` is the relative path to the file on which you want to perform the replacement.
++///
++/// See `replace_region_in_text` for documentation of the other options.
++pub fn replace_region_in_file<F>(
++    path: &Path,
++    start: &str,
++    end: &str,
++    replace_start: bool,
++    write_back: bool,
++    replacements: F,
++) -> FileChange
++where
++    F: FnOnce() -> Vec<String>,
++{
++    let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.display(), e));
++    let file_change = replace_region_in_text(&contents, start, end, replace_start, replacements);
++
++    if write_back {
++        if let Err(e) = fs::write(path, file_change.new_lines.as_bytes()) {
++            panic!("Cannot write to {}: {}", path.display(), e);
++        }
++    }
++    file_change
++}
++
++/// Replaces a region in a text delimited by two lines matching regexes.
++///
++/// * `text` is the input text on which you want to perform the replacement
++/// * `start` is a `&str` that describes the delimiter line before the region you want to replace.
++///   As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
++/// * `end` is a `&str` that describes the delimiter line until where the replacement should happen.
++///   As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
++/// * If `replace_start` is true, the `start` delimiter line is replaced as well. The `end`
++///   delimiter line is never replaced.
++/// * `replacements` is a closure that has to return a `Vec<String>` which contains the new text.
++///
++/// If you want to perform the replacement on files instead of already parsed text,
++/// use `replace_region_in_file`.
++///
++/// # Example
++///
++/// ```
++/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end";
++/// let result =
++///     clippy_dev::replace_region_in_text(the_text, "replace_start", "replace_end", false, || {
++///         vec!["a different".to_string(), "text".to_string()]
++///     })
++///     .new_lines;
++/// assert_eq!("replace_start\na different\ntext\nreplace_end", result);
++/// ```
++pub fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange
++where
++    F: FnOnce() -> Vec<String>,
++{
++    let replace_it = replacements();
++    let mut in_old_region = false;
++    let mut found = false;
++    let mut new_lines = vec![];
++    let start = Regex::new(start).unwrap();
++    let end = Regex::new(end).unwrap();
++
++    for line in text.lines() {
++        if in_old_region {
++            if end.is_match(line) {
++                in_old_region = false;
++                new_lines.extend(replace_it.clone());
++                new_lines.push(line.to_string());
++            }
++        } else if start.is_match(line) {
++            if !replace_start {
++                new_lines.push(line.to_string());
++            }
++            in_old_region = true;
++            found = true;
++        } else {
++            new_lines.push(line.to_string());
++        }
++    }
++
++    if !found {
++        // This happens if the provided regex in `clippy_dev/src/main.rs` does not match in the
++        // given text or file. Most likely this is an error on the programmer's side and the Regex
++        // is incorrect.
++        eprintln!("error: regex \n{:?}\ndoesn't match. You may have to update it.", start);
++        std::process::exit(1);
++    }
++
++    let mut new_lines = new_lines.join("\n");
++    if text.ends_with('\n') {
++        new_lines.push('\n');
++    }
++    let changed = new_lines != text;
++    FileChange { changed, new_lines }
++}
++
++/// Returns the path to the Clippy project directory
++#[must_use]
++pub fn clippy_project_root() -> PathBuf {
++    let current_dir = std::env::current_dir().unwrap();
++    for path in current_dir.ancestors() {
++        let result = std::fs::read_to_string(path.join("Cargo.toml"));
++        if let Err(err) = &result {
++            if err.kind() == std::io::ErrorKind::NotFound {
++                continue;
++            }
++        }
++
++        let content = result.unwrap();
++        if content.contains("[package]\nname = \"clippy\"") {
++            return path.to_path_buf();
++        }
++    }
++    panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
++}
++
++#[test]
++fn test_parse_contents() {
++    let result: Vec<Lint> = parse_contents(
++        r#"
++declare_clippy_lint! {
++    pub PTR_ARG,
++    style,
++    "really long \
++     text"
++}
++
++declare_clippy_lint!{
++    pub DOC_MARKDOWN,
++    pedantic,
++    "single line"
++}
++
++/// some doc comment
++declare_deprecated_lint! {
++    pub SHOULD_ASSERT_EQ,
++    "`assert!()` will be more flexible with RFC 2011"
++}
++    "#,
++        "module_name",
++    )
++    .collect();
++
++    let expected = vec![
++        Lint::new("ptr_arg", "style", "really long text", None, "module_name"),
++        Lint::new("doc_markdown", "pedantic", "single line", None, "module_name"),
++        Lint::new(
++            "should_assert_eq",
++            "Deprecated",
++            "`assert!()` will be more flexible with RFC 2011",
++            Some("`assert!()` will be more flexible with RFC 2011"),
++            "module_name",
++        ),
++    ];
++    assert_eq!(expected, result);
++}
++
++#[test]
++fn test_replace_region() {
++    let text = "\nabc\n123\n789\ndef\nghi";
++    let expected = FileChange {
++        changed: true,
++        new_lines: "\nabc\nhello world\ndef\nghi".to_string(),
++    };
++    let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || {
++        vec!["hello world".to_string()]
++    });
++    assert_eq!(expected, result);
++}
++
++#[test]
++fn test_replace_region_with_start() {
++    let text = "\nabc\n123\n789\ndef\nghi";
++    let expected = FileChange {
++        changed: true,
++        new_lines: "\nhello world\ndef\nghi".to_string(),
++    };
++    let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || {
++        vec!["hello world".to_string()]
++    });
++    assert_eq!(expected, result);
++}
++
++#[test]
++fn test_replace_region_no_changes() {
++    let text = "123\n456\n789";
++    let expected = FileChange {
++        changed: false,
++        new_lines: "123\n456\n789".to_string(),
++    };
++    let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, || vec![]);
++    assert_eq!(expected, result);
++}
++
++#[test]
++fn test_usable_lints() {
++    let lints = vec![
++        Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"),
++        Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"),
++        Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"),
++        Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name"),
++    ];
++    let expected = vec![Lint::new(
++        "should_assert_eq2",
++        "Not Deprecated",
++        "abc",
++        None,
++        "module_name",
++    )];
++    assert_eq!(expected, Lint::usable_lints(&lints));
++}
++
++#[test]
++fn test_by_lint_group() {
++    let lints = vec![
++        Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
++        Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
++        Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
++    ];
++    let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
++    expected.insert(
++        "group1".to_string(),
++        vec![
++            Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
++            Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
++        ],
++    );
++    expected.insert(
++        "group2".to_string(),
++        vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")],
++    );
++    assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
++}
++
++#[test]
++fn test_gen_changelog_lint_list() {
++    let lints = vec![
++        Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
++        Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
++    ];
++    let expected = vec![
++        format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK.to_string()),
++        format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK.to_string()),
++    ];
++    assert_eq!(expected, gen_changelog_lint_list(lints.iter()));
++}
++
++#[test]
++fn test_gen_deprecated() {
++    let lints = vec![
++        Lint::new(
++            "should_assert_eq",
++            "group1",
++            "abc",
++            Some("has been superseded by should_assert_eq2"),
++            "module_name",
++        ),
++        Lint::new(
++            "another_deprecated",
++            "group2",
++            "abc",
++            Some("will be removed"),
++            "module_name",
++        ),
++    ];
++    let expected: Vec<String> = vec![
++        "    store.register_removed(",
++        "        \"clippy::should_assert_eq\",",
++        "        \"has been superseded by should_assert_eq2\",",
++        "    );",
++        "    store.register_removed(",
++        "        \"clippy::another_deprecated\",",
++        "        \"will be removed\",",
++        "    );",
++    ]
++    .into_iter()
++    .map(String::from)
++    .collect();
++    assert_eq!(expected, gen_deprecated(lints.iter()));
++}
++
++#[test]
++#[should_panic]
++fn test_gen_deprecated_fail() {
++    let lints = vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")];
++    let _ = gen_deprecated(lints.iter());
++}
++
++#[test]
++fn test_gen_modules_list() {
++    let lints = vec![
++        Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
++        Lint::new("incorrect_stuff", "group3", "abc", None, "another_module"),
++    ];
++    let expected = vec!["mod another_module;".to_string(), "mod module_name;".to_string()];
++    assert_eq!(expected, gen_modules_list(lints.iter()));
++}
++
++#[test]
++fn test_gen_lint_group_list() {
++    let lints = vec![
++        Lint::new("abc", "group1", "abc", None, "module_name"),
++        Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
++        Lint::new("internal", "internal_style", "abc", None, "module_name"),
++    ];
++    let expected = vec![
++        "        LintId::of(&module_name::ABC),".to_string(),
++        "        LintId::of(&module_name::INTERNAL),".to_string(),
++        "        LintId::of(&module_name::SHOULD_ASSERT_EQ),".to_string(),
++    ];
++    assert_eq!(expected, gen_lint_group_list(lints.iter()));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d99235f7c07a7cf649498f5266468fdd89b49cfb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,120 @@@
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++
++use clap::{App, Arg, SubCommand};
++use clippy_dev::{fmt, new_lint, stderr_length_check, update_lints};
++
++fn main() {
++    let matches = App::new("Clippy developer tooling")
++        .subcommand(
++            SubCommand::with_name("fmt")
++                .about("Run rustfmt on all projects and tests")
++                .arg(
++                    Arg::with_name("check")
++                        .long("check")
++                        .help("Use the rustfmt --check option"),
++                )
++                .arg(
++                    Arg::with_name("verbose")
++                        .short("v")
++                        .long("verbose")
++                        .help("Echo commands run"),
++                ),
++        )
++        .subcommand(
++            SubCommand::with_name("update_lints")
++                .about("Updates lint registration and information from the source code")
++                .long_about(
++                    "Makes sure that:\n \
++                     * the lint count in README.md is correct\n \
++                     * the changelog contains markdown link references at the bottom\n \
++                     * all lint groups include the correct lints\n \
++                     * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \
++                     * all lints are registered in the lint store",
++                )
++                .arg(Arg::with_name("print-only").long("print-only").help(
++                    "Print a table of lints to STDOUT. \
++                     This does not include deprecated and internal lints. \
++                     (Does not modify any files)",
++                ))
++                .arg(
++                    Arg::with_name("check")
++                        .long("check")
++                        .help("Checks that `cargo dev update_lints` has been run. Used on CI."),
++                ),
++        )
++        .subcommand(
++            SubCommand::with_name("new_lint")
++                .about("Create new lint and run `cargo dev update_lints`")
++                .arg(
++                    Arg::with_name("pass")
++                        .short("p")
++                        .long("pass")
++                        .help("Specify whether the lint runs during the early or late pass")
++                        .takes_value(true)
++                        .possible_values(&["early", "late"])
++                        .required(true),
++                )
++                .arg(
++                    Arg::with_name("name")
++                        .short("n")
++                        .long("name")
++                        .help("Name of the new lint in snake case, ex: fn_too_long")
++                        .takes_value(true)
++                        .required(true),
++                )
++                .arg(
++                    Arg::with_name("category")
++                        .short("c")
++                        .long("category")
++                        .help("What category the lint belongs to")
++                        .default_value("nursery")
++                        .possible_values(&[
++                            "style",
++                            "correctness",
++                            "complexity",
++                            "perf",
++                            "pedantic",
++                            "restriction",
++                            "cargo",
++                            "nursery",
++                            "internal",
++                            "internal_warn",
++                        ])
++                        .takes_value(true),
++                ),
++        )
++        .subcommand(
++            SubCommand::with_name("limit_stderr_length")
++                .about("Ensures that stderr files do not grow longer than a certain amount of lines."),
++        )
++        .get_matches();
++
++    match matches.subcommand() {
++        ("fmt", Some(matches)) => {
++            fmt::run(matches.is_present("check"), matches.is_present("verbose"));
++        },
++        ("update_lints", Some(matches)) => {
++            if matches.is_present("print-only") {
++                update_lints::print_lints();
++            } else if matches.is_present("check") {
++                update_lints::run(update_lints::UpdateMode::Check);
++            } else {
++                update_lints::run(update_lints::UpdateMode::Change);
++            }
++        },
++        ("new_lint", Some(matches)) => {
++            match new_lint::create(
++                matches.value_of("pass"),
++                matches.value_of("name"),
++                matches.value_of("category"),
++            ) {
++                Ok(_) => update_lints::run(update_lints::UpdateMode::Change),
++                Err(e) => eprintln!("Unable to create lint: {}", e),
++            }
++        },
++        ("limit_stderr_length", _) => {
++            stderr_length_check::check();
++        },
++        _ => {},
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..44b2a5383d2113f367c1a6262e351b236ea33e16
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,177 @@@
++use crate::clippy_project_root;
++use std::fs::{File, OpenOptions};
++use std::io;
++use std::io::prelude::*;
++use std::io::ErrorKind;
++use std::path::Path;
++
++/// Creates files required to implement and test a new lint and runs `update_lints`.
++///
++/// # Errors
++///
++/// This function errors, if the files couldn't be created
++pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>) -> Result<(), io::Error> {
++    let pass = pass.expect("`pass` argument is validated by clap");
++    let lint_name = lint_name.expect("`name` argument is validated by clap");
++    let category = category.expect("`category` argument is validated by clap");
++
++    match open_files(lint_name) {
++        Ok((mut test_file, mut lint_file)) => {
++            let (pass_type, pass_lifetimes, pass_import, context_import) = match pass {
++                "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
++                "late" => ("LateLintPass", "<'_, '_>", "use rustc_hir::*;", "LateContext"),
++                _ => {
++                    unreachable!("`pass_type` should only ever be `early` or `late`!");
++                },
++            };
++
++            let camel_case_name = to_camel_case(lint_name);
++
++            if let Err(e) = test_file.write_all(get_test_file_contents(lint_name).as_bytes()) {
++                return Err(io::Error::new(
++                    ErrorKind::Other,
++                    format!("Could not write to test file: {}", e),
++                ));
++            };
++
++            if let Err(e) = lint_file.write_all(
++                get_lint_file_contents(
++                    pass_type,
++                    pass_lifetimes,
++                    lint_name,
++                    &camel_case_name,
++                    category,
++                    pass_import,
++                    context_import,
++                )
++                .as_bytes(),
++            ) {
++                return Err(io::Error::new(
++                    ErrorKind::Other,
++                    format!("Could not write to lint file: {}", e),
++                ));
++            }
++            Ok(())
++        },
++        Err(e) => Err(io::Error::new(
++            ErrorKind::Other,
++            format!("Unable to create lint: {}", e),
++        )),
++    }
++}
++
++fn open_files(lint_name: &str) -> Result<(File, File), io::Error> {
++    let project_root = clippy_project_root();
++
++    let test_file_path = project_root.join("tests").join("ui").join(format!("{}.rs", lint_name));
++    let lint_file_path = project_root
++        .join("clippy_lints")
++        .join("src")
++        .join(format!("{}.rs", lint_name));
++
++    if Path::new(&test_file_path).exists() {
++        return Err(io::Error::new(
++            ErrorKind::AlreadyExists,
++            format!("test file {:?} already exists", test_file_path),
++        ));
++    }
++    if Path::new(&lint_file_path).exists() {
++        return Err(io::Error::new(
++            ErrorKind::AlreadyExists,
++            format!("lint file {:?} already exists", lint_file_path),
++        ));
++    }
++
++    let test_file = OpenOptions::new().write(true).create_new(true).open(test_file_path)?;
++    let lint_file = OpenOptions::new().write(true).create_new(true).open(lint_file_path)?;
++
++    Ok((test_file, lint_file))
++}
++
++fn to_camel_case(name: &str) -> String {
++    name.split('_')
++        .map(|s| {
++            if s.is_empty() {
++                String::from("")
++            } else {
++                [&s[0..1].to_uppercase(), &s[1..]].concat()
++            }
++        })
++        .collect()
++}
++
++fn get_test_file_contents(lint_name: &str) -> String {
++    format!(
++        "#![warn(clippy::{})]
++
++fn main() {{
++    // test code goes here
++}}
++",
++        lint_name
++    )
++}
++
++fn get_lint_file_contents(
++    pass_type: &str,
++    pass_lifetimes: &str,
++    lint_name: &str,
++    camel_case_name: &str,
++    category: &str,
++    pass_import: &str,
++    context_import: &str,
++) -> String {
++    format!(
++        "use rustc_lint::{{{type}, {context_import}}};
++use rustc_session::{{declare_lint_pass, declare_tool_lint}};
++{pass_import}
++
++declare_clippy_lint! {{
++    /// **What it does:**
++    ///
++    /// **Why is this bad?**
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// // example code where clippy issues a warning
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// // example code which does not raise clippy warning
++    /// ```
++    pub {name_upper},
++    {category},
++    \"default lint description\"
++}}
++
++declare_lint_pass!({name_camel} => [{name_upper}]);
++
++impl {type}{lifetimes} for {name_camel} {{}}
++",
++        type=pass_type,
++        lifetimes=pass_lifetimes,
++        name_upper=lint_name.to_uppercase(),
++        name_camel=camel_case_name,
++        category=category,
++        pass_import=pass_import,
++        context_import=context_import
++    )
++}
++
++#[test]
++fn test_camel_case() {
++    let s = "a_lint";
++    let s2 = to_camel_case(s);
++    assert_eq!(s2, "ALint");
++
++    let name = "a_really_long_new_lint";
++    let name2 = to_camel_case(name);
++    assert_eq!(name2, "AReallyLongNewLint");
++
++    let name3 = "lint__name";
++    let name4 = to_camel_case(name3);
++    assert_eq!(name4, "LintName");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e02b6f7da5f7b66e7653403d2f823984eb000ce4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++use crate::clippy_project_root;
++use std::ffi::OsStr;
++use std::fs;
++use std::path::{Path, PathBuf};
++use walkdir::WalkDir;
++
++// The maximum length allowed for stderr files.
++//
++// We limit this because small files are easier to deal with than bigger files.
++const LENGTH_LIMIT: usize = 200;
++
++pub fn check() {
++    let exceeding_files: Vec<_> = exceeding_stderr_files();
++
++    if !exceeding_files.is_empty() {
++        eprintln!("Error: stderr files exceeding limit of {} lines:", LENGTH_LIMIT);
++        for (path, count) in exceeding_files {
++            println!("{}: {}", path.display(), count);
++        }
++        std::process::exit(1);
++    }
++}
++
++fn exceeding_stderr_files() -> Vec<(PathBuf, usize)> {
++    // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
++    WalkDir::new(clippy_project_root().join("tests/ui"))
++        .into_iter()
++        .filter_map(Result::ok)
++        .filter(|f| !f.file_type().is_dir())
++        .filter_map(|e| {
++            let p = e.into_path();
++            let count = count_linenumbers(&p);
++            if p.extension() == Some(OsStr::new("stderr")) && count > LENGTH_LIMIT {
++                Some((p, count))
++            } else {
++                None
++            }
++        })
++        .collect()
++}
++
++#[must_use]
++fn count_linenumbers(filepath: &Path) -> usize {
++    match fs::read(filepath) {
++        Ok(content) => bytecount::count(&content, b'\n'),
++        Err(e) => {
++            eprintln!("Failed to read file: {}", e);
++            0
++        },
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a9a7092994269b7cc84075756d74f0eda57a3af0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,162 @@@
++use crate::{
++    gather_all, gen_changelog_lint_list, gen_deprecated, gen_lint_group_list, gen_modules_list, gen_register_lint_list,
++    replace_region_in_file, Lint, DOCS_LINK,
++};
++use std::path::Path;
++
++#[derive(Clone, Copy, PartialEq)]
++pub enum UpdateMode {
++    Check,
++    Change,
++}
++
++#[allow(clippy::too_many_lines)]
++pub fn run(update_mode: UpdateMode) {
++    let lint_list: Vec<Lint> = gather_all().collect();
++
++    let internal_lints = Lint::internal_lints(&lint_list);
++    let deprecated_lints = Lint::deprecated_lints(&lint_list);
++    let usable_lints = Lint::usable_lints(&lint_list);
++    let mut sorted_usable_lints = usable_lints.clone();
++    sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
++
++    let usable_lint_count = round_to_fifty(usable_lints.len());
++
++    let mut file_change = replace_region_in_file(
++        Path::new("src/lintlist/mod.rs"),
++        "begin lint list",
++        "end lint list",
++        false,
++        update_mode == UpdateMode::Change,
++        || {
++            format!("pub static ref ALL_LINTS: Vec<Lint> = vec!{:#?};", sorted_usable_lints)
++                .lines()
++                .map(ToString::to_string)
++                .collect::<Vec<_>>()
++        },
++    )
++    .changed;
++
++    file_change |= replace_region_in_file(
++        Path::new("README.md"),
++        &format!(
++            r#"\[There are over \d+ lints included in this crate!\]\({}\)"#,
++            DOCS_LINK
++        ),
++        "",
++        true,
++        update_mode == UpdateMode::Change,
++        || {
++            vec![format!(
++                "[There are over {} lints included in this crate!]({})",
++                usable_lint_count, DOCS_LINK
++            )]
++        },
++    )
++    .changed;
++
++    file_change |= replace_region_in_file(
++        Path::new("CHANGELOG.md"),
++        "<!-- begin autogenerated links to lint list -->",
++        "<!-- end autogenerated links to lint list -->",
++        false,
++        update_mode == UpdateMode::Change,
++        || gen_changelog_lint_list(usable_lints.iter().chain(deprecated_lints.iter())),
++    )
++    .changed;
++
++    file_change |= replace_region_in_file(
++        Path::new("clippy_lints/src/lib.rs"),
++        "begin deprecated lints",
++        "end deprecated lints",
++        false,
++        update_mode == UpdateMode::Change,
++        || gen_deprecated(deprecated_lints.iter()),
++    )
++    .changed;
++
++    file_change |= replace_region_in_file(
++        Path::new("clippy_lints/src/lib.rs"),
++        "begin register lints",
++        "end register lints",
++        false,
++        update_mode == UpdateMode::Change,
++        || gen_register_lint_list(usable_lints.iter().chain(internal_lints.iter())),
++    )
++    .changed;
++
++    file_change |= replace_region_in_file(
++        Path::new("clippy_lints/src/lib.rs"),
++        "begin lints modules",
++        "end lints modules",
++        false,
++        update_mode == UpdateMode::Change,
++        || gen_modules_list(usable_lints.iter()),
++    )
++    .changed;
++
++    // Generate lists of lints in the clippy::all lint group
++    file_change |= replace_region_in_file(
++        Path::new("clippy_lints/src/lib.rs"),
++        r#"store.register_group\(true, "clippy::all""#,
++        r#"\]\);"#,
++        false,
++        update_mode == UpdateMode::Change,
++        || {
++            // clippy::all should only include the following lint groups:
++            let all_group_lints = usable_lints.iter().filter(|l| {
++                l.group == "correctness" || l.group == "style" || l.group == "complexity" || l.group == "perf"
++            });
++
++            gen_lint_group_list(all_group_lints)
++        },
++    )
++    .changed;
++
++    // Generate the list of lints for all other lint groups
++    for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) {
++        file_change |= replace_region_in_file(
++            Path::new("clippy_lints/src/lib.rs"),
++            &format!("store.register_group\\(true, \"clippy::{}\"", lint_group),
++            r#"\]\);"#,
++            false,
++            update_mode == UpdateMode::Change,
++            || gen_lint_group_list(lints.iter()),
++        )
++        .changed;
++    }
++
++    if update_mode == UpdateMode::Check && file_change {
++        println!(
++            "Not all lints defined properly. \
++             Please run `cargo dev update_lints` to make sure all lints are defined properly."
++        );
++        std::process::exit(1);
++    }
++}
++
++pub fn print_lints() {
++    let lint_list: Vec<Lint> = gather_all().collect();
++    let usable_lints = Lint::usable_lints(&lint_list);
++    let usable_lint_count = usable_lints.len();
++    let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
++
++    for (lint_group, mut lints) in grouped_by_lint_group {
++        if lint_group == "Deprecated" {
++            continue;
++        }
++        println!("\n## {}", lint_group);
++
++        lints.sort_by_key(|l| l.name.clone());
++
++        for lint in lints {
++            println!("* [{}]({}#{}) ({})", lint.name, DOCS_LINK, lint.name, lint.desc);
++        }
++    }
++
++    println!("there are {} lints", usable_lint_count);
++}
++
++fn round_to_fifty(count: usize) -> usize {
++    count / 50 * 50
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7b11795fafdc511e6e79e6cbffb9e155bea5901f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++[package]
++name = "clippy_dummy" # rename to clippy before publishing
++version = "0.0.303"
++authors = ["Manish Goregaokar <manishsmail@gmail.com>"]
++edition = "2018"
++readme = "crates-readme.md"
++description = "A bunch of helpful lints to avoid common pitfalls in Rust."
++build = 'build.rs'
++
++repository = "https://github.com/rust-lang/rust-clippy"
++
++license = "MIT OR Apache-2.0"
++keywords = ["clippy", "lint", "plugin"]
++categories = ["development-tools", "development-tools::cargo-plugins"]
++
++[build-dependencies]
++term = "0.6"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8e420ec959a26a85f79d186b922ef698333d3bdf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++This is a dummy crate to publish to crates.io. It primarily exists to ensure
++that folks trying to install clippy from crates.io get redirected to the
++`rustup` technique.
++
++Before publishing, be sure to rename `clippy_dummy` to `clippy` in `Cargo.toml`,
++it has a different name to avoid workspace issues.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..21af4f8244f44f93d5d2b73c5cde5af59162c8aa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++use term::color::{GREEN, RED, WHITE};
++use term::{Attr, Error, Result};
++
++fn main() {
++    if foo().is_err() {
++        eprintln!(
++            "error: Clippy is no longer available via crates.io\n\n\
++             help: please run `rustup component add clippy` instead"
++        );
++    }
++    std::process::exit(1);
++}
++
++fn foo() -> Result<()> {
++    let mut t = term::stderr().ok_or(Error::NotSupported)?;
++
++    t.attr(Attr::Bold)?;
++    t.fg(RED)?;
++    write!(t, "\nerror: ")?;
++
++    t.reset()?;
++    t.fg(WHITE)?;
++    writeln!(t, "Clippy is no longer available via crates.io\n")?;
++
++    t.attr(Attr::Bold)?;
++    t.fg(GREEN)?;
++    write!(t, "help: ")?;
++
++    t.reset()?;
++    t.fg(WHITE)?;
++    write!(t, "please run `")?;
++
++    t.attr(Attr::Bold)?;
++    write!(t, "rustup component add clippy")?;
++
++    t.reset()?;
++    t.fg(WHITE)?;
++    writeln!(t, "` instead")?;
++
++    t.reset()?;
++    Ok(())
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0decae8b9103d680516a611f740b5d691ad7abba
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++Installing clippy via crates.io is deprecated. Please use the following:
++
++```terminal
++rustup component add clippy
++```
++
++on a Rust version 1.29 or later. You may need to run `rustup self update` if it complains about a missing clippy binary.
++
++See [the homepage](https://github.com/rust-lang/rust-clippy/#clippy) for more information
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a118834f1fd47cb1b44ebac6fcdea1456e76b615
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++fn main() {
++    panic!("This shouldn't even compile")
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1c0be727834625b54970c77b3209613bcf26c3e5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++[package]
++name = "clippy_lints"
++# begin automatic update
++version = "0.0.212"
++# end automatic update
++authors = [
++      "Manish Goregaokar <manishsmail@gmail.com>",
++      "Andre Bogus <bogusandre@gmail.com>",
++      "Georg Brandl <georg@python.org>",
++      "Martin Carton <cartonmartin@gmail.com>"
++]
++description = "A bunch of helpful lints to avoid common pitfalls in Rust"
++repository = "https://github.com/rust-lang/rust-clippy"
++readme = "README.md"
++license = "MIT OR Apache-2.0"
++keywords = ["clippy", "lint", "plugin"]
++edition = "2018"
++
++[dependencies]
++cargo_metadata = "0.9.0"
++if_chain = "1.0.0"
++itertools = "0.9"
++lazy_static = "1.0.2"
++pulldown-cmark = { version = "0.7", 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.9.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"] }
++
++[features]
++deny-warnings = []
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..513583b7e349ee689d0a4fa130635cf1ee3a214a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++This crate contains Clippy lints. For the main crate, check [GitHub](https://github.com/rust-lang/rust-clippy).
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7e6e2c7eaebeaa8cc7bf2bdccdec8accbca3be88
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,117 @@@
++use crate::utils::span_lint;
++use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::symbol;
++use std::f64::consts as f64;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for floating point literals that approximate
++    /// constants which are defined in
++    /// [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)
++    /// or
++    /// [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),
++    /// respectively, suggesting to use the predefined constant.
++    ///
++    /// **Why is this bad?** Usually, the definition in the standard library is more
++    /// precise than what people come up with. If you find that your definition is
++    /// actually more precise, please [file a Rust
++    /// issue](https://github.com/rust-lang/rust/issues).
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x = 3.14;
++    /// let y = 1_f64 / x;
++    /// ```
++    /// Use predefined constants instead:
++    /// ```rust
++    /// let x = std::f32::consts::PI;
++    /// let y = std::f64::consts::FRAC_1_PI;
++    /// ```
++    pub APPROX_CONSTANT,
++    correctness,
++    "the approximate of a known float constant (in `std::fXX::consts`)"
++}
++
++// Tuples are of the form (constant, name, min_digits)
++const KNOWN_CONSTS: [(f64, &str, usize); 18] = [
++    (f64::E, "E", 4),
++    (f64::FRAC_1_PI, "FRAC_1_PI", 4),
++    (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5),
++    (f64::FRAC_2_PI, "FRAC_2_PI", 5),
++    (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5),
++    (f64::FRAC_PI_2, "FRAC_PI_2", 5),
++    (f64::FRAC_PI_3, "FRAC_PI_3", 5),
++    (f64::FRAC_PI_4, "FRAC_PI_4", 5),
++    (f64::FRAC_PI_6, "FRAC_PI_6", 5),
++    (f64::FRAC_PI_8, "FRAC_PI_8", 5),
++    (f64::LN_10, "LN_10", 5),
++    (f64::LN_2, "LN_2", 5),
++    (f64::LOG10_E, "LOG10_E", 5),
++    (f64::LOG2_E, "LOG2_E", 5),
++    (f64::LOG2_10, "LOG2_10", 5),
++    (f64::LOG10_2, "LOG10_2", 5),
++    (f64::PI, "PI", 3),
++    (f64::SQRT_2, "SQRT_2", 5),
++];
++
++declare_lint_pass!(ApproxConstant => [APPROX_CONSTANT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ApproxConstant {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        if let ExprKind::Lit(lit) = &e.kind {
++            check_lit(cx, &lit.node, e);
++        }
++    }
++}
++
++fn check_lit(cx: &LateContext<'_, '_>, lit: &LitKind, e: &Expr<'_>) {
++    match *lit {
++        LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty {
++            FloatTy::F32 => check_known_consts(cx, e, s, "f32"),
++            FloatTy::F64 => check_known_consts(cx, e, s, "f64"),
++        },
++        LitKind::Float(s, LitFloatType::Unsuffixed) => check_known_consts(cx, e, s, "f{32, 64}"),
++        _ => (),
++    }
++}
++
++fn check_known_consts(cx: &LateContext<'_, '_>, e: &Expr<'_>, s: symbol::Symbol, module: &str) {
++    let s = s.as_str();
++    if s.parse::<f64>().is_ok() {
++        for &(constant, name, min_digits) in &KNOWN_CONSTS {
++            if is_approx_const(constant, &s, min_digits) {
++                span_lint(
++                    cx,
++                    APPROX_CONSTANT,
++                    e.span,
++                    &format!(
++                        "approximate value of `{}::consts::{}` found. \
++                         Consider using it directly",
++                        module, &name
++                    ),
++                );
++                return;
++            }
++        }
++    }
++}
++
++/// Returns `false` if the number of significant figures in `value` are
++/// less than `min_digits`; otherwise, returns true if `value` is equal
++/// to `constant`, rounded to the number of digits present in `value`.
++#[must_use]
++fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool {
++    if value.len() <= min_digits {
++        false
++    } else if constant.to_string().starts_with(value) {
++        // The value is a truncated constant
++        true
++    } else {
++        let round_const = format!("{:.*}", value.len() - 2, constant);
++        value == round_const
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6cbe10a5352d1d1cf3b91caccfd3c59f8408c2da
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,149 @@@
++use crate::consts::constant_simple;
++use crate::utils::span_lint;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for integer arithmetic operations which could overflow or panic.
++    ///
++    /// Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable
++    /// of overflowing according to the [Rust
++    /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
++    /// or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is
++    /// attempted.
++    ///
++    /// **Why is this bad?** Integer overflow will trigger a panic in debug builds or will wrap in
++    /// release mode. Division by zero will cause a panic in either mode. In some applications one
++    /// wants explicitly checked, wrapping or saturating arithmetic.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let a = 0;
++    /// a + 1;
++    /// ```
++    pub INTEGER_ARITHMETIC,
++    restriction,
++    "any integer arithmetic expression which could overflow or panic"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for float arithmetic.
++    ///
++    /// **Why is this bad?** For some embedded systems or kernel development, it
++    /// can be useful to rule out floating-point numbers.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let a = 0.0;
++    /// a + 1.0;
++    /// ```
++    pub FLOAT_ARITHMETIC,
++    restriction,
++    "any floating-point arithmetic statement"
++}
++
++#[derive(Copy, Clone, Default)]
++pub struct Arithmetic {
++    expr_span: Option<Span>,
++    /// This field is used to check whether expressions are constants, such as in enum discriminants
++    /// and consts
++    const_span: Option<Span>,
++}
++
++impl_lint_pass!(Arithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if self.expr_span.is_some() {
++            return;
++        }
++
++        if let Some(span) = self.const_span {
++            if span.contains(expr.span) {
++                return;
++            }
++        }
++        match &expr.kind {
++            hir::ExprKind::Binary(op, l, r) | hir::ExprKind::AssignOp(op, l, r) => {
++                match op.node {
++                    hir::BinOpKind::And
++                    | hir::BinOpKind::Or
++                    | hir::BinOpKind::BitAnd
++                    | hir::BinOpKind::BitOr
++                    | hir::BinOpKind::BitXor
++                    | hir::BinOpKind::Eq
++                    | hir::BinOpKind::Lt
++                    | hir::BinOpKind::Le
++                    | hir::BinOpKind::Ne
++                    | hir::BinOpKind::Ge
++                    | hir::BinOpKind::Gt => return,
++                    _ => (),
++                }
++
++                let (l_ty, r_ty) = (cx.tables.expr_ty(l), cx.tables.expr_ty(r));
++                if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() {
++                    span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
++                    self.expr_span = Some(expr.span);
++                } else if l_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() {
++                    span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
++                    self.expr_span = Some(expr.span);
++                }
++            },
++            hir::ExprKind::Unary(hir::UnOp::UnNeg, arg) => {
++                let ty = cx.tables.expr_ty(arg);
++                if constant_simple(cx, cx.tables, expr).is_none() {
++                    if ty.is_integral() {
++                        span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
++                        self.expr_span = Some(expr.span);
++                    } else if ty.is_floating_point() {
++                        span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
++                        self.expr_span = Some(expr.span);
++                    }
++                }
++            },
++            _ => (),
++        }
++    }
++
++    fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if Some(expr.span) == self.expr_span {
++            self.expr_span = None;
++        }
++    }
++
++    fn check_body(&mut self, cx: &LateContext<'_, '_>, body: &hir::Body<'_>) {
++        let body_owner = cx.tcx.hir().body_owner(body.id());
++
++        match cx.tcx.hir().body_owner_kind(body_owner) {
++            hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => {
++                let body_span = cx.tcx.hir().span(body_owner);
++
++                if let Some(span) = self.const_span {
++                    if span.contains(body_span) {
++                        return;
++                    }
++                }
++                self.const_span = Some(body_span);
++            },
++            hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => (),
++        }
++    }
++
++    fn check_body_post(&mut self, cx: &LateContext<'_, '_>, body: &hir::Body<'_>) {
++        let body_owner = cx.tcx.hir().body_owner(body.id());
++        let body_span = cx.tcx.hir().span(body_owner);
++
++        if let Some(span) = self.const_span {
++            if span.contains(body_span) {
++                return;
++            }
++        }
++        self.const_span = None;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0c8efd755146ec2f03426b0b2f0d8c1950d6e957
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++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.
++    ///
++    /// **Why is this bad?** `as` conversions will perform many kinds of
++    /// conversions, including silently lossy conversions and dangerous coercions.
++    /// There are cases when it makes sense to use `as`, so the lint is
++    /// Allow by default.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// let a: u32;
++    /// ...
++    /// f(a as u16);
++    /// ```
++    ///
++    /// Usually better represents the semantics you expect:
++    /// ```rust,ignore
++    /// f(a.try_into()?);
++    /// ```
++    /// or
++    /// ```rust,ignore
++    /// f(a.try_into().expect("Unexpected u16 overflow in f"));
++    /// ```
++    ///
++    pub AS_CONVERSIONS,
++    restriction,
++    "using a potentially dangerous silent `as` conversion"
++}
++
++declare_lint_pass!(AsConversions => [AS_CONVERSIONS]);
++
++impl EarlyLintPass for AsConversions {
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++        if in_external_macro(cx.sess(), expr.span) {
++            return;
++        }
++
++        if let ExprKind::Cast(_, _) = expr.kind {
++            span_lint_and_help(
++                cx,
++                AS_CONVERSIONS,
++                expr.span,
++                "using a potentially dangerous silent `as` conversion",
++                None,
++                "consider using a safe wrapper for this conversion",
++            );
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f8a8fdcd3aa35e9753f3b8cfa79c069b2df403c6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,152 @@@
++use crate::consts::{constant, Constant};
++use crate::utils::paths;
++use crate::utils::{is_direct_expn_of, is_expn_of, match_function_call, snippet_opt, span_lint_and_help};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_hir::{Expr, ExprKind, PatKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `assert!(true)` and `assert!(false)` calls.
++    ///
++    /// **Why is this bad?** Will be optimized out by the compiler or should probably be replaced by a
++    /// `panic!()` or `unreachable!()`
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// assert!(false)
++    /// assert!(true)
++    /// const B: bool = false;
++    /// assert!(B)
++    /// ```
++    pub ASSERTIONS_ON_CONSTANTS,
++    style,
++    "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`"
++}
++
++declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssertionsOnConstants {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        let lint_true = |is_debug: bool| {
++            span_lint_and_help(
++                cx,
++                ASSERTIONS_ON_CONSTANTS,
++                e.span,
++                if is_debug {
++                    "`debug_assert!(true)` will be optimized out by the compiler"
++                } else {
++                    "`assert!(true)` will be optimized out by the compiler"
++                },
++                None,
++                "remove it",
++            );
++        };
++        let lint_false_without_message = || {
++            span_lint_and_help(
++                cx,
++                ASSERTIONS_ON_CONSTANTS,
++                e.span,
++                "`assert!(false)` should probably be replaced",
++                None,
++                "use `panic!()` or `unreachable!()`",
++            );
++        };
++        let lint_false_with_message = |panic_message: String| {
++            span_lint_and_help(
++                cx,
++                ASSERTIONS_ON_CONSTANTS,
++                e.span,
++                &format!("`assert!(false, {})` should probably be replaced", panic_message),
++                None,
++                &format!("use `panic!({})` or `unreachable!({})`", panic_message, panic_message),
++            )
++        };
++
++        if let Some(debug_assert_span) = is_expn_of(e.span, "debug_assert") {
++            if debug_assert_span.from_expansion() {
++                return;
++            }
++            if_chain! {
++                if let ExprKind::Unary(_, ref lit) = e.kind;
++                if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.tables, lit);
++                if is_true;
++                then {
++                    lint_true(true);
++                }
++            };
++        } else if let Some(assert_span) = is_direct_expn_of(e.span, "assert") {
++            if assert_span.from_expansion() {
++                return;
++            }
++            if let Some(assert_match) = match_assert_with_message(&cx, e) {
++                match assert_match {
++                    // matched assert but not message
++                    AssertKind::WithoutMessage(false) => lint_false_without_message(),
++                    AssertKind::WithoutMessage(true) | AssertKind::WithMessage(_, true) => lint_true(false),
++                    AssertKind::WithMessage(panic_message, false) => lint_false_with_message(panic_message),
++                };
++            }
++        }
++    }
++}
++
++/// Result of calling `match_assert_with_message`.
++enum AssertKind {
++    WithMessage(String, bool),
++    WithoutMessage(bool),
++}
++
++/// Check if the expression matches
++///
++/// ```rust,ignore
++/// match { let _t = !c; _t } {
++///     true => {
++///         {
++///             ::std::rt::begin_panic(message, _)
++///         }
++///     }
++///     _ => { }
++/// };
++/// ```
++///
++/// where `message` is any expression and `c` is a constant bool.
++fn match_assert_with_message<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> Option<AssertKind> {
++    if_chain! {
++        if let ExprKind::Match(ref expr, ref arms, _) = expr.kind;
++        // matches { let _t = expr; _t }
++        if let ExprKind::DropTemps(ref expr) = expr.kind;
++        if let ExprKind::Unary(UnOp::UnNot, ref expr) = expr.kind;
++        // bind the first argument of the `assert!` macro
++        if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.tables, expr);
++        // arm 1 pattern
++        if let PatKind::Lit(ref lit_expr) = arms[0].pat.kind;
++        if let ExprKind::Lit(ref lit) = lit_expr.kind;
++        if let LitKind::Bool(true) = lit.node;
++        // arm 1 block
++        if let ExprKind::Block(ref block, _) = arms[0].body.kind;
++        if block.stmts.is_empty();
++        if let Some(block_expr) = &block.expr;
++        if let ExprKind::Block(ref inner_block, _) = block_expr.kind;
++        if let Some(begin_panic_call) = &inner_block.expr;
++        // function call
++        if let Some(args) = match_function_call(cx, begin_panic_call, &paths::BEGIN_PANIC);
++        if args.len() == 1;
++        // bind the second argument of the `assert!` macro if it exists
++        if let panic_message = snippet_opt(cx, args[0].span);
++        // second argument of begin_panic is irrelevant
++        // as is the second match arm
++        then {
++            // an empty message occurs when it was generated by the macro
++            // (and not passed by the user)
++            return panic_message
++                .filter(|msg| !msg.is_empty())
++                .map(|msg| AssertKind::WithMessage(msg, is_true))
++                .or(Some(AssertKind::WithoutMessage(is_true)));
++        }
++    }
++    None
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..05e2650d0b715b06cc2aa67dcb4c8d0db0c187f6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,261 @@@
++use crate::utils::{
++    get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, SpanlessEq,
++};
++use crate::utils::{higher, sugg};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `a = a op b` or `a = b commutative_op a`
++    /// patterns.
++    ///
++    /// **Why is this bad?** These can be written as the shorter `a op= b`.
++    ///
++    /// **Known problems:** While forbidden by the spec, `OpAssign` traits may have
++    /// implementations that differ from the regular `Op` impl.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let mut a = 5;
++    /// let b = 0;
++    /// // ...
++    /// a = a + b;
++    /// ```
++    pub ASSIGN_OP_PATTERN,
++    style,
++    "assigning the result of an operation on a variable to that same variable"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `a op= a op b` or `a op= b op a` patterns.
++    ///
++    /// **Why is this bad?** Most likely these are bugs where one meant to write `a
++    /// op= b`.
++    ///
++    /// **Known problems:** Clippy cannot know for sure if `a op= a op b` should have
++    /// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.
++    /// If `a op= a op b` is really the correct behaviour it should be
++    /// written as `a = a op a op b` as it's less confusing.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let mut a = 5;
++    /// let b = 2;
++    /// // ...
++    /// a += a + b;
++    /// ```
++    pub MISREFACTORED_ASSIGN_OP,
++    complexity,
++    "having a variable on both sides of an assign op"
++}
++
++declare_lint_pass!(AssignOps => [ASSIGN_OP_PATTERN, MISREFACTORED_ASSIGN_OP]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
++    #[allow(clippy::too_many_lines)]
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++        match &expr.kind {
++            hir::ExprKind::AssignOp(op, lhs, rhs) => {
++                if let hir::ExprKind::Binary(binop, l, r) = &rhs.kind {
++                    if op.node != binop.node {
++                        return;
++                    }
++                    // lhs op= l op r
++                    if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) {
++                        lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r);
++                    }
++                    // lhs op= l commutative_op r
++                    if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, r) {
++                        lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, l);
++                    }
++                }
++            },
++            hir::ExprKind::Assign(assignee, e, _) => {
++                if let hir::ExprKind::Binary(op, l, r) = &e.kind {
++                    let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| {
++                        let ty = cx.tables.expr_ty(assignee);
++                        let rty = cx.tables.expr_ty(rhs);
++                        macro_rules! ops {
++                            ($op:expr,
++                             $cx:expr,
++                             $ty:expr,
++                             $rty:expr,
++                             $($trait_name:ident),+) => {
++                                match $op {
++                                    $(hir::BinOpKind::$trait_name => {
++                                        let [krate, module] = crate::utils::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
++                                        } else {
++                                            return; // useless if the trait doesn't exist
++                                        };
++                                        // check that we are not inside an `impl AssignOp` of this exact operation
++                                        let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id);
++                                        if_chain! {
++                                            if let Some(trait_ref) = trait_ref_of_method(cx, parent_fn);
++                                            if trait_ref.path.res.def_id() == trait_id;
++                                            then { return; }
++                                        }
++                                        implements_trait($cx, $ty, trait_id, &[$rty])
++                                    },)*
++                                    _ => false,
++                                }
++                            }
++                        }
++                        if ops!(
++                            op.node,
++                            cx,
++                            ty,
++                            rty.into(),
++                            Add,
++                            Sub,
++                            Mul,
++                            Div,
++                            Rem,
++                            And,
++                            Or,
++                            BitAnd,
++                            BitOr,
++                            BitXor,
++                            Shr,
++                            Shl
++                        ) {
++                            span_lint_and_then(
++                                cx,
++                                ASSIGN_OP_PATTERN,
++                                expr.span,
++                                "manual implementation of an assign operation",
++                                |diag| {
++                                    if let (Some(snip_a), Some(snip_r)) =
++                                        (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span))
++                                    {
++                                        diag.span_suggestion(
++                                            expr.span,
++                                            "replace it with",
++                                            format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
++                                            Applicability::MachineApplicable,
++                                        );
++                                    }
++                                },
++                            );
++                        }
++                    };
++
++                    let mut visitor = ExprVisitor {
++                        assignee,
++                        counter: 0,
++                        cx,
++                    };
++
++                    walk_expr(&mut visitor, e);
++
++                    if visitor.counter == 1 {
++                        // a = a op b
++                        if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, l) {
++                            lint(assignee, r);
++                        }
++                        // a = b commutative_op a
++                        // Limited to primitive type as these ops are know to be commutative
++                        if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, r)
++                            && cx.tables.expr_ty(assignee).is_primitive_ty()
++                        {
++                            match op.node {
++                                hir::BinOpKind::Add
++                                | hir::BinOpKind::Mul
++                                | hir::BinOpKind::And
++                                | hir::BinOpKind::Or
++                                | hir::BinOpKind::BitXor
++                                | hir::BinOpKind::BitAnd
++                                | hir::BinOpKind::BitOr => {
++                                    lint(assignee, l);
++                                },
++                                _ => {},
++                            }
++                        }
++                    }
++                }
++            },
++            _ => {},
++        }
++    }
++}
++
++fn lint_misrefactored_assign_op(
++    cx: &LateContext<'_, '_>,
++    expr: &hir::Expr<'_>,
++    op: hir::BinOp,
++    rhs: &hir::Expr<'_>,
++    assignee: &hir::Expr<'_>,
++    rhs_other: &hir::Expr<'_>,
++) {
++    span_lint_and_then(
++        cx,
++        MISREFACTORED_ASSIGN_OP,
++        expr.span,
++        "variable appears on both sides of an assignment operation",
++        |diag| {
++            if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) {
++                let a = &sugg::Sugg::hir(cx, assignee, "..");
++                let r = &sugg::Sugg::hir(cx, rhs, "..");
++                let long = format!("{} = {}", snip_a, sugg::make_binop(higher::binop(op.node), a, r));
++                diag.span_suggestion(
++                    expr.span,
++                    &format!(
++                        "Did you mean `{} = {} {} {}` or `{}`? Consider replacing it with",
++                        snip_a,
++                        snip_a,
++                        op.node.as_str(),
++                        snip_r,
++                        long
++                    ),
++                    format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
++                    Applicability::MaybeIncorrect,
++                );
++                diag.span_suggestion(
++                    expr.span,
++                    "or",
++                    long,
++                    Applicability::MaybeIncorrect, // snippet
++                );
++            }
++        },
++    );
++}
++
++#[must_use]
++fn is_commutative(op: hir::BinOpKind) -> bool {
++    use rustc_hir::BinOpKind::{
++        Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
++    };
++    match op {
++        Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true,
++        Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false,
++    }
++}
++
++struct ExprVisitor<'a, 'tcx> {
++    assignee: &'a hir::Expr<'a>,
++    counter: u8,
++    cx: &'a LateContext<'a, 'tcx>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
++        if SpanlessEq::new(self.cx).ignore_fn().eq_expr(self.assignee, expr) {
++            self.counter += 1;
++        }
++
++        walk_expr(self, expr);
++    }
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..73b4cef472505ee4e5bc21e14028e615ce9dbeb2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,135 @@@
++use crate::utils::{match_def_path, span_lint_and_help};
++use if_chain::if_chain;
++use rustc_hir::def_id::DefId;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of invalid atomic
++    /// ordering in atomic loads/stores and memory fences.
++    ///
++    /// **Why is this bad?** Using an invalid atomic ordering
++    /// will cause a panic at run-time.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,no_run
++    /// # use std::sync::atomic::{self, AtomicBool, Ordering};
++    ///
++    /// let x = AtomicBool::new(true);
++    ///
++    /// let _ = x.load(Ordering::Release);
++    /// let _ = x.load(Ordering::AcqRel);
++    ///
++    /// x.store(false, Ordering::Acquire);
++    /// x.store(false, Ordering::AcqRel);
++    ///
++    /// atomic::fence(Ordering::Relaxed);
++    /// atomic::compiler_fence(Ordering::Relaxed);
++    /// ```
++    pub INVALID_ATOMIC_ORDERING,
++    correctness,
++    "usage of invalid atomic ordering in atomic loads/stores and memory fences"
++}
++
++declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]);
++
++const ATOMIC_TYPES: [&str; 12] = [
++    "AtomicBool",
++    "AtomicI8",
++    "AtomicI16",
++    "AtomicI32",
++    "AtomicI64",
++    "AtomicIsize",
++    "AtomicPtr",
++    "AtomicU8",
++    "AtomicU16",
++    "AtomicU32",
++    "AtomicU64",
++    "AtomicUsize",
++];
++
++fn type_is_atomic(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++    if let ty::Adt(&ty::AdtDef { did, .. }, _) = cx.tables.expr_ty(expr).kind {
++        ATOMIC_TYPES
++            .iter()
++            .any(|ty| match_def_path(cx, did, &["core", "sync", "atomic", ty]))
++    } else {
++        false
++    }
++}
++
++fn match_ordering_def_path(cx: &LateContext<'_, '_>, did: DefId, orderings: &[&str]) -> bool {
++    orderings
++        .iter()
++        .any(|ordering| match_def_path(cx, did, &["core", "sync", "atomic", "Ordering", ordering]))
++}
++
++fn check_atomic_load_store(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++    if_chain! {
++        if let ExprKind::MethodCall(ref method_path, _, args) = &expr.kind;
++        let method = method_path.ident.name.as_str();
++        if type_is_atomic(cx, &args[0]);
++        if method == "load" || method == "store";
++        let ordering_arg = if method == "load" { &args[1] } else { &args[2] };
++        if let ExprKind::Path(ref ordering_qpath) = ordering_arg.kind;
++        if let Some(ordering_def_id) = cx.tables.qpath_res(ordering_qpath, ordering_arg.hir_id).opt_def_id();
++        then {
++            if method == "load" &&
++                match_ordering_def_path(cx, ordering_def_id, &["Release", "AcqRel"]) {
++                span_lint_and_help(
++                    cx,
++                    INVALID_ATOMIC_ORDERING,
++                    ordering_arg.span,
++                    "atomic loads cannot have `Release` and `AcqRel` ordering",
++                    None,
++                    "consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`"
++                );
++            } else if method == "store" &&
++                match_ordering_def_path(cx, ordering_def_id, &["Acquire", "AcqRel"]) {
++                span_lint_and_help(
++                    cx,
++                    INVALID_ATOMIC_ORDERING,
++                    ordering_arg.span,
++                    "atomic stores cannot have `Acquire` and `AcqRel` ordering",
++                    None,
++                    "consider using ordering modes `Release`, `SeqCst` or `Relaxed`"
++                );
++            }
++        }
++    }
++}
++
++fn check_memory_fence(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++    if_chain! {
++        if let ExprKind::Call(ref func, ref args) = expr.kind;
++        if let ExprKind::Path(ref func_qpath) = func.kind;
++        if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id();
++        if ["fence", "compiler_fence"]
++            .iter()
++            .any(|func| match_def_path(cx, def_id, &["core", "sync", "atomic", func]));
++        if let ExprKind::Path(ref ordering_qpath) = &args[0].kind;
++        if let Some(ordering_def_id) = cx.tables.qpath_res(ordering_qpath, args[0].hir_id).opt_def_id();
++        if match_ordering_def_path(cx, ordering_def_id, &["Relaxed"]);
++        then {
++            span_lint_and_help(
++                cx,
++                INVALID_ATOMIC_ORDERING,
++                args[0].span,
++                "memory fences cannot have `Relaxed` ordering",
++                None,
++                "consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`"
++            );
++        }
++    }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AtomicOrdering {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        check_atomic_load_store(cx, expr);
++        check_memory_fence(cx, expr);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..64abc9fdc71742ed7a488a47b9f8b3fc9eaad20b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,657 @@@
++//! checks for attributes
++
++use crate::reexport::Name;
++use crate::utils::{
++    first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_sugg,
++    span_lint_and_then, without_block_comments,
++};
++use if_chain::if_chain;
++use rustc_ast::ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
++use rustc_ast::util::lev_distance::find_best_match_for_name;
++use rustc_errors::Applicability;
++use rustc_hir::{
++    Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
++};
++use rustc_lint::{CheckLintNameResult, EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++use rustc_span::symbol::Symbol;
++use semver::Version;
++
++static UNIX_SYSTEMS: &[&str] = &[
++    "android",
++    "dragonfly",
++    "emscripten",
++    "freebsd",
++    "fuchsia",
++    "haiku",
++    "illumos",
++    "ios",
++    "l4re",
++    "linux",
++    "macos",
++    "netbsd",
++    "openbsd",
++    "redox",
++    "solaris",
++    "vxworks",
++];
++
++// NOTE: windows is excluded from the list because it's also a valid target family.
++static NON_UNIX_SYSTEMS: &[&str] = &["cloudabi", "hermit", "none", "wasi"];
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for items annotated with `#[inline(always)]`,
++    /// unless the annotated function is empty or simply panics.
++    ///
++    /// **Why is this bad?** While there are valid uses of this annotation (and once
++    /// you know when to use it, by all means `allow` this lint), it's a common
++    /// newbie-mistake to pepper one's code with it.
++    ///
++    /// As a rule of thumb, before slapping `#[inline(always)]` on a function,
++    /// measure if that additional function call really affects your runtime profile
++    /// sufficiently to make up for the increase in compile time.
++    ///
++    /// **Known problems:** False positives, big time. This lint is meant to be
++    /// deactivated by everyone doing serious performance work. This means having
++    /// done the measurement.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// #[inline(always)]
++    /// fn not_quite_hot_code(..) { ... }
++    /// ```
++    pub INLINE_ALWAYS,
++    pedantic,
++    "use of `#[inline(always)]`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `extern crate` and `use` items annotated with
++    /// lint attributes.
++    ///
++    /// This lint whitelists `#[allow(unused_imports)]`, `#[allow(deprecated)]` and
++    /// `#[allow(unreachable_pub)]` on `use` items and `#[allow(unused_imports)]` on
++    /// `extern crate` items with a `#[macro_use]` attribute.
++    ///
++    /// **Why is this bad?** Lint attributes have no effect on crate imports. Most
++    /// likely a `!` was forgotten.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// // Bad
++    /// #[deny(dead_code)]
++    /// extern crate foo;
++    /// #[forbid(dead_code)]
++    /// use foo::bar;
++    ///
++    /// // Ok
++    /// #[allow(unused_imports)]
++    /// use foo::baz;
++    /// #[allow(unused_imports)]
++    /// #[macro_use]
++    /// extern crate baz;
++    /// ```
++    pub USELESS_ATTRIBUTE,
++    correctness,
++    "use of lint attributes on `extern crate` items"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `#[deprecated]` annotations with a `since`
++    /// field that is not a valid semantic version.
++    ///
++    /// **Why is this bad?** For checking the version of the deprecation, it must be
++    /// a valid semver. Failing that, the contained information is useless.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// #[deprecated(since = "forever")]
++    /// fn something_else() { /* ... */ }
++    /// ```
++    pub DEPRECATED_SEMVER,
++    correctness,
++    "use of `#[deprecated(since = \"x\")]` where x is not semver"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for empty lines after outer attributes
++    ///
++    /// **Why is this bad?**
++    /// Most likely the attribute was meant to be an inner attribute using a '!'.
++    /// If it was meant to be an outer attribute, then the following item
++    /// should not be separated by empty lines.
++    ///
++    /// **Known problems:** Can cause false positives.
++    ///
++    /// From the clippy side it's difficult to detect empty lines between an attributes and the
++    /// following item because empty lines and comments are not part of the AST. The parsing
++    /// currently works for basic cases but is not perfect.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// // Good (as inner attribute)
++    /// #![inline(always)]
++    ///
++    /// fn this_is_fine() { }
++    ///
++    /// // Bad
++    /// #[inline(always)]
++    ///
++    /// fn not_quite_good_code() { }
++    ///
++    /// // Good (as outer attribute)
++    /// #[inline(always)]
++    /// fn this_is_fine_too() { }
++    /// ```
++    pub EMPTY_LINE_AFTER_OUTER_ATTR,
++    nursery,
++    "empty line after outer attribute"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `allow`/`warn`/`deny`/`forbid` attributes with scoped clippy
++    /// lints and if those lints exist in clippy. If there is an uppercase letter in the lint name
++    /// (not the tool name) and a lowercase version of this lint exists, it will suggest to lowercase
++    /// the lint name.
++    ///
++    /// **Why is this bad?** A lint attribute with a mistyped lint name won't have an effect.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// Bad:
++    /// ```rust
++    /// #![warn(if_not_els)]
++    /// #![deny(clippy::All)]
++    /// ```
++    ///
++    /// Good:
++    /// ```rust
++    /// #![warn(if_not_else)]
++    /// #![deny(clippy::all)]
++    /// ```
++    pub UNKNOWN_CLIPPY_LINTS,
++    style,
++    "unknown_lints for scoped Clippy lints"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it
++    /// with `#[rustfmt::skip]`.
++    ///
++    /// **Why is this bad?** Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690))
++    /// are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes.
++    ///
++    /// **Known problems:** This lint doesn't detect crate level inner attributes, because they get
++    /// processed before the PreExpansionPass lints get executed. See
++    /// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)
++    ///
++    /// **Example:**
++    ///
++    /// Bad:
++    /// ```rust
++    /// #[cfg_attr(rustfmt, rustfmt_skip)]
++    /// fn main() { }
++    /// ```
++    ///
++    /// Good:
++    /// ```rust
++    /// #[rustfmt::skip]
++    /// fn main() { }
++    /// ```
++    pub DEPRECATED_CFG_ATTR,
++    complexity,
++    "usage of `cfg_attr(rustfmt)` instead of tool attributes"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for cfg attributes having operating systems used in target family position.
++    ///
++    /// **Why is this bad?** The configuration option will not be recognised and the related item will not be included
++    /// by the conditional compilation engine.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// Bad:
++    /// ```rust
++    /// #[cfg(linux)]
++    /// fn conditional() { }
++    /// ```
++    ///
++    /// Good:
++    /// ```rust
++    /// #[cfg(target_os = "linux")]
++    /// fn conditional() { }
++    /// ```
++    ///
++    /// Or:
++    /// ```rust
++    /// #[cfg(unix)]
++    /// fn conditional() { }
++    /// ```
++    /// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.
++    pub MISMATCHED_TARGET_OS,
++    correctness,
++    "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
++}
++
++declare_lint_pass!(Attributes => [
++    INLINE_ALWAYS,
++    DEPRECATED_SEMVER,
++    USELESS_ATTRIBUTE,
++    EMPTY_LINE_AFTER_OUTER_ATTR,
++    UNKNOWN_CLIPPY_LINTS,
++]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Attributes {
++    fn check_attribute(&mut self, cx: &LateContext<'a, 'tcx>, attr: &'tcx Attribute) {
++        if let Some(items) = &attr.meta_item_list() {
++            if let Some(ident) = attr.ident() {
++                match &*ident.as_str() {
++                    "allow" | "warn" | "deny" | "forbid" => {
++                        check_clippy_lint_names(cx, items);
++                    },
++                    _ => {},
++                }
++                if items.is_empty() || !attr.check_name(sym!(deprecated)) {
++                    return;
++                }
++                for item in items {
++                    if_chain! {
++                        if let NestedMetaItem::MetaItem(mi) = &item;
++                        if let MetaItemKind::NameValue(lit) = &mi.kind;
++                        if mi.check_name(sym!(since));
++                        then {
++                            check_semver(cx, item.span(), lit);
++                        }
++                    }
++                }
++            }
++        }
++    }
++
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++        if is_relevant_item(cx, item) {
++            check_attrs(cx, item.span, item.ident.name, &item.attrs)
++        }
++        match item.kind {
++            ItemKind::ExternCrate(..) | ItemKind::Use(..) => {
++                let skip_unused_imports = item.attrs.iter().any(|attr| attr.check_name(sym!(macro_use)));
++
++                for attr in item.attrs {
++                    if in_external_macro(cx.sess(), attr.span) {
++                        return;
++                    }
++                    if let Some(lint_list) = &attr.meta_item_list() {
++                        if let Some(ident) = attr.ident() {
++                            match &*ident.as_str() {
++                                "allow" | "warn" | "deny" | "forbid" => {
++                                    // whitelist `unused_imports`, `deprecated` and `unreachable_pub` for `use` items
++                                    // and `unused_imports` for `extern crate` items with `macro_use`
++                                    for lint in lint_list {
++                                        match item.kind {
++                                            ItemKind::Use(..) => {
++                                                if is_word(lint, sym!(unused_imports))
++                                                    || is_word(lint, sym!(deprecated))
++                                                    || is_word(lint, sym!(unreachable_pub))
++                                                    || is_word(lint, sym!(unused))
++                                                {
++                                                    return;
++                                                }
++                                            },
++                                            ItemKind::ExternCrate(..) => {
++                                                if is_word(lint, sym!(unused_imports)) && skip_unused_imports {
++                                                    return;
++                                                }
++                                                if is_word(lint, sym!(unused_extern_crates)) {
++                                                    return;
++                                                }
++                                            },
++                                            _ => {},
++                                        }
++                                    }
++                                    let line_span = first_line_of_span(cx, attr.span);
++
++                                    if let Some(mut sugg) = snippet_opt(cx, line_span) {
++                                        if sugg.contains("#[") {
++                                            span_lint_and_then(
++                                                cx,
++                                                USELESS_ATTRIBUTE,
++                                                line_span,
++                                                "useless lint attribute",
++                                                |diag| {
++                                                    sugg = sugg.replacen("#[", "#![", 1);
++                                                    diag.span_suggestion(
++                                                        line_span,
++                                                        "if you just forgot a `!`, use",
++                                                        sugg,
++                                                        Applicability::MaybeIncorrect,
++                                                    );
++                                                },
++                                            );
++                                        }
++                                    }
++                                },
++                                _ => {},
++                            }
++                        }
++                    }
++                }
++            },
++            _ => {},
++        }
++    }
++
++    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem<'_>) {
++        if is_relevant_impl(cx, item) {
++            check_attrs(cx, item.span, item.ident.name, &item.attrs)
++        }
++    }
++
++    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) {
++        if is_relevant_trait(cx, item) {
++            check_attrs(cx, item.span, item.ident.name, &item.attrs)
++        }
++    }
++}
++
++#[allow(clippy::single_match_else)]
++fn check_clippy_lint_names(cx: &LateContext<'_, '_>, items: &[NestedMetaItem]) {
++    let lint_store = cx.lints();
++    for lint in items {
++        if_chain! {
++            if let Some(meta_item) = lint.meta_item();
++            if meta_item.path.segments.len() > 1;
++            if let tool_name = meta_item.path.segments[0].ident;
++            if tool_name.as_str() == "clippy";
++            let name = meta_item.path.segments.last().unwrap().ident.name;
++            if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name(
++                &name.as_str(),
++                Some(tool_name.name),
++            );
++            then {
++                span_lint_and_then(
++                    cx,
++                    UNKNOWN_CLIPPY_LINTS,
++                    lint.span(),
++                    &format!("unknown clippy lint: clippy::{}", name),
++                    |diag| {
++                        let name_lower = name.as_str().to_lowercase();
++                        let symbols = lint_store.get_lints().iter().map(
++                            |l| Symbol::intern(&l.name_lower())
++                        ).collect::<Vec<_>>();
++                        let sugg = find_best_match_for_name(
++                            symbols.iter(),
++                            &format!("clippy::{}", name_lower),
++                            None,
++                        );
++                        if name.as_str().chars().any(char::is_uppercase)
++                            && lint_store.find_lints(&format!("clippy::{}", name_lower)).is_ok() {
++                            diag.span_suggestion(
++                                lint.span(),
++                                "lowercase the lint name",
++                                format!("clippy::{}", name_lower),
++                                Applicability::MachineApplicable,
++                            );
++                        } else if let Some(sugg) = sugg {
++                            diag.span_suggestion(
++                                lint.span(),
++                                "did you mean",
++                                sugg.to_string(),
++                                Applicability::MachineApplicable,
++                            );
++                        }
++                    }
++                );
++            }
++        };
++    }
++}
++
++fn is_relevant_item(cx: &LateContext<'_, '_>, item: &Item<'_>) -> bool {
++    if let ItemKind::Fn(_, _, eid) = item.kind {
++        is_relevant_expr(cx, cx.tcx.body_tables(eid), &cx.tcx.hir().body(eid).value)
++    } else {
++        true
++    }
++}
++
++fn is_relevant_impl(cx: &LateContext<'_, '_>, item: &ImplItem<'_>) -> bool {
++    match item.kind {
++        ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.body_tables(eid), &cx.tcx.hir().body(eid).value),
++        _ => false,
++    }
++}
++
++fn is_relevant_trait(cx: &LateContext<'_, '_>, item: &TraitItem<'_>) -> bool {
++    match item.kind {
++        TraitItemKind::Fn(_, TraitFn::Required(_)) => true,
++        TraitItemKind::Fn(_, TraitFn::Provided(eid)) => {
++            is_relevant_expr(cx, cx.tcx.body_tables(eid), &cx.tcx.hir().body(eid).value)
++        },
++        _ => false,
++    }
++}
++
++fn is_relevant_block(cx: &LateContext<'_, '_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool {
++    if let Some(stmt) = block.stmts.first() {
++        match &stmt.kind {
++            StmtKind::Local(_) => true,
++            StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, tables, expr),
++            _ => false,
++        }
++    } else {
++        block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e))
++    }
++}
++
++fn is_relevant_expr(cx: &LateContext<'_, '_>, tables: &ty::TypeckTables<'_>, expr: &Expr<'_>) -> bool {
++    match &expr.kind {
++        ExprKind::Block(block, _) => is_relevant_block(cx, tables, block),
++        ExprKind::Ret(Some(e)) => is_relevant_expr(cx, tables, e),
++        ExprKind::Ret(None) | ExprKind::Break(_, None) => false,
++        ExprKind::Call(path_expr, _) => {
++            if let ExprKind::Path(qpath) = &path_expr.kind {
++                if let Some(fun_id) = tables.qpath_res(qpath, path_expr.hir_id).opt_def_id() {
++                    !match_def_path(cx, fun_id, &paths::BEGIN_PANIC)
++                } else {
++                    true
++                }
++            } else {
++                true
++            }
++        },
++        _ => true,
++    }
++}
++
++fn check_attrs(cx: &LateContext<'_, '_>, span: Span, name: Name, attrs: &[Attribute]) {
++    if span.from_expansion() {
++        return;
++    }
++
++    for attr in attrs {
++        let attr_item = if let AttrKind::Normal(ref attr) = attr.kind {
++            attr
++        } else {
++            continue;
++        };
++
++        if attr.style == AttrStyle::Outer {
++            if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) {
++                return;
++            }
++
++            let begin_of_attr_to_item = Span::new(attr.span.lo(), span.lo(), span.ctxt());
++            let end_of_attr_to_item = Span::new(attr.span.hi(), span.lo(), span.ctxt());
++
++            if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) {
++                let lines = snippet.split('\n').collect::<Vec<_>>();
++                let lines = without_block_comments(lines);
++
++                if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
++                    span_lint(
++                        cx,
++                        EMPTY_LINE_AFTER_OUTER_ATTR,
++                        begin_of_attr_to_item,
++                        "Found an empty line after an outer attribute. \
++                         Perhaps you forgot to add a `!` to make it an inner attribute?",
++                    );
++                }
++            }
++        }
++
++        if let Some(values) = attr.meta_item_list() {
++            if values.len() != 1 || !attr.check_name(sym!(inline)) {
++                continue;
++            }
++            if is_word(&values[0], sym!(always)) {
++                span_lint(
++                    cx,
++                    INLINE_ALWAYS,
++                    attr.span,
++                    &format!(
++                        "you have declared `#[inline(always)]` on `{}`. This is usually a bad idea",
++                        name
++                    ),
++                );
++            }
++        }
++    }
++}
++
++fn check_semver(cx: &LateContext<'_, '_>, span: Span, lit: &Lit) {
++    if let LitKind::Str(is, _) = lit.kind {
++        if Version::parse(&is.as_str()).is_ok() {
++            return;
++        }
++    }
++    span_lint(
++        cx,
++        DEPRECATED_SEMVER,
++        span,
++        "the since field must contain a semver-compliant version",
++    );
++}
++
++fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
++    if let NestedMetaItem::MetaItem(mi) = &nmi {
++        mi.is_word() && mi.check_name(expected)
++    } else {
++        false
++    }
++}
++
++declare_lint_pass!(EarlyAttributes => [DEPRECATED_CFG_ATTR, MISMATCHED_TARGET_OS]);
++
++impl EarlyLintPass for EarlyAttributes {
++    fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
++        check_deprecated_cfg_attr(cx, attr);
++        check_mismatched_target_os(cx, attr);
++    }
++}
++
++fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) {
++    if_chain! {
++        // check cfg_attr
++        if attr.check_name(sym!(cfg_attr));
++        if let Some(items) = attr.meta_item_list();
++        if items.len() == 2;
++        // check for `rustfmt`
++        if let Some(feature_item) = items[0].meta_item();
++        if feature_item.check_name(sym!(rustfmt));
++        // check for `rustfmt_skip` and `rustfmt::skip`
++        if let Some(skip_item) = &items[1].meta_item();
++        if skip_item.check_name(sym!(rustfmt_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;
++        then {
++            span_lint_and_sugg(
++                cx,
++                DEPRECATED_CFG_ATTR,
++                attr.span,
++                "`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes",
++                "use",
++                "#[rustfmt::skip]".to_string(),
++                Applicability::MachineApplicable,
++            );
++        }
++    }
++}
++
++fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
++    fn find_os(name: &str) -> Option<&'static str> {
++        UNIX_SYSTEMS
++            .iter()
++            .chain(NON_UNIX_SYSTEMS.iter())
++            .find(|&&os| os == name)
++            .copied()
++    }
++
++    fn is_unix(name: &str) -> bool {
++        UNIX_SYSTEMS.iter().any(|&os| os == name)
++    }
++
++    fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> {
++        let mut mismatched = Vec::new();
++
++        for item in items {
++            if let NestedMetaItem::MetaItem(meta) = item {
++                match &meta.kind {
++                    MetaItemKind::List(list) => {
++                        mismatched.extend(find_mismatched_target_os(&list));
++                    },
++                    MetaItemKind::Word => {
++                        if_chain! {
++                            if let Some(ident) = meta.ident();
++                            if let Some(os) = find_os(&*ident.name.as_str());
++                            then {
++                                mismatched.push((os, ident.span));
++                            }
++                        }
++                    },
++                    _ => {},
++                }
++            }
++        }
++
++        mismatched
++    }
++
++    if_chain! {
++        if attr.check_name(sym!(cfg));
++        if let Some(list) = attr.meta_item_list();
++        let mismatched = find_mismatched_target_os(&list);
++        if !mismatched.is_empty();
++        then {
++            let mess = "operating system used in target family position";
++
++            span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, &mess, |diag| {
++                // Avoid showing the unix suggestion multiple times in case
++                // we have more than one mismatch for unix-like systems
++                let mut unix_suggested = false;
++
++                for (os, span) in mismatched {
++                    let sugg = format!("target_os = \"{}\"", os);
++                    diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect);
++
++                    if !unix_suggested && is_unix(os) {
++                        diag.help("Did you mean `unix`?");
++                        unix_suggested = true;
++                    }
++                }
++            });
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..832910763e60c99e55b4adb7f1bcc00e292c556e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,97 @@@
++use crate::utils::{match_def_path, paths, span_lint_and_note};
++use rustc_hir::def_id::DefId;
++use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::GeneratorInteriorTypeCause;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for calls to await while holding a
++    /// non-async-aware MutexGuard.
++    ///
++    /// **Why is this bad?** The Mutex types found in syd::sync and parking_lot
++    /// are not designed to operator in an async context across await points.
++    ///
++    /// There are two potential solutions. One is to use an asynx-aware Mutex
++    /// type. Many asynchronous foundation crates provide such a Mutex type. The
++    /// other solution is to ensure the mutex is unlocked before calling await,
++    /// either by introducing a scope or an explicit call to Drop::drop.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust,ignore
++    /// use std::sync::Mutex;
++    ///
++    /// async fn foo(x: &Mutex<u32>) {
++    ///   let guard = x.lock().unwrap();
++    ///   *guard += 1;
++    ///   bar.await;
++    /// }
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust,ignore
++    /// use std::sync::Mutex;
++    ///
++    /// async fn foo(x: &Mutex<u32>) {
++    ///   {
++    ///     let guard = x.lock().unwrap();
++    ///     *guard += 1;
++    ///   }
++    ///   bar.await;
++    /// }
++    /// ```
++    pub AWAIT_HOLDING_LOCK,
++    pedantic,
++    "Inside an async function, holding a MutexGuard while calling await"
++}
++
++declare_lint_pass!(AwaitHoldingLock => [AWAIT_HOLDING_LOCK]);
++
++impl LateLintPass<'_, '_> for AwaitHoldingLock {
++    fn check_body(&mut self, cx: &LateContext<'_, '_>, body: &'_ Body<'_>) {
++        use AsyncGeneratorKind::{Block, Closure, Fn};
++        match body.generator_kind {
++            Some(GeneratorKind::Async(Block))
++            | Some(GeneratorKind::Async(Closure))
++            | Some(GeneratorKind::Async(Fn)) => {
++                let body_id = BodyId {
++                    hir_id: body.value.hir_id,
++                };
++                let def_id = cx.tcx.hir().body_owner_def_id(body_id);
++                let tables = cx.tcx.typeck_tables_of(def_id);
++                check_interior_types(cx, &tables.generator_interior_types, body.value.span);
++            },
++            _ => {},
++        }
++    }
++}
++
++fn check_interior_types(cx: &LateContext<'_, '_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
++    for ty_cause in ty_causes {
++        if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind {
++            if is_mutex_guard(cx, adt.did) {
++                span_lint_and_note(
++                    cx,
++                    AWAIT_HOLDING_LOCK,
++                    ty_cause.span,
++                    "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.",
++                    ty_cause.scope_span.or(Some(span)),
++                    "these are all the await points this lock is held through",
++                );
++            }
++        }
++    }
++}
++
++fn is_mutex_guard(cx: &LateContext<'_, '_>, def_id: DefId) -> bool {
++    match_def_path(cx, def_id, &paths::MUTEX_GUARD)
++        || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD)
++        || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD)
++        || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD)
++        || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
++        || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ccb62cb038fd0a4c7aab05a7178d31e840d7e6e1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,326 @@@
++use crate::consts::{constant, Constant};
++use crate::utils::sugg::Sugg;
++use crate::utils::{span_lint, span_lint_and_then};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{BinOpKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for incompatible bit masks in comparisons.
++    ///
++    /// The formula for detecting if an expression of the type `_ <bit_op> m
++    /// <cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of
++    /// {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following
++    /// table:
++    ///
++    /// |Comparison  |Bit Op|Example     |is always|Formula               |
++    /// |------------|------|------------|---------|----------------------|
++    /// |`==` or `!=`| `&`  |`x & 2 == 3`|`false`  |`c & m != c`          |
++    /// |`<`  or `>=`| `&`  |`x & 2 < 3` |`true`   |`m < c`               |
++    /// |`>`  or `<=`| `&`  |`x & 1 > 1` |`false`  |`m <= c`              |
++    /// |`==` or `!=`| `|`  |`x | 1 == 0`|`false`  |`c | m != c`          |
++    /// |`<`  or `>=`| `|`  |`x | 1 < 1` |`false`  |`m >= c`              |
++    /// |`<=` or `>` | `|`  |`x | 1 > 0` |`true`   |`m > c`               |
++    ///
++    /// **Why is this bad?** If the bits that the comparison cares about are always
++    /// set to zero or one by the bit mask, the comparison is constant `true` or
++    /// `false` (depending on mask, compared value, and operators).
++    ///
++    /// So the code is actively misleading, and the only reason someone would write
++    /// this intentionally is to win an underhanded Rust contest or create a
++    /// test-case for this lint.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let x = 1;
++    /// if (x & 1 == 2) { }
++    /// ```
++    pub BAD_BIT_MASK,
++    correctness,
++    "expressions of the form `_ & mask == select` that will only ever return `true` or `false`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for bit masks in comparisons which can be removed
++    /// without changing the outcome. The basic structure can be seen in the
++    /// following table:
++    ///
++    /// |Comparison| Bit Op  |Example    |equals |
++    /// |----------|---------|-----------|-------|
++    /// |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|
++    /// |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|
++    ///
++    /// **Why is this bad?** Not equally evil as [`bad_bit_mask`](#bad_bit_mask),
++    /// but still a bit misleading, because the bit mask is ineffective.
++    ///
++    /// **Known problems:** False negatives: This lint will only match instances
++    /// where we have figured out the math (which is for a power-of-two compared
++    /// value). This means things like `x | 1 >= 7` (which would be better written
++    /// as `x >= 6`) will not be reported (but bit masks like this are fairly
++    /// uncommon).
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let x = 1;
++    /// if (x | 1 > 3) {  }
++    /// ```
++    pub INEFFECTIVE_BIT_MASK,
++    correctness,
++    "expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for bit masks that can be replaced by a call
++    /// to `trailing_zeros`
++    ///
++    /// **Why is this bad?** `x.trailing_zeros() > 4` is much clearer than `x & 15
++    /// == 0`
++    ///
++    /// **Known problems:** llvm generates better code for `x & 15 == 0` on x86
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let x = 1;
++    /// if x & 0b1111 == 0 { }
++    /// ```
++    pub VERBOSE_BIT_MASK,
++    style,
++    "expressions where a bit mask is less readable than the corresponding method call"
++}
++
++#[derive(Copy, Clone)]
++pub struct BitMask {
++    verbose_bit_mask_threshold: u64,
++}
++
++impl BitMask {
++    #[must_use]
++    pub fn new(verbose_bit_mask_threshold: u64) -> Self {
++        Self {
++            verbose_bit_mask_threshold,
++        }
++    }
++}
++
++impl_lint_pass!(BitMask => [BAD_BIT_MASK, INEFFECTIVE_BIT_MASK, VERBOSE_BIT_MASK]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BitMask {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        if let ExprKind::Binary(cmp, left, right) = &e.kind {
++            if cmp.node.is_comparison() {
++                if let Some(cmp_opt) = fetch_int_literal(cx, right) {
++                    check_compare(cx, left, cmp.node, cmp_opt, e.span)
++                } else if let Some(cmp_val) = fetch_int_literal(cx, left) {
++                    check_compare(cx, right, invert_cmp(cmp.node), cmp_val, e.span)
++                }
++            }
++        }
++        if_chain! {
++            if let ExprKind::Binary(op, left, right) = &e.kind;
++            if BinOpKind::Eq == op.node;
++            if let ExprKind::Binary(op1, left1, right1) = &left.kind;
++            if BinOpKind::BitAnd == op1.node;
++            if let ExprKind::Lit(lit) = &right1.kind;
++            if let LitKind::Int(n, _) = lit.node;
++            if let ExprKind::Lit(lit1) = &right.kind;
++            if let LitKind::Int(0, _) = lit1.node;
++            if n.leading_zeros() == n.count_zeros();
++            if n > u128::from(self.verbose_bit_mask_threshold);
++            then {
++                span_lint_and_then(cx,
++                                   VERBOSE_BIT_MASK,
++                                   e.span,
++                                   "bit mask could be simplified with a call to `trailing_zeros`",
++                                   |diag| {
++                    let sugg = Sugg::hir(cx, left1, "...").maybe_par();
++                    diag.span_suggestion(
++                        e.span,
++                        "try",
++                        format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()),
++                        Applicability::MaybeIncorrect,
++                    );
++                });
++            }
++        }
++    }
++}
++
++#[must_use]
++fn invert_cmp(cmp: BinOpKind) -> BinOpKind {
++    match cmp {
++        BinOpKind::Eq => BinOpKind::Eq,
++        BinOpKind::Ne => BinOpKind::Ne,
++        BinOpKind::Lt => BinOpKind::Gt,
++        BinOpKind::Gt => BinOpKind::Lt,
++        BinOpKind::Le => BinOpKind::Ge,
++        BinOpKind::Ge => BinOpKind::Le,
++        _ => BinOpKind::Or, // Dummy
++    }
++}
++
++fn check_compare(cx: &LateContext<'_, '_>, bit_op: &Expr<'_>, cmp_op: BinOpKind, cmp_value: u128, span: Span) {
++    if let ExprKind::Binary(op, left, right) = &bit_op.kind {
++        if op.node != BinOpKind::BitAnd && op.node != BinOpKind::BitOr {
++            return;
++        }
++        fetch_int_literal(cx, right)
++            .or_else(|| fetch_int_literal(cx, left))
++            .map_or((), |mask| check_bit_mask(cx, op.node, cmp_op, mask, cmp_value, span))
++    }
++}
++
++#[allow(clippy::too_many_lines)]
++fn check_bit_mask(
++    cx: &LateContext<'_, '_>,
++    bit_op: BinOpKind,
++    cmp_op: BinOpKind,
++    mask_value: u128,
++    cmp_value: u128,
++    span: Span,
++) {
++    match cmp_op {
++        BinOpKind::Eq | BinOpKind::Ne => match bit_op {
++            BinOpKind::BitAnd => {
++                if mask_value & cmp_value != cmp_value {
++                    if cmp_value != 0 {
++                        span_lint(
++                            cx,
++                            BAD_BIT_MASK,
++                            span,
++                            &format!(
++                                "incompatible bit mask: `_ & {}` can never be equal to `{}`",
++                                mask_value, cmp_value
++                            ),
++                        );
++                    }
++                } else if mask_value == 0 {
++                    span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
++                }
++            },
++            BinOpKind::BitOr => {
++                if mask_value | cmp_value != cmp_value {
++                    span_lint(
++                        cx,
++                        BAD_BIT_MASK,
++                        span,
++                        &format!(
++                            "incompatible bit mask: `_ | {}` can never be equal to `{}`",
++                            mask_value, cmp_value
++                        ),
++                    );
++                }
++            },
++            _ => (),
++        },
++        BinOpKind::Lt | BinOpKind::Ge => match bit_op {
++            BinOpKind::BitAnd => {
++                if mask_value < cmp_value {
++                    span_lint(
++                        cx,
++                        BAD_BIT_MASK,
++                        span,
++                        &format!(
++                            "incompatible bit mask: `_ & {}` will always be lower than `{}`",
++                            mask_value, cmp_value
++                        ),
++                    );
++                } else if mask_value == 0 {
++                    span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
++                }
++            },
++            BinOpKind::BitOr => {
++                if mask_value >= cmp_value {
++                    span_lint(
++                        cx,
++                        BAD_BIT_MASK,
++                        span,
++                        &format!(
++                            "incompatible bit mask: `_ | {}` will never be lower than `{}`",
++                            mask_value, cmp_value
++                        ),
++                    );
++                } else {
++                    check_ineffective_lt(cx, span, mask_value, cmp_value, "|");
++                }
++            },
++            BinOpKind::BitXor => check_ineffective_lt(cx, span, mask_value, cmp_value, "^"),
++            _ => (),
++        },
++        BinOpKind::Le | BinOpKind::Gt => match bit_op {
++            BinOpKind::BitAnd => {
++                if mask_value <= cmp_value {
++                    span_lint(
++                        cx,
++                        BAD_BIT_MASK,
++                        span,
++                        &format!(
++                            "incompatible bit mask: `_ & {}` will never be higher than `{}`",
++                            mask_value, cmp_value
++                        ),
++                    );
++                } else if mask_value == 0 {
++                    span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
++                }
++            },
++            BinOpKind::BitOr => {
++                if mask_value > cmp_value {
++                    span_lint(
++                        cx,
++                        BAD_BIT_MASK,
++                        span,
++                        &format!(
++                            "incompatible bit mask: `_ | {}` will always be higher than `{}`",
++                            mask_value, cmp_value
++                        ),
++                    );
++                } else {
++                    check_ineffective_gt(cx, span, mask_value, cmp_value, "|");
++                }
++            },
++            BinOpKind::BitXor => check_ineffective_gt(cx, span, mask_value, cmp_value, "^"),
++            _ => (),
++        },
++        _ => (),
++    }
++}
++
++fn check_ineffective_lt(cx: &LateContext<'_, '_>, span: Span, m: u128, c: u128, op: &str) {
++    if c.is_power_of_two() && m < c {
++        span_lint(
++            cx,
++            INEFFECTIVE_BIT_MASK,
++            span,
++            &format!(
++                "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
++                op, m, c
++            ),
++        );
++    }
++}
++
++fn check_ineffective_gt(cx: &LateContext<'_, '_>, span: Span, m: u128, c: u128, op: &str) {
++    if (c + 1).is_power_of_two() && m <= c {
++        span_lint(
++            cx,
++            INEFFECTIVE_BIT_MASK,
++            span,
++            &format!(
++                "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
++                op, m, c
++            ),
++        );
++    }
++}
++
++fn fetch_int_literal(cx: &LateContext<'_, '_>, lit: &Expr<'_>) -> Option<u128> {
++    match constant(cx, cx.tables, lit)?.0 {
++        Constant::Int(n) => Some(n),
++        _ => None,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6f26f031431dbfb0eb7cda8e722a59fc14ebf1bc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++use crate::utils::span_lint;
++use rustc_data_structures::fx::FxHashSet;
++use rustc_hir::{Pat, PatKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of blacklisted names for variables, such
++    /// as `foo`.
++    ///
++    /// **Why is this bad?** These names are usually placeholder names and should be
++    /// avoided.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let foo = 3.14;
++    /// ```
++    pub BLACKLISTED_NAME,
++    style,
++    "usage of a blacklisted/placeholder name"
++}
++
++#[derive(Clone, Debug)]
++pub struct BlacklistedName {
++    blacklist: FxHashSet<String>,
++}
++
++impl BlacklistedName {
++    pub fn new(blacklist: FxHashSet<String>) -> Self {
++        Self { blacklist }
++    }
++}
++
++impl_lint_pass!(BlacklistedName => [BLACKLISTED_NAME]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlacklistedName {
++    fn check_pat(&mut self, cx: &LateContext<'a, 'tcx>, pat: &'tcx Pat<'_>) {
++        if let PatKind::Binding(.., ident, _) = pat.kind {
++            if self.blacklist.contains(&ident.name.to_string()) {
++                span_lint(
++                    cx,
++                    BLACKLISTED_NAME,
++                    ident.span,
++                    &format!("use of a blacklisted/placeholder name `{}`", ident.name),
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9e533eaa32c93a08e5866245f52741238378af52
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,148 @@@
++use crate::utils::{differing_macro_contexts, higher, snippet_block_with_applicability, span_lint, span_lint_and_sugg};
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_hir::{BlockCheckMode, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `if` conditions that use blocks to contain an
++    /// expression.
++    ///
++    /// **Why is this bad?** It isn't really Rust style, same as using parentheses
++    /// to contain expressions.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// if { true } { /* ... */ }
++    /// ```
++    pub BLOCK_IN_IF_CONDITION_EXPR,
++    style,
++    "braces that can be eliminated in conditions, e.g., `if { true } ...`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `if` conditions that use blocks containing
++    /// statements, or conditions that use closures with blocks.
++    ///
++    /// **Why is this bad?** Using blocks in the condition makes it hard to read.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// if { let x = somefunc(); x } {}
++    /// // or
++    /// if somefunc(|x| { x == 47 }) {}
++    /// ```
++    pub BLOCK_IN_IF_CONDITION_STMT,
++    style,
++    "complex blocks in conditions, e.g., `if { let x = true; x } ...`"
++}
++
++declare_lint_pass!(BlockInIfCondition => [BLOCK_IN_IF_CONDITION_EXPR, BLOCK_IN_IF_CONDITION_STMT]);
++
++struct ExVisitor<'a, 'tcx> {
++    found_block: Option<&'tcx Expr<'tcx>>,
++    cx: &'a LateContext<'a, 'tcx>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
++        if let ExprKind::Closure(_, _, eid, _, _) = expr.kind {
++            let body = self.cx.tcx.hir().body(eid);
++            let ex = &body.value;
++            if matches!(ex.kind, ExprKind::Block(_, _)) && !body.value.span.from_expansion() {
++                self.found_block = Some(ex);
++                return;
++            }
++        }
++        walk_expr(self, expr);
++    }
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition";
++const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \
++                                     instead, move the block or closure higher and bind it with a `let`";
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if in_external_macro(cx.sess(), expr.span) {
++            return;
++        }
++        if let Some((cond, _, _)) = higher::if_block(&expr) {
++            if let ExprKind::Block(block, _) = &cond.kind {
++                if block.rules == BlockCheckMode::DefaultBlock {
++                    if block.stmts.is_empty() {
++                        if let Some(ex) = &block.expr {
++                            // don't dig into the expression here, just suggest that they remove
++                            // the block
++                            if expr.span.from_expansion() || differing_macro_contexts(expr.span, ex.span) {
++                                return;
++                            }
++                            let mut applicability = Applicability::MachineApplicable;
++                            span_lint_and_sugg(
++                                cx,
++                                BLOCK_IN_IF_CONDITION_EXPR,
++                                cond.span,
++                                BRACED_EXPR_MESSAGE,
++                                "try",
++                                format!(
++                                    "{}",
++                                    snippet_block_with_applicability(
++                                        cx,
++                                        ex.span,
++                                        "..",
++                                        Some(expr.span),
++                                        &mut applicability
++                                    )
++                                ),
++                                applicability,
++                            );
++                        }
++                    } else {
++                        let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
++                        if span.from_expansion() || differing_macro_contexts(expr.span, span) {
++                            return;
++                        }
++                        // move block higher
++                        let mut applicability = Applicability::MachineApplicable;
++                        span_lint_and_sugg(
++                            cx,
++                            BLOCK_IN_IF_CONDITION_STMT,
++                            expr.span.with_hi(cond.span.hi()),
++                            COMPLEX_BLOCK_MESSAGE,
++                            "try",
++                            format!(
++                                "let res = {}; if res",
++                                snippet_block_with_applicability(
++                                    cx,
++                                    block.span,
++                                    "..",
++                                    Some(expr.span),
++                                    &mut applicability
++                                ),
++                            ),
++                            applicability,
++                        );
++                    }
++                }
++            } else {
++                let mut visitor = ExVisitor { found_block: None, cx };
++                walk_expr(&mut visitor, cond);
++                if let Some(block) = visitor.found_block {
++                    span_lint(cx, BLOCK_IN_IF_CONDITION_STMT, block.span, COMPLEX_BLOCK_MESSAGE);
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8031052e073d5f8eacf8c45187ca7e0bf185ddb2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,499 @@@
++use crate::utils::{
++    get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt, span_lint_and_sugg,
++    span_lint_and_then, SpanlessEq,
++};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor};
++use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for boolean expressions that can be written more
++    /// concisely.
++    ///
++    /// **Why is this bad?** Readability of boolean expressions suffers from
++    /// unnecessary duplication.
++    ///
++    /// **Known problems:** Ignores short circuiting behavior of `||` and
++    /// `&&`. Ignores `|`, `&` and `^`.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// if a && true  // should be: if a
++    /// if !(a == b)  // should be: if a != b
++    /// ```
++    pub NONMINIMAL_BOOL,
++    complexity,
++    "boolean expressions that can be written more concisely"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for boolean expressions that contain terminals that
++    /// can be eliminated.
++    ///
++    /// **Why is this bad?** This is most likely a logic bug.
++    ///
++    /// **Known problems:** Ignores short circuiting behavior.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// if a && b || a { ... }
++    /// ```
++    /// The `b` is unnecessary, the expression is equivalent to `if a`.
++    pub LOGIC_BUG,
++    correctness,
++    "boolean expressions that contain terminals which can be eliminated"
++}
++
++// For each pairs, both orders are considered.
++const METHODS_WITH_NEGATION: [(&str, &str); 2] = [("is_some", "is_none"), ("is_err", "is_ok")];
++
++declare_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, LOGIC_BUG]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonminimalBool {
++    fn check_fn(
++        &mut self,
++        cx: &LateContext<'a, 'tcx>,
++        _: FnKind<'tcx>,
++        _: &'tcx FnDecl<'_>,
++        body: &'tcx Body<'_>,
++        _: Span,
++        _: HirId,
++    ) {
++        NonminimalBoolVisitor { cx }.visit_body(body)
++    }
++}
++
++struct NonminimalBoolVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++}
++
++use quine_mc_cluskey::Bool;
++struct Hir2Qmm<'a, 'tcx, 'v> {
++    terminals: Vec<&'v Expr<'v>>,
++    cx: &'a LateContext<'a, 'tcx>,
++}
++
++impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
++    fn extract(&mut self, op: BinOpKind, a: &[&'v Expr<'_>], mut v: Vec<Bool>) -> Result<Vec<Bool>, String> {
++        for a in a {
++            if let ExprKind::Binary(binop, lhs, rhs) = &a.kind {
++                if binop.node == op {
++                    v = self.extract(op, &[lhs, rhs], v)?;
++                    continue;
++                }
++            }
++            v.push(self.run(a)?);
++        }
++        Ok(v)
++    }
++
++    fn run(&mut self, e: &'v Expr<'_>) -> Result<Bool, String> {
++        fn negate(bin_op_kind: BinOpKind) -> Option<BinOpKind> {
++            match bin_op_kind {
++                BinOpKind::Eq => Some(BinOpKind::Ne),
++                BinOpKind::Ne => Some(BinOpKind::Eq),
++                BinOpKind::Gt => Some(BinOpKind::Le),
++                BinOpKind::Ge => Some(BinOpKind::Lt),
++                BinOpKind::Lt => Some(BinOpKind::Ge),
++                BinOpKind::Le => Some(BinOpKind::Gt),
++                _ => None,
++            }
++        }
++
++        // prevent folding of `cfg!` macros and the like
++        if !e.span.from_expansion() {
++            match &e.kind {
++                ExprKind::Unary(UnOp::UnNot, inner) => return Ok(Bool::Not(box self.run(inner)?)),
++                ExprKind::Binary(binop, lhs, rhs) => match &binop.node {
++                    BinOpKind::Or => return Ok(Bool::Or(self.extract(BinOpKind::Or, &[lhs, rhs], Vec::new())?)),
++                    BinOpKind::And => return Ok(Bool::And(self.extract(BinOpKind::And, &[lhs, rhs], Vec::new())?)),
++                    _ => (),
++                },
++                ExprKind::Lit(lit) => match lit.node {
++                    LitKind::Bool(true) => return Ok(Bool::True),
++                    LitKind::Bool(false) => return Ok(Bool::False),
++                    _ => (),
++                },
++                _ => (),
++            }
++        }
++        for (n, expr) in self.terminals.iter().enumerate() {
++            if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e, expr) {
++                #[allow(clippy::cast_possible_truncation)]
++                return Ok(Bool::Term(n as u8));
++            }
++
++            if_chain! {
++                if let ExprKind::Binary(e_binop, e_lhs, e_rhs) = &e.kind;
++                if implements_ord(self.cx, e_lhs);
++                if let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind;
++                if negate(e_binop.node) == Some(expr_binop.node);
++                if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_lhs, expr_lhs);
++                if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_rhs, expr_rhs);
++                then {
++                    #[allow(clippy::cast_possible_truncation)]
++                    return Ok(Bool::Not(Box::new(Bool::Term(n as u8))));
++                }
++            }
++        }
++        let n = self.terminals.len();
++        self.terminals.push(e);
++        if n < 32 {
++            #[allow(clippy::cast_possible_truncation)]
++            Ok(Bool::Term(n as u8))
++        } else {
++            Err("too many literals".to_owned())
++        }
++    }
++}
++
++struct SuggestContext<'a, 'tcx, 'v> {
++    terminals: &'v [&'v Expr<'v>],
++    cx: &'a LateContext<'a, 'tcx>,
++    output: String,
++}
++
++impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
++    fn recurse(&mut self, suggestion: &Bool) -> Option<()> {
++        use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True};
++        match suggestion {
++            True => {
++                self.output.push_str("true");
++            },
++            False => {
++                self.output.push_str("false");
++            },
++            Not(inner) => match **inner {
++                And(_) | Or(_) => {
++                    self.output.push('!');
++                    self.output.push('(');
++                    self.recurse(inner);
++                    self.output.push(')');
++                },
++                Term(n) => {
++                    let terminal = self.terminals[n as usize];
++                    if let Some(str) = simplify_not(self.cx, terminal) {
++                        self.output.push_str(&str)
++                    } else {
++                        self.output.push('!');
++                        let snip = snippet_opt(self.cx, terminal.span)?;
++                        self.output.push_str(&snip);
++                    }
++                },
++                True | False | Not(_) => {
++                    self.output.push('!');
++                    self.recurse(inner)?;
++                },
++            },
++            And(v) => {
++                for (index, inner) in v.iter().enumerate() {
++                    if index > 0 {
++                        self.output.push_str(" && ");
++                    }
++                    if let Or(_) = *inner {
++                        self.output.push('(');
++                        self.recurse(inner);
++                        self.output.push(')');
++                    } else {
++                        self.recurse(inner);
++                    }
++                }
++            },
++            Or(v) => {
++                for (index, inner) in v.iter().rev().enumerate() {
++                    if index > 0 {
++                        self.output.push_str(" || ");
++                    }
++                    self.recurse(inner);
++                }
++            },
++            &Term(n) => {
++                let snip = snippet_opt(self.cx, self.terminals[n as usize].span)?;
++                self.output.push_str(&snip);
++            },
++        }
++        Some(())
++    }
++}
++
++fn simplify_not(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option<String> {
++    match &expr.kind {
++        ExprKind::Binary(binop, lhs, rhs) => {
++            if !implements_ord(cx, lhs) {
++                return None;
++            }
++
++            match binop.node {
++                BinOpKind::Eq => Some(" != "),
++                BinOpKind::Ne => Some(" == "),
++                BinOpKind::Lt => Some(" >= "),
++                BinOpKind::Gt => Some(" <= "),
++                BinOpKind::Le => Some(" > "),
++                BinOpKind::Ge => Some(" < "),
++                _ => None,
++            }
++            .and_then(|op| {
++                Some(format!(
++                    "{}{}{}",
++                    snippet_opt(cx, lhs.span)?,
++                    op,
++                    snippet_opt(cx, rhs.span)?
++                ))
++            })
++        },
++        ExprKind::MethodCall(path, _, args) if args.len() == 1 => {
++            let type_of_receiver = cx.tables.expr_ty(&args[0]);
++            if !is_type_diagnostic_item(cx, type_of_receiver, sym!(option_type))
++                && !is_type_diagnostic_item(cx, type_of_receiver, sym!(result_type))
++            {
++                return None;
++            }
++            METHODS_WITH_NEGATION
++                .iter()
++                .cloned()
++                .flat_map(|(a, b)| vec![(a, b), (b, a)])
++                .find(|&(a, _)| {
++                    let path: &str = &path.ident.name.as_str();
++                    a == path
++                })
++                .and_then(|(_, neg_method)| Some(format!("{}.{}()", snippet_opt(cx, args[0].span)?, neg_method)))
++        },
++        _ => None,
++    }
++}
++
++fn suggest(cx: &LateContext<'_, '_>, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String {
++    let mut suggest_context = SuggestContext {
++        terminals,
++        cx,
++        output: String::new(),
++    };
++    suggest_context.recurse(suggestion);
++    suggest_context.output
++}
++
++fn simple_negate(b: Bool) -> Bool {
++    use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True};
++    match b {
++        True => False,
++        False => True,
++        t @ Term(_) => Not(Box::new(t)),
++        And(mut v) => {
++            for el in &mut v {
++                *el = simple_negate(::std::mem::replace(el, True));
++            }
++            Or(v)
++        },
++        Or(mut v) => {
++            for el in &mut v {
++                *el = simple_negate(::std::mem::replace(el, True));
++            }
++            And(v)
++        },
++        Not(inner) => *inner,
++    }
++}
++
++#[derive(Default)]
++struct Stats {
++    terminals: [usize; 32],
++    negations: usize,
++    ops: usize,
++}
++
++fn terminal_stats(b: &Bool) -> Stats {
++    fn recurse(b: &Bool, stats: &mut Stats) {
++        match b {
++            True | False => stats.ops += 1,
++            Not(inner) => {
++                match **inner {
++                    And(_) | Or(_) => stats.ops += 1, // brackets are also operations
++                    _ => stats.negations += 1,
++                }
++                recurse(inner, stats);
++            },
++            And(v) | Or(v) => {
++                stats.ops += v.len() - 1;
++                for inner in v {
++                    recurse(inner, stats);
++                }
++            },
++            &Term(n) => stats.terminals[n as usize] += 1,
++        }
++    }
++    use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True};
++    let mut stats = Stats::default();
++    recurse(b, &mut stats);
++    stats
++}
++
++impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
++    fn bool_expr(&self, e: &'tcx Expr<'_>) {
++        let mut h2q = Hir2Qmm {
++            terminals: Vec::new(),
++            cx: self.cx,
++        };
++        if let Ok(expr) = h2q.run(e) {
++            if h2q.terminals.len() > 8 {
++                // QMC has exponentially slow behavior as the number of terminals increases
++                // 8 is reasonable, it takes approximately 0.2 seconds.
++                // See #825
++                return;
++            }
++
++            let stats = terminal_stats(&expr);
++            let mut simplified = expr.simplify();
++            for simple in Bool::Not(Box::new(expr)).simplify() {
++                match simple {
++                    Bool::Not(_) | Bool::True | Bool::False => {},
++                    _ => simplified.push(Bool::Not(Box::new(simple.clone()))),
++                }
++                let simple_negated = simple_negate(simple);
++                if simplified.iter().any(|s| *s == simple_negated) {
++                    continue;
++                }
++                simplified.push(simple_negated);
++            }
++            let mut improvements = Vec::with_capacity(simplified.len());
++            'simplified: for suggestion in &simplified {
++                let simplified_stats = terminal_stats(suggestion);
++                let mut improvement = false;
++                for i in 0..32 {
++                    // ignore any "simplifications" that end up requiring a terminal more often
++                    // than in the original expression
++                    if stats.terminals[i] < simplified_stats.terminals[i] {
++                        continue 'simplified;
++                    }
++                    if stats.terminals[i] != 0 && simplified_stats.terminals[i] == 0 {
++                        span_lint_and_then(
++                            self.cx,
++                            LOGIC_BUG,
++                            e.span,
++                            "this boolean expression contains a logic bug",
++                            |diag| {
++                                diag.span_help(
++                                    h2q.terminals[i].span,
++                                    "this expression can be optimized out by applying boolean operations to the \
++                                     outer expression",
++                                );
++                                diag.span_suggestion(
++                                    e.span,
++                                    "it would look like the following",
++                                    suggest(self.cx, suggestion, &h2q.terminals),
++                                    // nonminimal_bool can produce minimal but
++                                    // not human readable expressions (#3141)
++                                    Applicability::Unspecified,
++                                );
++                            },
++                        );
++                        // don't also lint `NONMINIMAL_BOOL`
++                        return;
++                    }
++                    // if the number of occurrences of a terminal decreases or any of the stats
++                    // decreases while none increases
++                    improvement |= (stats.terminals[i] > simplified_stats.terminals[i])
++                        || (stats.negations > simplified_stats.negations && stats.ops == simplified_stats.ops)
++                        || (stats.ops > simplified_stats.ops && stats.negations == simplified_stats.negations);
++                }
++                if improvement {
++                    improvements.push(suggestion);
++                }
++            }
++            let nonminimal_bool_lint = |suggestions: Vec<_>| {
++                span_lint_and_then(
++                    self.cx,
++                    NONMINIMAL_BOOL,
++                    e.span,
++                    "this boolean expression can be simplified",
++                    |diag| {
++                        diag.span_suggestions(
++                            e.span,
++                            "try",
++                            suggestions.into_iter(),
++                            // nonminimal_bool can produce minimal but
++                            // not human readable expressions (#3141)
++                            Applicability::Unspecified,
++                        );
++                    },
++                );
++            };
++            if improvements.is_empty() {
++                let mut visitor = NotSimplificationVisitor { cx: self.cx };
++                visitor.visit_expr(e);
++            } else {
++                nonminimal_bool_lint(
++                    improvements
++                        .into_iter()
++                        .map(|suggestion| suggest(self.cx, suggestion, &h2q.terminals))
++                        .collect(),
++                );
++            }
++        }
++    }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
++        if in_macro(e.span) {
++            return;
++        }
++        match &e.kind {
++            ExprKind::Binary(binop, _, _) if binop.node == BinOpKind::Or || binop.node == BinOpKind::And => {
++                self.bool_expr(e)
++            },
++            ExprKind::Unary(UnOp::UnNot, inner) => {
++                if self.cx.tables.node_types()[inner.hir_id].is_bool() {
++                    self.bool_expr(e);
++                } else {
++                    walk_expr(self, e);
++                }
++            },
++            _ => walk_expr(self, e),
++        }
++    }
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++fn implements_ord<'a, 'tcx>(cx: &'a LateContext<'a, 'tcx>, expr: &Expr<'_>) -> bool {
++    let ty = cx.tables.expr_ty(expr);
++    get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[]))
++}
++
++struct NotSimplificationVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        if let ExprKind::Unary(UnOp::UnNot, inner) = &expr.kind {
++            if let Some(suggestion) = simplify_not(self.cx, inner) {
++                span_lint_and_sugg(
++                    self.cx,
++                    NONMINIMAL_BOOL,
++                    expr.span,
++                    "this boolean expression can be simplified",
++                    "try",
++                    suggestion,
++                    Applicability::MachineApplicable,
++                );
++            }
++        }
++
++        walk_expr(self, expr);
++    }
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..91d3e47d7870c21ab7f6034e0d9c7f56dfeac100
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,117 @@@
++use crate::utils::{
++    contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability,
++    span_lint_and_sugg, walk_ptrs_ty,
++};
++use if_chain::if_chain;
++use rustc_ast::ast::{Name, UintTy};
++use rustc_errors::Applicability;
++use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for naive byte counts
++    ///
++    /// **Why is this bad?** The [`bytecount`](https://crates.io/crates/bytecount)
++    /// crate has methods to count your bytes faster, especially for large slices.
++    ///
++    /// **Known problems:** If you have predominantly small slices, the
++    /// `bytecount::count(..)` method may actually be slower. However, if you can
++    /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
++    /// faster in those cases.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// # let vec = vec![1_u8];
++    /// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead
++    /// ```
++    pub NAIVE_BYTECOUNT,
++    perf,
++    "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
++}
++
++declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ByteCount {
++    fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++        if_chain! {
++            if let ExprKind::MethodCall(ref count, _, ref count_args) = expr.kind;
++            if count.ident.name == sym!(count);
++            if count_args.len() == 1;
++            if let ExprKind::MethodCall(ref filter, _, ref filter_args) = count_args[0].kind;
++            if filter.ident.name == sym!(filter);
++            if filter_args.len() == 2;
++            if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind;
++            then {
++                let body = cx.tcx.hir().body(body_id);
++                if_chain! {
++                    if body.params.len() == 1;
++                    if let Some(argname) = get_pat_name(&body.params[0].pat);
++                    if let ExprKind::Binary(ref op, ref l, ref r) = body.value.kind;
++                    if op.node == BinOpKind::Eq;
++                    if match_type(cx,
++                               walk_ptrs_ty(cx.tables.expr_ty(&filter_args[0])),
++                               &paths::SLICE_ITER);
++                    then {
++                        let needle = match get_path_name(l) {
++                            Some(name) if check_arg(name, argname, r) => r,
++                            _ => match get_path_name(r) {
++                                Some(name) if check_arg(name, argname, l) => l,
++                                _ => { return; }
++                            }
++                        };
++                        if ty::Uint(UintTy::U8) != walk_ptrs_ty(cx.tables.expr_ty(needle)).kind {
++                            return;
++                        }
++                        let haystack = if let ExprKind::MethodCall(ref path, _, ref args) =
++                                filter_args[0].kind {
++                            let p = path.ident.name;
++                            if (p == sym!(iter) || p == sym!(iter_mut)) && args.len() == 1 {
++                                &args[0]
++                            } else {
++                                &filter_args[0]
++                            }
++                        } else {
++                            &filter_args[0]
++                        };
++                        let mut applicability = Applicability::MaybeIncorrect;
++                        span_lint_and_sugg(
++                            cx,
++                            NAIVE_BYTECOUNT,
++                            expr.span,
++                            "You appear to be counting bytes the naive way",
++                            "Consider using the bytecount crate",
++                            format!("bytecount::count({}, {})",
++                                    snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
++                                    snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
++                            applicability,
++                        );
++                    }
++                };
++            }
++        };
++    }
++}
++
++fn check_arg(name: Name, arg: Name, needle: &Expr<'_>) -> bool {
++    name == arg && !contains_name(name, needle)
++}
++
++fn get_path_name(expr: &Expr<'_>) -> Option<Name> {
++    match expr.kind {
++        ExprKind::Box(ref e) | ExprKind::AddrOf(BorrowKind::Ref, _, ref e) | ExprKind::Unary(UnOp::UnDeref, ref e) => {
++            get_path_name(e)
++        },
++        ExprKind::Block(ref b, _) => {
++            if b.stmts.is_empty() {
++                b.expr.as_ref().and_then(|p| get_path_name(p))
++            } else {
++                None
++            }
++        },
++        ExprKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
++        _ => None,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..782da249808d0e71b3af319d9ef86cdb66236338
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,105 @@@
++//! lint on missing cargo common metadata
++
++use std::path::PathBuf;
++
++use crate::utils::{run_lints, span_lint};
++use rustc_hir::{hir_id::CRATE_HIR_ID, Crate};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::DUMMY_SP;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks to see if all common metadata is defined in
++    /// `Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata
++    ///
++    /// **Why is this bad?** It will be more difficult for users to discover the
++    /// purpose of the crate, and key information related to it.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```toml
++    /// # This `Cargo.toml` is missing an authors field:
++    /// [package]
++    /// name = "clippy"
++    /// version = "0.0.212"
++    /// description = "A bunch of helpful lints to avoid common pitfalls in Rust"
++    /// repository = "https://github.com/rust-lang/rust-clippy"
++    /// readme = "README.md"
++    /// license = "MIT OR Apache-2.0"
++    /// keywords = ["clippy", "lint", "plugin"]
++    /// categories = ["development-tools", "development-tools::cargo-plugins"]
++    /// ```
++    pub CARGO_COMMON_METADATA,
++    cargo,
++    "common metadata is defined in `Cargo.toml`"
++}
++
++fn warning(cx: &LateContext<'_, '_>, message: &str) {
++    span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, message);
++}
++
++fn missing_warning(cx: &LateContext<'_, '_>, package: &cargo_metadata::Package, field: &str) {
++    let message = format!("package `{}` is missing `{}` metadata", package.name, field);
++    warning(cx, &message);
++}
++
++fn is_empty_str(value: &Option<String>) -> bool {
++    value.as_ref().map_or(true, String::is_empty)
++}
++
++fn is_empty_path(value: &Option<PathBuf>) -> bool {
++    value.as_ref().and_then(|x| x.to_str()).map_or(true, str::is_empty)
++}
++
++fn is_empty_vec(value: &[String]) -> bool {
++    // This works because empty iterators return true
++    value.iter().all(String::is_empty)
++}
++
++declare_lint_pass!(CargoCommonMetadata => [CARGO_COMMON_METADATA]);
++
++impl LateLintPass<'_, '_> for CargoCommonMetadata {
++    fn check_crate(&mut self, cx: &LateContext<'_, '_>, _: &Crate<'_>) {
++        if !run_lints(cx, &[CARGO_COMMON_METADATA], CRATE_HIR_ID) {
++            return;
++        }
++
++        let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().no_deps().exec() {
++            metadata
++        } else {
++            warning(cx, "could not read cargo metadata");
++            return;
++        };
++
++        for package in metadata.packages {
++            if is_empty_vec(&package.authors) {
++                missing_warning(cx, &package, "package.authors");
++            }
++
++            if is_empty_str(&package.description) {
++                missing_warning(cx, &package, "package.description");
++            }
++
++            if is_empty_str(&package.license) && is_empty_path(&package.license_file) {
++                missing_warning(cx, &package, "either package.license or package.license_file");
++            }
++
++            if is_empty_str(&package.repository) {
++                missing_warning(cx, &package, "package.repository");
++            }
++
++            if is_empty_path(&package.readme) {
++                missing_warning(cx, &package, "package.readme");
++            }
++
++            if is_empty_vec(&package.keywords) {
++                missing_warning(cx, &package, "package.keywords");
++            }
++
++            if is_empty_vec(&package.categories) {
++                missing_warning(cx, &package, "package.categories");
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d9776dd50a836add3ab5635534b41bbe225c6180
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,345 @@@
++//! lint on manually implemented checked conversions that could be transformed into `try_from`
++
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
++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::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for explicit bounds checking when casting.
++    ///
++    /// **Why is this bad?** Reduces the readability of statements & is error prone.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let foo: u32 = 5;
++    /// # let _ =
++    /// foo <= i32::MAX as u32
++    /// # ;
++    /// ```
++    ///
++    /// Could be written:
++    ///
++    /// ```rust
++    /// # use std::convert::TryFrom;
++    /// # let foo = 1;
++    /// # let _ =
++    /// i32::try_from(foo).is_ok()
++    /// # ;
++    /// ```
++    pub CHECKED_CONVERSIONS,
++    pedantic,
++    "`try_from` could replace manual bounds checking when casting"
++}
++
++declare_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CheckedConversions {
++    fn check_expr(&mut self, cx: &LateContext<'_, '_>, item: &Expr<'_>) {
++        let result = if_chain! {
++            if !in_external_macro(cx.sess(), item.span);
++            if let ExprKind::Binary(op, ref left, ref right) = &item.kind;
++
++            then {
++                match op.node {
++                    BinOpKind::Ge | BinOpKind::Le => single_check(item),
++                    BinOpKind::And => double_check(cx, left, right),
++                    _ => None,
++                }
++            } else {
++                None
++            }
++        };
++
++        if_chain! {
++            if let Some(cv) = result;
++            if let Some(to_type) = cv.to_type;
++
++            then {
++                let mut applicability = Applicability::MachineApplicable;
++                let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut
++                                applicability);
++                span_lint_and_sugg(
++                    cx,
++                    CHECKED_CONVERSIONS,
++                    item.span,
++                    "Checked cast can be simplified.",
++                    "try",
++                    format!("{}::try_from({}).is_ok()",
++                            to_type,
++                            snippet),
++                    applicability
++                );
++            }
++        }
++    }
++}
++
++/// Searches for a single check from unsigned to _ is done
++/// todo: check for case signed -> larger unsigned == only x >= 0
++fn single_check<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
++    check_upper_bound(expr).filter(|cv| cv.cvt == ConversionType::FromUnsigned)
++}
++
++/// Searches for a combination of upper & lower bound checks
++fn double_check<'a>(cx: &LateContext<'_, '_>, left: &'a Expr<'_>, right: &'a Expr<'_>) -> Option<Conversion<'a>> {
++    let upper_lower = |l, r| {
++        let upper = check_upper_bound(l);
++        let lower = check_lower_bound(r);
++
++        transpose(upper, lower).and_then(|(l, r)| l.combine(r, cx))
++    };
++
++    upper_lower(left, right).or_else(|| upper_lower(right, left))
++}
++
++/// Contains the result of a tried conversion check
++#[derive(Clone, Debug)]
++struct Conversion<'a> {
++    cvt: ConversionType,
++    expr_to_cast: &'a Expr<'a>,
++    to_type: Option<&'a str>,
++}
++
++/// The kind of conversion that is checked
++#[derive(Copy, Clone, Debug, PartialEq)]
++enum ConversionType {
++    SignedToUnsigned,
++    SignedToSigned,
++    FromUnsigned,
++}
++
++impl<'a> Conversion<'a> {
++    /// Combine multiple conversions if the are compatible
++    pub fn combine(self, other: Self, cx: &LateContext<'_, '_>) -> Option<Conversion<'a>> {
++        if self.is_compatible(&other, cx) {
++            // Prefer a Conversion that contains a type-constraint
++            Some(if self.to_type.is_some() { self } else { other })
++        } else {
++            None
++        }
++    }
++
++    /// Checks if two conversions are compatible
++    /// same type of conversion, same 'castee' and same 'to type'
++    pub fn is_compatible(&self, other: &Self, cx: &LateContext<'_, '_>) -> bool {
++        (self.cvt == other.cvt)
++            && (SpanlessEq::new(cx).eq_expr(self.expr_to_cast, other.expr_to_cast))
++            && (self.has_compatible_to_type(other))
++    }
++
++    /// Checks if the to-type is the same (if there is a type constraint)
++    fn has_compatible_to_type(&self, other: &Self) -> bool {
++        transpose(self.to_type.as_ref(), other.to_type.as_ref()).map_or(true, |(l, r)| l == r)
++    }
++
++    /// Try to construct a new conversion if the conversion type is valid
++    fn try_new(expr_to_cast: &'a Expr<'_>, from_type: &str, to_type: &'a str) -> Option<Conversion<'a>> {
++        ConversionType::try_new(from_type, to_type).map(|cvt| Conversion {
++            cvt,
++            expr_to_cast,
++            to_type: Some(to_type),
++        })
++    }
++
++    /// Construct a new conversion without type constraint
++    fn new_any(expr_to_cast: &'a Expr<'_>) -> Conversion<'a> {
++        Conversion {
++            cvt: ConversionType::SignedToUnsigned,
++            expr_to_cast,
++            to_type: None,
++        }
++    }
++}
++
++impl ConversionType {
++    /// Creates a conversion type if the type is allowed & conversion is valid
++    #[must_use]
++    fn try_new(from: &str, to: &str) -> Option<Self> {
++        if UINTS.contains(&from) {
++            Some(Self::FromUnsigned)
++        } else if SINTS.contains(&from) {
++            if UINTS.contains(&to) {
++                Some(Self::SignedToUnsigned)
++            } else if SINTS.contains(&to) {
++                Some(Self::SignedToSigned)
++            } else {
++                None
++            }
++        } else {
++            None
++        }
++    }
++}
++
++/// Check for `expr <= (to_type::MAX as from_type)`
++fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
++    if_chain! {
++         if let ExprKind::Binary(ref op, ref left, ref right) = &expr.kind;
++         if let Some((candidate, check)) = normalize_le_ge(op, left, right);
++         if let Some((from, to)) = get_types_from_cast(check, MAX_VALUE, INTS);
++
++         then {
++             Conversion::try_new(candidate, from, to)
++         } else {
++            None
++        }
++    }
++}
++
++/// Check for `expr >= 0|(to_type::MIN as from_type)`
++fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
++    fn check_function<'a>(candidate: &'a Expr<'a>, check: &'a Expr<'a>) -> Option<Conversion<'a>> {
++        (check_lower_bound_zero(candidate, check)).or_else(|| (check_lower_bound_min(candidate, check)))
++    }
++
++    // First of we need a binary containing the expression & the cast
++    if let ExprKind::Binary(ref op, ref left, ref right) = &expr.kind {
++        normalize_le_ge(op, right, left).and_then(|(l, r)| check_function(l, r))
++    } else {
++        None
++    }
++}
++
++/// Check for `expr >= 0`
++fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option<Conversion<'a>> {
++    if_chain! {
++        if let ExprKind::Lit(ref lit) = &check.kind;
++        if let LitKind::Int(0, _) = &lit.node;
++
++        then {
++            Some(Conversion::new_any(candidate))
++        } else {
++            None
++        }
++    }
++}
++
++/// Check for `expr >= (to_type::MIN as from_type)`
++fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option<Conversion<'a>> {
++    if let Some((from, to)) = get_types_from_cast(check, MIN_VALUE, SINTS) {
++        Conversion::try_new(candidate, from, to)
++    } else {
++        None
++    }
++}
++
++/// Tries to extract the from- and to-type from a cast expression
++fn get_types_from_cast<'a>(expr: &'a Expr<'_>, func: &'a str, types: &'a [&str]) -> Option<(&'a str, &'a str)> {
++    // `to_type::maxmin_value() as from_type`
++    let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! {
++        // to_type::maxmin_value(), from_type
++        if let ExprKind::Cast(ref limit, ref from_type) = &expr.kind;
++        if let TyKind::Path(ref from_type_path) = &from_type.kind;
++        if let Some(from_sym) = int_ty_to_sym(from_type_path);
++
++        then {
++            Some((limit, from_sym))
++        } else {
++            None
++        }
++    };
++
++    // `from_type::from(to_type::maxmin_value())`
++    let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| {
++        if_chain! {
++            // `from_type::from, to_type::maxmin_value()`
++            if let ExprKind::Call(ref from_func, ref args) = &expr.kind;
++            // `to_type::maxmin_value()`
++            if args.len() == 1;
++            if let limit = &args[0];
++            // `from_type::from`
++            if let ExprKind::Path(ref path) = &from_func.kind;
++            if let Some(from_sym) = get_implementing_type(path, INTS, FROM);
++
++            then {
++                Some((limit, from_sym))
++            } else {
++                None
++            }
++        }
++    });
++
++    if let Some((limit, from_type)) = limit_from {
++        if_chain! {
++            if let ExprKind::Call(ref fun_name, _) = &limit.kind;
++            // `to_type, maxmin_value`
++            if let ExprKind::Path(ref path) = &fun_name.kind;
++            // `to_type`
++            if let Some(to_type) = get_implementing_type(path, types, func);
++
++            then {
++                Some((from_type, to_type))
++            } else {
++                None
++            }
++        }
++    } else {
++        None
++    }
++}
++
++/// Gets the type which implements the called function
++fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: &str) -> Option<&'a str> {
++    if_chain! {
++        if let QPath::TypeRelative(ref ty, ref path) = &path;
++        if path.ident.name.as_str() == function;
++        if let TyKind::Path(QPath::Resolved(None, ref tp)) = &ty.kind;
++        if let [int] = &*tp.segments;
++        let name = &int.ident.name.as_str();
++
++        then {
++            candidates.iter().find(|c| name == *c).cloned()
++        } else {
++            None
++        }
++    }
++}
++
++/// Gets the type as a string, if it is a supported integer
++fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> {
++    if_chain! {
++        if let QPath::Resolved(_, ref path) = *path;
++        if let [ty] = &*path.segments;
++        let name = &ty.ident.name.as_str();
++
++        then {
++            INTS.iter().find(|c| name == *c).cloned()
++        } else {
++            None
++        }
++    }
++}
++
++/// (Option<T>, Option<U>) -> Option<(T, U)>
++fn transpose<T, U>(lhs: Option<T>, rhs: Option<U>) -> Option<(T, U)> {
++    match (lhs, rhs) {
++        (Some(l), Some(r)) => Some((l, r)),
++        _ => None,
++    }
++}
++
++/// Will return the expressions as if they were expr1 <= expr2
++fn normalize_le_ge<'a>(op: &BinOp, left: &'a Expr<'a>, right: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
++    match op.node {
++        BinOpKind::Le => Some((left, right)),
++        BinOpKind::Ge => Some((right, left)),
++        _ => None,
++    }
++}
++
++// Constants
++const FROM: &str = "from";
++const MAX_VALUE: &str = "max_value";
++const MIN_VALUE: &str = "min_value";
++
++const UINTS: &[&str] = &["u8", "u16", "u32", "u64", "usize"];
++const SINTS: &[&str] = &["i8", "i16", "i32", "i64", "isize"];
++const INTS: &[&str] = &["u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize"];
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3ba72e84fa827a7dace8698873b845b815fe1ae6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,163 @@@
++//! calculate cognitive complexity and warn about overly complex functions
++
++use rustc_ast::ast::Attribute;
++use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor};
++use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++use rustc_span::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.
++    ///
++    /// **Why is this bad?** Methods of high cognitive complexity tend to be hard to
++    /// both read and maintain. Also LLVM will tend to optimize small methods better.
++    ///
++    /// **Known problems:** Sometimes it's hard to find a way to reduce the
++    /// complexity.
++    ///
++    /// **Example:** No. You'll see it when you get the warning.
++    pub COGNITIVE_COMPLEXITY,
++    nursery,
++    "functions that should be split up into multiple functions"
++}
++
++pub struct CognitiveComplexity {
++    limit: LimitStack,
++}
++
++impl CognitiveComplexity {
++    #[must_use]
++    pub fn new(limit: u64) -> Self {
++        Self {
++            limit: LimitStack::new(limit),
++        }
++    }
++}
++
++impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]);
++
++impl CognitiveComplexity {
++    #[allow(clippy::cast_possible_truncation)]
++    fn check<'a, 'tcx>(
++        &mut self,
++        cx: &'a LateContext<'a, 'tcx>,
++        kind: FnKind<'tcx>,
++        decl: &'tcx FnDecl<'_>,
++        body: &'tcx Body<'_>,
++        body_span: Span,
++    ) {
++        if body_span.from_expansion() {
++            return;
++        }
++
++        let expr = &body.value;
++
++        let mut helper = CCHelper { cc: 1, returns: 0 };
++        helper.visit_expr(expr);
++        let CCHelper { cc, returns } = helper;
++        let ret_ty = cx.tables.node_type(expr.hir_id);
++        let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym!(result_type)) {
++            returns
++        } else {
++            #[allow(clippy::integer_division)]
++            (returns / 2)
++        };
++
++        let mut rust_cc = cc;
++        // prevent degenerate cases where unreachable code contains `return` statements
++        if rust_cc >= ret_adjust {
++            rust_cc -= ret_adjust;
++        }
++
++        if rust_cc > self.limit.limit() {
++            let fn_span = match kind {
++                FnKind::ItemFn(ident, _, _, _, _) | FnKind::Method(ident, _, _, _) => ident.span,
++                FnKind::Closure(_) => {
++                    let header_span = body_span.with_hi(decl.output.span().lo());
++                    let pos = snippet_opt(cx, header_span).and_then(|snip| {
++                        let low_offset = snip.find('|')?;
++                        let high_offset = 1 + snip.get(low_offset + 1..)?.find('|')?;
++                        let low = header_span.lo() + BytePos(low_offset as u32);
++                        let high = low + BytePos(high_offset as u32 + 1);
++
++                        Some((low, high))
++                    });
++
++                    if let Some((low, high)) = pos {
++                        Span::new(low, high, header_span.ctxt())
++                    } else {
++                        return;
++                    }
++                },
++            };
++
++            span_lint_and_help(
++                cx,
++                COGNITIVE_COMPLEXITY,
++                fn_span,
++                &format!(
++                    "the function has a cognitive complexity of ({}/{})",
++                    rust_cc,
++                    self.limit.limit()
++                ),
++                None,
++                "you could split it up into multiple smaller functions",
++            );
++        }
++    }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CognitiveComplexity {
++    fn check_fn(
++        &mut self,
++        cx: &LateContext<'a, 'tcx>,
++        kind: FnKind<'tcx>,
++        decl: &'tcx FnDecl<'_>,
++        body: &'tcx Body<'_>,
++        span: Span,
++        hir_id: HirId,
++    ) {
++        let def_id = cx.tcx.hir().local_def_id(hir_id);
++        if !cx.tcx.has_attr(def_id.to_def_id(), sym!(test)) {
++            self.check(cx, kind, decl, body, span);
++        }
++    }
++
++    fn enter_lint_attrs(&mut self, cx: &LateContext<'a, 'tcx>, attrs: &'tcx [Attribute]) {
++        self.limit.push_attrs(cx.sess(), attrs, "cognitive_complexity");
++    }
++    fn exit_lint_attrs(&mut self, cx: &LateContext<'a, 'tcx>, attrs: &'tcx [Attribute]) {
++        self.limit.pop_attrs(cx.sess(), attrs, "cognitive_complexity");
++    }
++}
++
++struct CCHelper {
++    cc: u64,
++    returns: u64,
++}
++
++impl<'tcx> Visitor<'tcx> for CCHelper {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
++        walk_expr(self, e);
++        match e.kind {
++            ExprKind::Match(_, ref arms, _) => {
++                if arms.len() > 1 {
++                    self.cc += 1;
++                }
++                self.cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64;
++            },
++            ExprKind::Ret(_) => self.returns += 1,
++            _ => {},
++        }
++    }
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8090f4673aae0e225f5507869893c6cfcd4c639b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,170 @@@
++//! Checks for if expressions that contain only an if expression.
++//!
++//! For example, the lint would catch:
++//!
++//! ```rust,ignore
++//! if x {
++//!     if y {
++//!         println!("Hello world");
++//!     }
++//! }
++//! ```
++//!
++//! This lint is **warn** by default
++
++use if_chain::if_chain;
++use rustc_ast::ast;
++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 and for `else { if ... }` expressions
++    /// that
++    /// can be collapsed to `else if ...`.
++    ///
++    /// **Why is this bad?** Each `if`-statement adds one level of nesting, which
++    /// makes code look more complex than it really is.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// if x {
++    ///     if y {
++    ///         …
++    ///     }
++    /// }
++    ///
++    /// // or
++    ///
++    /// if x {
++    ///     …
++    /// } else {
++    ///     if y {
++    ///         …
++    ///     }
++    /// }
++    /// ```
++    ///
++    /// Should be written:
++    ///
++    /// ```rust.ignore
++    /// if x && y {
++    ///     …
++    /// }
++    ///
++    /// // or
++    ///
++    /// if x {
++    ///     …
++    /// } else if y {
++    ///     …
++    /// }
++    /// ```
++    pub COLLAPSIBLE_IF,
++    style,
++    "`if`s that can be collapsed (e.g., `if x { if y { ... } }` and `else { if x { ... } }`)"
++}
++
++declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF]);
++
++impl EarlyLintPass for CollapsibleIf {
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
++        if !expr.span.from_expansion() {
++            check_if(cx, expr)
++        }
++    }
++}
++
++fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
++    if let ast::ExprKind::If(check, then, else_) = &expr.kind {
++        if let Some(else_) = else_ {
++            check_collapsible_maybe_if_let(cx, else_);
++        } else if let ast::ExprKind::Let(..) = check.kind {
++            // Prevent triggering on `if let a = b { if c { .. } }`.
++        } else {
++            check_collapsible_no_if_let(cx, expr, check, then);
++        }
++    }
++}
++
++fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool {
++    // We trim all opening braces and whitespaces and then check if the next string is a comment.
++    let trimmed_block_text = snippet_block(cx, expr.span, "..", None)
++        .trim_start_matches(|c: char| c.is_whitespace() || c == '{')
++        .to_owned();
++    trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*")
++}
++
++fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
++    if_chain! {
++        if let ast::ExprKind::Block(ref block, _) = else_.kind;
++        if !block_starts_with_comment(cx, block);
++        if let Some(else_) = expr_block(block);
++        if !else_.span.from_expansion();
++        if let ast::ExprKind::If(..) = else_.kind;
++        then {
++            let mut applicability = Applicability::MachineApplicable;
++            span_lint_and_sugg(
++                cx,
++                COLLAPSIBLE_IF,
++                block.span,
++                "this `else { if .. }` block can be collapsed",
++                "try",
++                snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(),
++                applicability,
++            );
++        }
++    }
++}
++
++fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) {
++    if_chain! {
++        if !block_starts_with_comment(cx, then);
++        if let Some(inner) = expr_block(then);
++        if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind;
++        then {
++            if let ast::ExprKind::Let(..) = check_inner.kind {
++                // Prevent triggering on `if c { if let a = b { .. } }`.
++                return;
++            }
++
++            if expr.span.ctxt() != inner.span.ctxt() {
++                return;
++            }
++            span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| {
++                let lhs = Sugg::ast(cx, check, "..");
++                let rhs = Sugg::ast(cx, check_inner, "..");
++                diag.span_suggestion(
++                    expr.span,
++                    "try",
++                    format!(
++                        "if {} {}",
++                        lhs.and(&rhs),
++                        snippet_block(cx, content.span, "..", Some(expr.span)),
++                    ),
++                    Applicability::MachineApplicable, // snippet
++                );
++            });
++        }
++    }
++}
++
++/// If the block contains only one expression, return it.
++fn expr_block(block: &ast::Block) -> Option<&ast::Expr> {
++    let mut it = block.stmts.iter();
++
++    if let (Some(stmt), None) = (it.next(), it.next()) {
++        match stmt.kind {
++            ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => Some(expr),
++            _ => None,
++        }
++    } else {
++        None
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..96df3ffe3ce6727650e8da1c95448c046aab2f74
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,118 @@@
++use crate::utils::{
++    get_trait_def_id, if_sequence, implements_trait, parent_node_is_if_expr, paths, span_lint_and_help, SpanlessEq,
++};
++use rustc_hir::{BinOpKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks comparison chains written with `if` that can be
++    /// rewritten with `match` and `cmp`.
++    ///
++    /// **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get
++    /// repetitive
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// # fn a() {}
++    /// # fn b() {}
++    /// # fn c() {}
++    /// fn f(x: u8, y: u8) {
++    ///     if x > y {
++    ///         a()
++    ///     } else if x < y {
++    ///         b()
++    ///     } else {
++    ///         c()
++    ///     }
++    /// }
++    /// ```
++    ///
++    /// Could be written:
++    ///
++    /// ```rust,ignore
++    /// use std::cmp::Ordering;
++    /// # fn a() {}
++    /// # fn b() {}
++    /// # fn c() {}
++    /// fn f(x: u8, y: u8) {
++    ///      match x.cmp(&y) {
++    ///          Ordering::Greater => a(),
++    ///          Ordering::Less => b(),
++    ///          Ordering::Equal => c()
++    ///      }
++    /// }
++    /// ```
++    pub COMPARISON_CHAIN,
++    style,
++    "`if`s that can be rewritten with `match` and `cmp`"
++}
++
++declare_lint_pass!(ComparisonChain => [COMPARISON_CHAIN]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ComparisonChain {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if expr.span.from_expansion() {
++            return;
++        }
++
++        // We only care about the top-most `if` in the chain
++        if parent_node_is_if_expr(expr, cx) {
++            return;
++        }
++
++        // Check that there exists at least one explicit else condition
++        let (conds, _) = if_sequence(expr);
++        if conds.len() < 2 {
++            return;
++        }
++
++        for cond in conds.windows(2) {
++            if let (
++                &ExprKind::Binary(ref kind1, ref lhs1, ref rhs1),
++                &ExprKind::Binary(ref kind2, ref lhs2, ref rhs2),
++            ) = (&cond[0].kind, &cond[1].kind)
++            {
++                if !kind_is_cmp(kind1.node) || !kind_is_cmp(kind2.node) {
++                    return;
++                }
++
++                // Check that both sets of operands are equal
++                let mut spanless_eq = SpanlessEq::new(cx);
++                if (!spanless_eq.eq_expr(lhs1, lhs2) || !spanless_eq.eq_expr(rhs1, rhs2))
++                    && (!spanless_eq.eq_expr(lhs1, rhs2) || !spanless_eq.eq_expr(rhs1, lhs2))
++                {
++                    return;
++                }
++
++                // Check that the type being compared implements `core::cmp::Ord`
++                let ty = cx.tables.expr_ty(lhs1);
++                let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[]));
++
++                if !is_ord {
++                    return;
++                }
++            } else {
++                // We only care about comparison chains
++                return;
++            }
++        }
++        span_lint_and_help(
++            cx,
++            COMPARISON_CHAIN,
++            expr.span,
++            "`if` chain can be rewritten with `match`",
++            None,
++            "Consider rewriting the `if` chain to use `cmp` and `match`.",
++        )
++    }
++}
++
++fn kind_is_cmp(kind: BinOpKind) -> bool {
++    match kind {
++        BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq => true,
++        _ => false,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..81ddc8c0067c7184dd0e35af2bd5129724e2e7d2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,559 @@@
++#![allow(clippy::float_cmp)]
++
++use crate::utils::{clip, higher, sext, unsext};
++use if_chain::if_chain;
++use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
++use rustc_data_structures::sync::Lrc;
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp};
++use rustc_lint::LateContext;
++use rustc_middle::ty::subst::{Subst, SubstsRef};
++use rustc_middle::ty::{self, Ty, TyCtxt};
++use rustc_middle::{bug, span_bug};
++use rustc_span::symbol::Symbol;
++use std::cmp::Ordering::{self, Equal};
++use std::convert::TryInto;
++use std::hash::{Hash, Hasher};
++
++/// A `LitKind`-like enum to fold constant `Expr`s into.
++#[derive(Debug, Clone)]
++pub enum Constant {
++    /// A `String` (e.g., "abc").
++    Str(String),
++    /// A binary string (e.g., `b"abc"`).
++    Binary(Lrc<Vec<u8>>),
++    /// A single `char` (e.g., `'a'`).
++    Char(char),
++    /// An integer's bit representation.
++    Int(u128),
++    /// An `f32`.
++    F32(f32),
++    /// An `f64`.
++    F64(f64),
++    /// `true` or `false`.
++    Bool(bool),
++    /// An array of constants.
++    Vec(Vec<Constant>),
++    /// Also an array, but with only one constant, repeated N times.
++    Repeat(Box<Constant>, u64),
++    /// A tuple of constants.
++    Tuple(Vec<Constant>),
++    /// A raw pointer.
++    RawPtr(u128),
++    /// A literal with syntax error.
++    Err(Symbol),
++}
++
++impl PartialEq for Constant {
++    fn eq(&self, other: &Self) -> bool {
++        match (self, other) {
++            (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs,
++            (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r,
++            (&Self::Char(l), &Self::Char(r)) => l == r,
++            (&Self::Int(l), &Self::Int(r)) => l == r,
++            (&Self::F64(l), &Self::F64(r)) => {
++                // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
++                // `Fw32 == Fw64`, so don’t compare them.
++                // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
++                l.to_bits() == r.to_bits()
++            },
++            (&Self::F32(l), &Self::F32(r)) => {
++                // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
++                // `Fw32 == Fw64`, so don’t compare them.
++                // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
++                f64::from(l).to_bits() == f64::from(r).to_bits()
++            },
++            (&Self::Bool(l), &Self::Bool(r)) => l == r,
++            (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r,
++            (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
++            // TODO: are there inter-type equalities?
++            _ => false,
++        }
++    }
++}
++
++impl Hash for Constant {
++    fn hash<H>(&self, state: &mut H)
++    where
++        H: Hasher,
++    {
++        std::mem::discriminant(self).hash(state);
++        match *self {
++            Self::Str(ref s) => {
++                s.hash(state);
++            },
++            Self::Binary(ref b) => {
++                b.hash(state);
++            },
++            Self::Char(c) => {
++                c.hash(state);
++            },
++            Self::Int(i) => {
++                i.hash(state);
++            },
++            Self::F32(f) => {
++                f64::from(f).to_bits().hash(state);
++            },
++            Self::F64(f) => {
++                f.to_bits().hash(state);
++            },
++            Self::Bool(b) => {
++                b.hash(state);
++            },
++            Self::Vec(ref v) | Self::Tuple(ref v) => {
++                v.hash(state);
++            },
++            Self::Repeat(ref c, l) => {
++                c.hash(state);
++                l.hash(state);
++            },
++            Self::RawPtr(u) => {
++                u.hash(state);
++            },
++            Self::Err(ref s) => {
++                s.hash(state);
++            },
++        }
++    }
++}
++
++impl Constant {
++    pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
++        match (left, right) {
++            (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
++            (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
++            (&Self::Int(l), &Self::Int(r)) => {
++                if let ty::Int(int_ty) = cmp_type.kind {
++                    Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty)))
++                } else {
++                    Some(l.cmp(&r))
++                }
++            },
++            (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
++            (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
++            (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)),
++            (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => l
++                .iter()
++                .zip(r.iter())
++                .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri))
++                .find(|r| r.map_or(true, |o| o != Ordering::Equal))
++                .unwrap_or_else(|| Some(l.len().cmp(&r.len()))),
++            (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => {
++                match Self::partial_cmp(tcx, cmp_type, lv, rv) {
++                    Some(Equal) => Some(ls.cmp(rs)),
++                    x => x,
++                }
++            },
++            // TODO: are there any useful inter-type orderings?
++            _ => None,
++        }
++    }
++}
++
++/// Parses a `LitKind` to a `Constant`.
++pub fn lit_to_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
++    match *lit {
++        LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
++        LitKind::Byte(b) => Constant::Int(u128::from(b)),
++        LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)),
++        LitKind::Char(c) => Constant::Char(c),
++        LitKind::Int(n, _) => Constant::Int(n),
++        LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty {
++            FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()),
++            FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()),
++        },
++        LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind {
++            ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()),
++            ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()),
++            _ => bug!(),
++        },
++        LitKind::Bool(b) => Constant::Bool(b),
++        LitKind::Err(s) => Constant::Err(s),
++    }
++}
++
++pub fn constant<'c, 'cc>(
++    lcx: &LateContext<'c, 'cc>,
++    tables: &'c ty::TypeckTables<'cc>,
++    e: &Expr<'_>,
++) -> Option<(Constant, bool)> {
++    let mut cx = ConstEvalLateContext {
++        lcx,
++        tables,
++        param_env: lcx.param_env,
++        needed_resolution: false,
++        substs: lcx.tcx.intern_substs(&[]),
++    };
++    cx.expr(e).map(|cst| (cst, cx.needed_resolution))
++}
++
++pub fn constant_simple<'c, 'cc>(
++    lcx: &LateContext<'c, 'cc>,
++    tables: &'c ty::TypeckTables<'cc>,
++    e: &Expr<'_>,
++) -> Option<Constant> {
++    constant(lcx, tables, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
++}
++
++/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckTables`.
++pub fn constant_context<'c, 'cc>(
++    lcx: &'c LateContext<'c, 'cc>,
++    tables: &'c ty::TypeckTables<'cc>,
++) -> ConstEvalLateContext<'c, 'cc> {
++    ConstEvalLateContext {
++        lcx,
++        tables,
++        param_env: lcx.param_env,
++        needed_resolution: false,
++        substs: lcx.tcx.intern_substs(&[]),
++    }
++}
++
++pub struct ConstEvalLateContext<'a, 'tcx> {
++    lcx: &'a LateContext<'a, 'tcx>,
++    tables: &'a ty::TypeckTables<'tcx>,
++    param_env: ty::ParamEnv<'tcx>,
++    needed_resolution: bool,
++    substs: SubstsRef<'tcx>,
++}
++
++impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
++    /// Simple constant folding: Insert an expression, get a constant or none.
++    pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> {
++        if let Some((ref cond, ref then, otherwise)) = higher::if_block(&e) {
++            return self.ifthenelse(cond, then, otherwise);
++        }
++        match e.kind {
++            ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.tables.expr_ty(e)),
++            ExprKind::Block(ref block, _) => self.block(block),
++            ExprKind::Lit(ref lit) => Some(lit_to_constant(&lit.node, self.tables.expr_ty_opt(e))),
++            ExprKind::Array(ref vec) => self.multi(vec).map(Constant::Vec),
++            ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple),
++            ExprKind::Repeat(ref value, _) => {
++                let n = match self.tables.expr_ty(e).kind {
++                    ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?,
++                    _ => span_bug!(e.span, "typeck error"),
++                };
++                self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
++            },
++            ExprKind::Unary(op, ref operand) => self.expr(operand).and_then(|o| match op {
++                UnOp::UnNot => self.constant_not(&o, self.tables.expr_ty(e)),
++                UnOp::UnNeg => self.constant_negate(&o, self.tables.expr_ty(e)),
++                UnOp::UnDeref => Some(o),
++            }),
++            ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right),
++            ExprKind::Call(ref callee, ref args) => {
++                // We only handle a few const functions for now.
++                if_chain! {
++                    if args.is_empty();
++                    if let ExprKind::Path(qpath) = &callee.kind;
++                    let res = self.tables.qpath_res(qpath, callee.hir_id);
++                    if let Some(def_id) = res.opt_def_id();
++                    let def_path: Vec<_> = self.lcx.get_def_path(def_id).into_iter().map(Symbol::as_str).collect();
++                    let def_path: Vec<&str> = def_path.iter().take(4).map(|s| &**s).collect();
++                    if let ["core", "num", int_impl, "max_value"] = *def_path;
++                    then {
++                       let value = match int_impl {
++                           "<impl i8>" => i8::max_value() as u128,
++                           "<impl i16>" => i16::max_value() as u128,
++                           "<impl i32>" => i32::max_value() as u128,
++                           "<impl i64>" => i64::max_value() as u128,
++                           "<impl i128>" => i128::max_value() as u128,
++                           _ => return None,
++                       };
++                       Some(Constant::Int(value))
++                    }
++                    else {
++                        None
++                    }
++                }
++            },
++            ExprKind::Index(ref arr, ref index) => self.index(arr, index),
++            // TODO: add other expressions.
++            _ => None,
++        }
++    }
++
++    #[allow(clippy::cast_possible_wrap)]
++    fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
++        use self::Constant::{Bool, Int};
++        match *o {
++            Bool(b) => Some(Bool(!b)),
++            Int(value) => {
++                let value = !value;
++                match ty.kind {
++                    ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))),
++                    ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))),
++                    _ => None,
++                }
++            },
++            _ => None,
++        }
++    }
++
++    fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
++        use self::Constant::{Int, F32, F64};
++        match *o {
++            Int(value) => {
++                let ity = match ty.kind {
++                    ty::Int(ity) => ity,
++                    _ => return None,
++                };
++                // sign extend
++                let value = sext(self.lcx.tcx, value, ity);
++                let value = value.checked_neg()?;
++                // clear unused bits
++                Some(Int(unsext(self.lcx.tcx, value, ity)))
++            },
++            F32(f) => Some(F32(-f)),
++            F64(f) => Some(F64(-f)),
++            _ => None,
++        }
++    }
++
++    /// Create `Some(Vec![..])` of all constants, unless there is any
++    /// non-constant part.
++    fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant>> {
++        vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
++    }
++
++    /// Lookup a possibly constant expression from a `ExprKind::Path`.
++    fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'cc>) -> Option<Constant> {
++        let res = self.tables.qpath_res(qpath, id);
++        match res {
++            Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
++                let substs = self.tables.node_substs(id);
++                let substs = if self.substs.is_empty() {
++                    substs
++                } else {
++                    substs.subst(self.lcx.tcx, self.substs)
++                };
++
++                let result = self
++                    .lcx
++                    .tcx
++                    .const_eval_resolve(self.param_env, def_id, substs, None, None)
++                    .ok()
++                    .map(|val| rustc_middle::ty::Const::from_value(self.lcx.tcx, val, ty))?;
++                let result = miri_to_const(&result);
++                if result.is_some() {
++                    self.needed_resolution = true;
++                }
++                result
++            },
++            // FIXME: cover all usable cases.
++            _ => None,
++        }
++    }
++
++    fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant> {
++        let lhs = self.expr(lhs);
++        let index = self.expr(index);
++
++        match (lhs, index) {
++            (Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) {
++                Some(Constant::F32(x)) => Some(Constant::F32(*x)),
++                Some(Constant::F64(x)) => Some(Constant::F64(*x)),
++                _ => None,
++            },
++            (Some(Constant::Vec(vec)), _) => {
++                if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) {
++                    match vec.get(0) {
++                        Some(Constant::F32(x)) => Some(Constant::F32(*x)),
++                        Some(Constant::F64(x)) => Some(Constant::F64(*x)),
++                        _ => None,
++                    }
++                } else {
++                    None
++                }
++            },
++            _ => None,
++        }
++    }
++
++    /// A block can only yield a constant if it only has one constant expression.
++    fn block(&mut self, block: &Block<'_>) -> Option<Constant> {
++        if block.stmts.is_empty() {
++            block.expr.as_ref().and_then(|b| self.expr(b))
++        } else {
++            None
++        }
++    }
++
++    fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant> {
++        if let Some(Constant::Bool(b)) = self.expr(cond) {
++            if b {
++                self.expr(&*then)
++            } else {
++                otherwise.as_ref().and_then(|expr| self.expr(expr))
++            }
++        } else {
++            None
++        }
++    }
++
++    fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant> {
++        let l = self.expr(left)?;
++        let r = self.expr(right);
++        match (l, r) {
++            (Constant::Int(l), Some(Constant::Int(r))) => match self.tables.expr_ty(left).kind {
++                ty::Int(ity) => {
++                    let l = sext(self.lcx.tcx, l, ity);
++                    let r = sext(self.lcx.tcx, r, ity);
++                    let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity));
++                    match op.node {
++                        BinOpKind::Add => l.checked_add(r).map(zext),
++                        BinOpKind::Sub => l.checked_sub(r).map(zext),
++                        BinOpKind::Mul => l.checked_mul(r).map(zext),
++                        BinOpKind::Div if r != 0 => l.checked_div(r).map(zext),
++                        BinOpKind::Rem if r != 0 => l.checked_rem(r).map(zext),
++                        BinOpKind::Shr => l.checked_shr(r.try_into().expect("invalid shift")).map(zext),
++                        BinOpKind::Shl => l.checked_shl(r.try_into().expect("invalid shift")).map(zext),
++                        BinOpKind::BitXor => Some(zext(l ^ r)),
++                        BinOpKind::BitOr => Some(zext(l | r)),
++                        BinOpKind::BitAnd => Some(zext(l & r)),
++                        BinOpKind::Eq => Some(Constant::Bool(l == r)),
++                        BinOpKind::Ne => Some(Constant::Bool(l != r)),
++                        BinOpKind::Lt => Some(Constant::Bool(l < r)),
++                        BinOpKind::Le => Some(Constant::Bool(l <= r)),
++                        BinOpKind::Ge => Some(Constant::Bool(l >= r)),
++                        BinOpKind::Gt => Some(Constant::Bool(l > r)),
++                        _ => None,
++                    }
++                },
++                ty::Uint(_) => match op.node {
++                    BinOpKind::Add => l.checked_add(r).map(Constant::Int),
++                    BinOpKind::Sub => l.checked_sub(r).map(Constant::Int),
++                    BinOpKind::Mul => l.checked_mul(r).map(Constant::Int),
++                    BinOpKind::Div => l.checked_div(r).map(Constant::Int),
++                    BinOpKind::Rem => l.checked_rem(r).map(Constant::Int),
++                    BinOpKind::Shr => l.checked_shr(r.try_into().expect("shift too large")).map(Constant::Int),
++                    BinOpKind::Shl => l.checked_shl(r.try_into().expect("shift too large")).map(Constant::Int),
++                    BinOpKind::BitXor => Some(Constant::Int(l ^ r)),
++                    BinOpKind::BitOr => Some(Constant::Int(l | r)),
++                    BinOpKind::BitAnd => Some(Constant::Int(l & r)),
++                    BinOpKind::Eq => Some(Constant::Bool(l == r)),
++                    BinOpKind::Ne => Some(Constant::Bool(l != r)),
++                    BinOpKind::Lt => Some(Constant::Bool(l < r)),
++                    BinOpKind::Le => Some(Constant::Bool(l <= r)),
++                    BinOpKind::Ge => Some(Constant::Bool(l >= r)),
++                    BinOpKind::Gt => Some(Constant::Bool(l > r)),
++                    _ => None,
++                },
++                _ => None,
++            },
++            (Constant::F32(l), Some(Constant::F32(r))) => match op.node {
++                BinOpKind::Add => Some(Constant::F32(l + r)),
++                BinOpKind::Sub => Some(Constant::F32(l - r)),
++                BinOpKind::Mul => Some(Constant::F32(l * r)),
++                BinOpKind::Div => Some(Constant::F32(l / r)),
++                BinOpKind::Rem => Some(Constant::F32(l % r)),
++                BinOpKind::Eq => Some(Constant::Bool(l == r)),
++                BinOpKind::Ne => Some(Constant::Bool(l != r)),
++                BinOpKind::Lt => Some(Constant::Bool(l < r)),
++                BinOpKind::Le => Some(Constant::Bool(l <= r)),
++                BinOpKind::Ge => Some(Constant::Bool(l >= r)),
++                BinOpKind::Gt => Some(Constant::Bool(l > r)),
++                _ => None,
++            },
++            (Constant::F64(l), Some(Constant::F64(r))) => match op.node {
++                BinOpKind::Add => Some(Constant::F64(l + r)),
++                BinOpKind::Sub => Some(Constant::F64(l - r)),
++                BinOpKind::Mul => Some(Constant::F64(l * r)),
++                BinOpKind::Div => Some(Constant::F64(l / r)),
++                BinOpKind::Rem => Some(Constant::F64(l % r)),
++                BinOpKind::Eq => Some(Constant::Bool(l == r)),
++                BinOpKind::Ne => Some(Constant::Bool(l != r)),
++                BinOpKind::Lt => Some(Constant::Bool(l < r)),
++                BinOpKind::Le => Some(Constant::Bool(l <= r)),
++                BinOpKind::Ge => Some(Constant::Bool(l >= r)),
++                BinOpKind::Gt => Some(Constant::Bool(l > r)),
++                _ => None,
++            },
++            (l, r) => match (op.node, l, r) {
++                (BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)),
++                (BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)),
++                (BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => {
++                    Some(r)
++                },
++                (BinOpKind::BitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)),
++                (BinOpKind::BitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)),
++                (BinOpKind::BitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)),
++                _ => None,
++            },
++        }
++    }
++}
++
++pub fn miri_to_const(result: &ty::Const<'_>) -> Option<Constant> {
++    use rustc_middle::mir::interpret::{ConstValue, Scalar};
++    match result.val {
++        ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data: d, .. })) => match result.ty.kind {
++            ty::Bool => Some(Constant::Bool(d == 1)),
++            ty::Uint(_) | ty::Int(_) => Some(Constant::Int(d)),
++            ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
++                d.try_into().expect("invalid f32 bit representation"),
++            ))),
++            ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
++                d.try_into().expect("invalid f64 bit representation"),
++            ))),
++            ty::RawPtr(type_and_mut) => {
++                if let ty::Uint(_) = type_and_mut.ty.kind {
++                    return Some(Constant::RawPtr(d));
++                }
++                None
++            },
++            // FIXME: implement other conversions.
++            _ => None,
++        },
++        ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind {
++            ty::Ref(_, tam, _) => match tam.kind {
++                ty::Str => String::from_utf8(
++                    data.inspect_with_undef_and_ptr_outside_interpreter(start..end)
++                        .to_owned(),
++                )
++                .ok()
++                .map(Constant::Str),
++                _ => None,
++            },
++            _ => None,
++        },
++        ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind {
++            ty::Array(sub_type, len) => match sub_type.kind {
++                ty::Float(FloatTy::F32) => match miri_to_const(len) {
++                    Some(Constant::Int(len)) => alloc
++                        .inspect_with_undef_and_ptr_outside_interpreter(0..(4 * len as usize))
++                        .to_owned()
++                        .chunks(4)
++                        .map(|chunk| {
++                            Some(Constant::F32(f32::from_le_bytes(
++                                chunk.try_into().expect("this shouldn't happen"),
++                            )))
++                        })
++                        .collect::<Option<Vec<Constant>>>()
++                        .map(Constant::Vec),
++                    _ => None,
++                },
++                ty::Float(FloatTy::F64) => match miri_to_const(len) {
++                    Some(Constant::Int(len)) => alloc
++                        .inspect_with_undef_and_ptr_outside_interpreter(0..(8 * len as usize))
++                        .to_owned()
++                        .chunks(8)
++                        .map(|chunk| {
++                            Some(Constant::F64(f64::from_le_bytes(
++                                chunk.try_into().expect("this shouldn't happen"),
++                            )))
++                        })
++                        .collect::<Option<Vec<Constant>>>()
++                        .map(Constant::Vec),
++                    _ => None,
++                },
++                // FIXME: implement other array type conversions.
++                _ => None,
++            },
++            _ => None,
++        },
++        // FIXME: implement other conversions.
++        _ => None,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..66722786eab49c52f1429ada6f21cd7e80a15b0b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,417 @@@
++use crate::utils::{get_parent_expr, higher, if_sequence, same_tys, snippet, span_lint_and_note, span_lint_and_then};
++use crate::utils::{SpanlessEq, SpanlessHash};
++use rustc_data_structures::fx::FxHashMap;
++use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::Ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::symbol::Symbol;
++use std::collections::hash_map::Entry;
++use std::hash::BuildHasherDefault;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for consecutive `if`s with the same condition.
++    ///
++    /// **Why is this bad?** This is probably a copy & paste error.
++    ///
++    /// **Known problems:** Hopefully none.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// if a == b {
++    ///     …
++    /// } else if a == b {
++    ///     …
++    /// }
++    /// ```
++    ///
++    /// Note that this lint ignores all conditions with a function call as it could
++    /// have side effects:
++    ///
++    /// ```ignore
++    /// if foo() {
++    ///     …
++    /// } else if foo() { // not linted
++    ///     …
++    /// }
++    /// ```
++    pub IFS_SAME_COND,
++    correctness,
++    "consecutive `if`s with the same condition"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for consecutive `if`s with the same function call.
++    ///
++    /// **Why is this bad?** This is probably a copy & paste error.
++    /// Despite the fact that function can have side effects and `if` works as
++    /// intended, such an approach is implicit and can be considered a "code smell".
++    ///
++    /// **Known problems:** Hopefully none.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// if foo() == bar {
++    ///     …
++    /// } else if foo() == bar {
++    ///     …
++    /// }
++    /// ```
++    ///
++    /// This probably should be:
++    /// ```ignore
++    /// if foo() == bar {
++    ///     …
++    /// } else if foo() == baz {
++    ///     …
++    /// }
++    /// ```
++    ///
++    /// or if the original code was not a typo and called function mutates a state,
++    /// consider move the mutation out of the `if` condition to avoid similarity to
++    /// a copy & paste error:
++    ///
++    /// ```ignore
++    /// let first = foo();
++    /// if first == bar {
++    ///     …
++    /// } else {
++    ///     let second = foo();
++    ///     if second == bar {
++    ///     …
++    ///     }
++    /// }
++    /// ```
++    pub SAME_FUNCTIONS_IN_IF_CONDITION,
++    pedantic,
++    "consecutive `if`s with the same function call"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `if/else` with the same body as the *then* part
++    /// and the *else* part.
++    ///
++    /// **Why is this bad?** This is probably a copy & paste error.
++    ///
++    /// **Known problems:** Hopefully none.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// let foo = if … {
++    ///     42
++    /// } else {
++    ///     42
++    /// };
++    /// ```
++    pub IF_SAME_THEN_ELSE,
++    correctness,
++    "`if` with the same `then` and `else` blocks"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `match` with identical arm bodies.
++    ///
++    /// **Why is this bad?** This is probably a copy & paste error. If arm bodies
++    /// are the same on purpose, you can factor them
++    /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
++    ///
++    /// **Known problems:** False positive possible with order dependent `match`
++    /// (see issue
++    /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// match foo {
++    ///     Bar => bar(),
++    ///     Quz => quz(),
++    ///     Baz => bar(), // <= oops
++    /// }
++    /// ```
++    ///
++    /// This should probably be
++    /// ```rust,ignore
++    /// match foo {
++    ///     Bar => bar(),
++    ///     Quz => quz(),
++    ///     Baz => baz(), // <= fixed
++    /// }
++    /// ```
++    ///
++    /// or if the original code was not a typo:
++    /// ```rust,ignore
++    /// match foo {
++    ///     Bar | Baz => bar(), // <= shows the intent better
++    ///     Quz => quz(),
++    /// }
++    /// ```
++    pub MATCH_SAME_ARMS,
++    pedantic,
++    "`match` with identical arm bodies"
++}
++
++declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE, MATCH_SAME_ARMS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CopyAndPaste {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if !expr.span.from_expansion() {
++            // skip ifs directly in else, it will be checked in the parent if
++            if let Some(expr) = get_parent_expr(cx, expr) {
++                if let Some((_, _, Some(ref else_expr))) = higher::if_block(&expr) {
++                    if else_expr.hir_id == expr.hir_id {
++                        return;
++                    }
++                }
++            }
++
++            let (conds, blocks) = if_sequence(expr);
++            lint_same_then_else(cx, &blocks);
++            lint_same_cond(cx, &conds);
++            lint_same_fns_in_if_cond(cx, &conds);
++            lint_match_arms(cx, expr);
++        }
++    }
++}
++
++/// Implementation of `IF_SAME_THEN_ELSE`.
++fn lint_same_then_else(cx: &LateContext<'_, '_>, blocks: &[&Block<'_>]) {
++    let eq: &dyn Fn(&&Block<'_>, &&Block<'_>) -> bool =
++        &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).eq_block(lhs, rhs) };
++
++    if let Some((i, j)) = search_same_sequenced(blocks, eq) {
++        span_lint_and_note(
++            cx,
++            IF_SAME_THEN_ELSE,
++            j.span,
++            "this `if` has identical blocks",
++            Some(i.span),
++            "same as this",
++        );
++    }
++}
++
++/// Implementation of `IFS_SAME_COND`.
++fn lint_same_cond(cx: &LateContext<'_, '_>, conds: &[&Expr<'_>]) {
++    let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 {
++        let mut h = SpanlessHash::new(cx, cx.tables);
++        h.hash_expr(expr);
++        h.finish()
++    };
++
++    let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool =
++        &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) };
++
++    for (i, j) in search_same(conds, hash, eq) {
++        span_lint_and_note(
++            cx,
++            IFS_SAME_COND,
++            j.span,
++            "this `if` has the same condition as a previous `if`",
++            Some(i.span),
++            "same as this",
++        );
++    }
++}
++
++/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`.
++fn lint_same_fns_in_if_cond(cx: &LateContext<'_, '_>, conds: &[&Expr<'_>]) {
++    let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 {
++        let mut h = SpanlessHash::new(cx, cx.tables);
++        h.hash_expr(expr);
++        h.finish()
++    };
++
++    let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool {
++        // Do not spawn warning if `IFS_SAME_COND` already produced it.
++        if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) {
++            return false;
++        }
++        SpanlessEq::new(cx).eq_expr(lhs, rhs)
++    };
++
++    for (i, j) in search_same(conds, hash, eq) {
++        span_lint_and_note(
++            cx,
++            SAME_FUNCTIONS_IN_IF_CONDITION,
++            j.span,
++            "this `if` has the same function call as a previous `if`",
++            Some(i.span),
++            "same as this",
++        );
++    }
++}
++
++/// Implementation of `MATCH_SAME_ARMS`.
++fn lint_match_arms<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &Expr<'_>) {
++    fn same_bindings<'tcx>(
++        cx: &LateContext<'_, 'tcx>,
++        lhs: &FxHashMap<Symbol, Ty<'tcx>>,
++        rhs: &FxHashMap<Symbol, Ty<'tcx>>,
++    ) -> bool {
++        lhs.len() == rhs.len()
++            && lhs
++                .iter()
++                .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| same_tys(cx, l_ty, r_ty)))
++    }
++
++    if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind {
++        let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
++            let mut h = SpanlessHash::new(cx, cx.tables);
++            h.hash_expr(&arm.body);
++            h.finish()
++        };
++
++        let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
++            let min_index = usize::min(lindex, rindex);
++            let max_index = usize::max(lindex, rindex);
++
++            // Arms with a guard are ignored, those can’t always be merged together
++            // This is also the case for arms in-between each there is an arm with a guard
++            (min_index..=max_index).all(|index| arms[index].guard.is_none()) &&
++                SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) &&
++                // all patterns should have the same bindings
++                same_bindings(cx, &bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat))
++        };
++
++        let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
++        for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
++            span_lint_and_then(
++                cx,
++                MATCH_SAME_ARMS,
++                j.body.span,
++                "this `match` has identical arm bodies",
++                |diag| {
++                    diag.span_note(i.body.span, "same as this");
++
++                    // Note: this does not use `span_suggestion` on purpose:
++                    // there is no clean way
++                    // to remove the other arm. Building a span and suggest to replace it to ""
++                    // makes an even more confusing error message. Also in order not to make up a
++                    // span for the whole pattern, the suggestion is only shown when there is only
++                    // one pattern. The user should know about `|` if they are already using it…
++
++                    let lhs = snippet(cx, i.pat.span, "<pat1>");
++                    let rhs = snippet(cx, j.pat.span, "<pat2>");
++
++                    if let PatKind::Wild = j.pat.kind {
++                        // if the last arm is _, then i could be integrated into _
++                        // note that i.pat cannot be _, because that would mean that we're
++                        // hiding all the subsequent arms, and rust won't compile
++                        diag.span_note(
++                            i.body.span,
++                            &format!(
++                                "`{}` has the same arm body as the `_` wildcard, consider removing it",
++                                lhs
++                            ),
++                        );
++                    } else {
++                        diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
++                    }
++                },
++            );
++        }
++    }
++}
++
++/// Returns the list of bindings in a pattern.
++fn bindings<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat<'_>) -> FxHashMap<Symbol, Ty<'tcx>> {
++    fn bindings_impl<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat<'_>, map: &mut FxHashMap<Symbol, Ty<'tcx>>) {
++        match pat.kind {
++            PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map),
++            PatKind::TupleStruct(_, pats, _) => {
++                for pat in pats {
++                    bindings_impl(cx, pat, map);
++                }
++            },
++            PatKind::Binding(.., ident, ref as_pat) => {
++                if let Entry::Vacant(v) = map.entry(ident.name) {
++                    v.insert(cx.tables.pat_ty(pat));
++                }
++                if let Some(ref as_pat) = *as_pat {
++                    bindings_impl(cx, as_pat, map);
++                }
++            },
++            PatKind::Or(fields) | PatKind::Tuple(fields, _) => {
++                for pat in fields {
++                    bindings_impl(cx, pat, map);
++                }
++            },
++            PatKind::Struct(_, fields, _) => {
++                for pat in fields {
++                    bindings_impl(cx, &pat.pat, map);
++                }
++            },
++            PatKind::Slice(lhs, ref mid, rhs) => {
++                for pat in lhs {
++                    bindings_impl(cx, pat, map);
++                }
++                if let Some(ref mid) = *mid {
++                    bindings_impl(cx, mid, map);
++                }
++                for pat in rhs {
++                    bindings_impl(cx, pat, map);
++                }
++            },
++            PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (),
++        }
++    }
++
++    let mut result = FxHashMap::default();
++    bindings_impl(cx, pat, &mut result);
++    result
++}
++
++fn search_same_sequenced<T, Eq>(exprs: &[T], eq: Eq) -> Option<(&T, &T)>
++where
++    Eq: Fn(&T, &T) -> bool,
++{
++    for win in exprs.windows(2) {
++        if eq(&win[0], &win[1]) {
++            return Some((&win[0], &win[1]));
++        }
++    }
++    None
++}
++
++fn search_common_cases<'a, T, Eq>(exprs: &'a [T], eq: &Eq) -> Option<(&'a T, &'a T)>
++where
++    Eq: Fn(&T, &T) -> bool,
++{
++    if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) {
++        Some((&exprs[0], &exprs[1]))
++    } else {
++        None
++    }
++}
++
++fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
++where
++    Hash: Fn(&T) -> u64,
++    Eq: Fn(&T, &T) -> bool,
++{
++    if let Some(expr) = search_common_cases(&exprs, &eq) {
++        return vec![expr];
++    }
++
++    let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
++
++    let mut map: FxHashMap<_, Vec<&_>> =
++        FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
++
++    for expr in exprs {
++        match map.entry(hash(expr)) {
++            Entry::Occupied(mut o) => {
++                for o in o.get() {
++                    if eq(o, expr) {
++                        match_expr_list.push((o, expr));
++                    }
++                }
++                o.get_mut().push(expr);
++            },
++            Entry::Vacant(v) => {
++                v.insert(vec![expr]);
++            },
++        }
++    }
++
++    match_expr_list
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d79aa2ef0209e1da9f8046572e73e79b46c40a41
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++use crate::utils::{is_copy, match_path, paths, span_lint_and_note};
++use rustc_hir::{Item, ItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for types that implement `Copy` as well as
++    /// `Iterator`.
++    ///
++    /// **Why is this bad?** Implicit copies can be confusing when working with
++    /// iterator combinators.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// #[derive(Copy, Clone)]
++    /// struct Countdown(u8);
++    ///
++    /// impl Iterator for Countdown {
++    ///     // ...
++    /// }
++    ///
++    /// let a: Vec<_> = my_iterator.take(1).collect();
++    /// let b: Vec<_> = my_iterator.collect();
++    /// ```
++    pub COPY_ITERATOR,
++    pedantic,
++    "implementing `Iterator` on a `Copy` type"
++}
++
++declare_lint_pass!(CopyIterator => [COPY_ITERATOR]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CopyIterator {
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++        if let ItemKind::Impl {
++            of_trait: Some(ref trait_ref),
++            ..
++        } = item.kind
++        {
++            let ty = cx.tcx.type_of(cx.tcx.hir().local_def_id(item.hir_id));
++
++            if is_copy(cx, ty) && match_path(&trait_ref.path, &paths::ITERATOR) {
++                span_lint_and_note(
++                    cx,
++                    COPY_ITERATOR,
++                    item.span,
++                    "you are implementing `Iterator` on a `Copy` type",
++                    None,
++                    "consider implementing `IntoIterator` instead",
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e513dcce64e5349fb6668ceb1bf3fc864d577809
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,65 @@@
++use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg};
++use rustc_ast::ast;
++use rustc_ast::tokenstream::TokenStream;
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of dbg!() macro.
++    ///
++    /// **Why is this bad?** `dbg!` macro is intended as a debugging tool. It
++    /// should not be in version control.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// // Bad
++    /// dbg!(true)
++    ///
++    /// // Good
++    /// true
++    /// ```
++    pub DBG_MACRO,
++    restriction,
++    "`dbg!` macro is intended as a debugging tool"
++}
++
++declare_lint_pass!(DbgMacro => [DBG_MACRO]);
++
++impl EarlyLintPass for DbgMacro {
++    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) {
++        if mac.path == sym!(dbg) {
++            if let Some(sugg) = tts_span(mac.args.inner_tokens()).and_then(|span| snippet_opt(cx, span)) {
++                span_lint_and_sugg(
++                    cx,
++                    DBG_MACRO,
++                    mac.span(),
++                    "`dbg!` macro is intended as a debugging tool",
++                    "ensure to avoid having uses of it in version control",
++                    sugg,
++                    Applicability::MaybeIncorrect,
++                );
++            } else {
++                span_lint_and_help(
++                    cx,
++                    DBG_MACRO,
++                    mac.span(),
++                    "`dbg!` macro is intended as a debugging tool",
++                    None,
++                    "ensure to avoid having uses of it in version control",
++                );
++            }
++        }
++    }
++}
++
++// Get span enclosing entire the token stream.
++fn tts_span(tts: TokenStream) -> Option<Span> {
++    let mut cursor = tts.into_trees();
++    let first = cursor.next()?.span();
++    let span = cursor.last().map_or(first, |tree| first.to(tree.span()));
++    Some(span)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..635d609c382890f1a04ae880afdaf965f535c6f6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,76 @@@
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, QPath};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use crate::utils::{any_parent_is_automatically_derived, match_def_path, paths, span_lint_and_sugg};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for literal calls to `Default::default()`.
++    ///
++    /// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is
++    /// being gotten than the generic `Default`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// // Bad
++    /// let s: String = Default::default();
++    ///
++    /// // Good
++    /// let s = String::default();
++    /// ```
++    pub DEFAULT_TRAIT_ACCESS,
++    pedantic,
++    "checks for literal calls to `Default::default()`"
++}
++
++declare_lint_pass!(DefaultTraitAccess => [DEFAULT_TRAIT_ACCESS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DefaultTraitAccess {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            if let ExprKind::Call(ref path, ..) = expr.kind;
++            if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
++            if let ExprKind::Path(ref qpath) = path.kind;
++            if let Some(def_id) = cx.tables.qpath_res(qpath, path.hir_id).opt_def_id();
++            if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
++            then {
++                match qpath {
++                    QPath::Resolved(..) => {
++                        if_chain! {
++                            // Detect and ignore <Foo as Default>::default() because these calls do
++                            // explicitly name the type.
++                            if let ExprKind::Call(ref method, ref _args) = expr.kind;
++                            if let ExprKind::Path(ref p) = method.kind;
++                            if let QPath::Resolved(Some(_ty), _path) = p;
++                            then {
++                                return;
++                            }
++                        }
++
++                        // TODO: Work out a way to put "whatever the imported way of referencing
++                        // this type in this file" rather than a fully-qualified type.
++                        let expr_ty = cx.tables.expr_ty(expr);
++                        if let ty::Adt(..) = expr_ty.kind {
++                            let replacement = format!("{}::default()", expr_ty);
++                            span_lint_and_sugg(
++                                cx,
++                                DEFAULT_TRAIT_ACCESS,
++                                expr.span,
++                                &format!("Calling `{}` is more clear than this expression", replacement),
++                                "try",
++                                replacement,
++                                Applicability::Unspecified, // First resolve the TODO above
++                            );
++                         }
++                    },
++                    QPath::TypeRelative(..) => {},
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6e8ca647dd7ae930e9c7dde397c4ae78e0178314
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,157 @@@
++macro_rules! declare_deprecated_lint {
++    (pub $name: ident, $_reason: expr) => {
++        declare_lint!(pub $name, Allow, "deprecated lint")
++    }
++}
++
++declare_deprecated_lint! {
++    /// **What it does:** Nothing. This lint has been deprecated.
++    ///
++    /// **Deprecation reason:** This used to check for `assert!(a == b)` and recommend
++    /// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011.
++    pub SHOULD_ASSERT_EQ,
++    "`assert!()` will be more flexible with RFC 2011"
++}
++
++declare_deprecated_lint! {
++    /// **What it does:** Nothing. This lint has been deprecated.
++    ///
++    /// **Deprecation reason:** This used to check for `Vec::extend`, which was slower than
++    /// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true.
++    pub EXTEND_FROM_SLICE,
++    "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice"
++}
++
++declare_deprecated_lint! {
++    /// **What it does:** Nothing. This lint has been deprecated.
++    ///
++    /// **Deprecation reason:** `Range::step_by(0)` used to be linted since it's
++    /// an infinite iterator, which is better expressed by `iter::repeat`,
++    /// but the method has been removed for `Iterator::step_by` which panics
++    /// if given a zero
++    pub RANGE_STEP_BY_ZERO,
++    "`iterator.step_by(0)` panics nowadays"
++}
++
++declare_deprecated_lint! {
++    /// **What it does:** Nothing. This lint has been deprecated.
++    ///
++    /// **Deprecation reason:** This used to check for `Vec::as_slice`, which was unstable with good
++    /// stable alternatives. `Vec::as_slice` has now been stabilized.
++    pub UNSTABLE_AS_SLICE,
++    "`Vec::as_slice` has been stabilized in 1.7"
++}
++
++declare_deprecated_lint! {
++    /// **What it does:** Nothing. This lint has been deprecated.
++    ///
++    /// **Deprecation reason:** This used to check for `Vec::as_mut_slice`, which was unstable with good
++    /// stable alternatives. `Vec::as_mut_slice` has now been stabilized.
++    pub UNSTABLE_AS_MUT_SLICE,
++    "`Vec::as_mut_slice` has been stabilized in 1.7"
++}
++
++declare_deprecated_lint! {
++    /// **What it does:** Nothing. This lint has been deprecated.
++    ///
++    /// **Deprecation reason:** This used to check for `.to_string()` method calls on values
++    /// of type `&str`. This is not unidiomatic and with specialization coming, `to_string` could be
++    /// specialized to be as efficient as `to_owned`.
++    pub STR_TO_STRING,
++    "using `str::to_string` is common even today and specialization will likely happen soon"
++}
++
++declare_deprecated_lint! {
++    /// **What it does:** Nothing. This lint has been deprecated.
++    ///
++    /// **Deprecation reason:** This used to check for `.to_string()` method calls on values
++    /// of type `String`. This is not unidiomatic and with specialization coming, `to_string` could be
++    /// specialized to be as efficient as `clone`.
++    pub STRING_TO_STRING,
++    "using `string::to_string` is common even today and specialization will likely happen soon"
++}
++
++declare_deprecated_lint! {
++    /// **What it does:** Nothing. This lint has been deprecated.
++    ///
++    /// **Deprecation reason:** This lint should never have applied to non-pointer types, as transmuting
++    /// between non-pointer types of differing alignment is well-defined behavior (it's semantically
++    /// equivalent to a memcpy). This lint has thus been refactored into two separate lints:
++    /// cast_ptr_alignment and transmute_ptr_to_ptr.
++    pub MISALIGNED_TRANSMUTE,
++    "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr"
++}
++
++declare_deprecated_lint! {
++    /// **What it does:** Nothing. This lint has been deprecated.
++    ///
++    /// **Deprecation reason:** This lint is too subjective, not having a good reason for being in clippy.
++    /// Additionally, compound assignment operators may be overloaded separately from their non-assigning
++    /// counterparts, so this lint may suggest a change in behavior or the code may not compile.
++    pub ASSIGN_OPS,
++    "using compound assignment operators (e.g., `+=`) is harmless"
++}
++
++declare_deprecated_lint! {
++    /// **What it does:** Nothing. This lint has been deprecated.
++    ///
++    /// **Deprecation reason:** The original rule will only lint for `if let`. After
++    /// making it support to lint `match`, naming as `if let` is not suitable for it.
++    /// So, this lint is deprecated.
++    pub IF_LET_REDUNDANT_PATTERN_MATCHING,
++    "this lint has been changed to redundant_pattern_matching"
++}
++
++declare_deprecated_lint! {
++    /// **What it does:** Nothing. This lint has been deprecated.
++    ///
++    /// **Deprecation reason:** This lint used to suggest replacing `let mut vec =
++    /// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The
++    /// replacement has very different performance characteristics so the lint is
++    /// deprecated.
++    pub UNSAFE_VECTOR_INITIALIZATION,
++    "the replacement suggested by this lint had substantially different behavior"
++}
++
++declare_deprecated_lint! {
++    /// **What it does:** Nothing. This lint has been deprecated.
++    ///
++    /// **Deprecation reason:** This lint has been superseded by the warn-by-default
++    /// `invalid_value` rustc lint.
++    pub INVALID_REF,
++    "superseded by rustc lint `invalid_value`"
++}
++
++declare_deprecated_lint! {
++    /// **What it does:** Nothing. This lint has been deprecated.
++    ///
++    /// **Deprecation reason:** This lint has been superseded by #[must_use] in rustc.
++    pub UNUSED_COLLECT,
++    "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint"
++}
++
++declare_deprecated_lint! {
++    /// **What it does:** Nothing. This lint has been deprecated.
++    ///
++    /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
++    /// `array_into_iter`.
++    pub INTO_ITER_ON_ARRAY,
++    "this lint has been uplifted to rustc and is now called `array_into_iter`"
++}
++
++declare_deprecated_lint! {
++    /// **What it does:** Nothing. This lint has been deprecated.
++    ///
++    /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
++    /// `unused_labels`.
++    pub UNUSED_LABEL,
++    "this lint has been uplifted to rustc and is now called `unused_labels`"
++}
++
++declare_deprecated_lint! {
++    /// **What it does:** Nothing. This lint has been deprecated.
++    ///
++    /// **Deprecation reason:** Associated-constants are now preferred.
++    pub REPLACE_CONSTS,
++    "associated-constants `MIN`/`MAX` of integers are prefer to `{min,max}_value()` and module constants"
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..68ec07e2bcb0f58f874bdbce221741d1559c7c54
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,113 @@@
++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 rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls.
++    ///
++    /// **Why is this bad?** Derefencing by `&*x` or `&mut *x` is clearer and more concise,
++    /// when not part of a method chain.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// use std::ops::Deref;
++    /// let a: &mut String = &mut String::from("foo");
++    /// let b: &str = a.deref();
++    /// ```
++    /// Could be written as:
++    /// ```rust
++    /// let a: &mut String = &mut String::from("foo");
++    /// let b = &*a;
++    /// ```
++    ///
++    /// This lint excludes
++    /// ```rust,ignore
++    /// let _ = d.unwrap().deref();
++    /// ```
++    pub EXPLICIT_DEREF_METHODS,
++    pedantic,
++    "Explicit use of deref or deref_mut method while not in a method chain."
++}
++
++declare_lint_pass!(Dereferencing => [
++    EXPLICIT_DEREF_METHODS
++]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Dereferencing {
++    fn check_expr(&mut self, cx: &LateContext<'a, '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;
++                            }
++                        }
++                    }
++                }
++                let name = method_name.ident.as_str();
++                lint_deref(cx, &*name, &args[0], args[0].span, expr.span);
++            }
++        }
++    }
++}
++
++fn lint_deref(cx: &LateContext<'_, '_>, method_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) {
++    match method_name {
++        "deref" => {
++            if cx
++                .tcx
++                .lang_items()
++                .deref_trait()
++                .map_or(false, |id| implements_trait(cx, cx.tables.expr_ty(&call_expr), id, &[]))
++            {
++                span_lint_and_sugg(
++                    cx,
++                    EXPLICIT_DEREF_METHODS,
++                    expr_span,
++                    "explicit deref method call",
++                    "try this",
++                    format!("&*{}", &snippet(cx, var_span, "..")),
++                    Applicability::MachineApplicable,
++                );
++            }
++        },
++        "deref_mut" => {
++            if cx
++                .tcx
++                .lang_items()
++                .deref_mut_trait()
++                .map_or(false, |id| implements_trait(cx, cx.tables.expr_ty(&call_expr), id, &[]))
++            {
++                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,
++                );
++            }
++        },
++        _ => (),
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3cbb8fa72f74f70f9cb9fcb6b8e25996a8d58814
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,309 @@@
++use crate::utils::paths;
++use crate::utils::{
++    is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then,
++};
++use if_chain::if_chain;
++use rustc_hir::def_id::DefId;
++use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor};
++use rustc_hir::{
++    BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Item, ItemKind, TraitRef, UnsafeSource, Unsafety,
++};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for deriving `Hash` but implementing `PartialEq`
++    /// explicitly or vice versa.
++    ///
++    /// **Why is this bad?** The implementation of these traits must agree (for
++    /// example for use with `HashMap`) so it’s probably a bad idea to use a
++    /// default-generated `Hash` implementation with an explicitly defined
++    /// `PartialEq`. In particular, the following must hold for any type:
++    ///
++    /// ```text
++    /// k1 == k2 ⇒ hash(k1) == hash(k2)
++    /// ```
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// #[derive(Hash)]
++    /// struct Foo;
++    ///
++    /// impl PartialEq for Foo {
++    ///     ...
++    /// }
++    /// ```
++    pub DERIVE_HASH_XOR_EQ,
++    correctness,
++    "deriving `Hash` but implementing `PartialEq` explicitly"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for explicit `Clone` implementations for `Copy`
++    /// types.
++    ///
++    /// **Why is this bad?** To avoid surprising behaviour, these traits should
++    /// agree and the behaviour of `Copy` cannot be overridden. In almost all
++    /// situations a `Copy` type should have a `Clone` implementation that does
++    /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
++    /// gets you.
++    ///
++    /// **Known problems:** Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// #[derive(Copy)]
++    /// struct Foo;
++    ///
++    /// impl Clone for Foo {
++    ///     // ..
++    /// }
++    /// ```
++    pub EXPL_IMPL_CLONE_ON_COPY,
++    pedantic,
++    "implementing `Clone` explicitly on `Copy` types"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for deriving `serde::Deserialize` on a type that
++    /// has methods using `unsafe`.
++    ///
++    /// **Why is this bad?** Deriving `serde::Deserialize` will create a constructor
++    /// that may violate invariants hold by another constructor.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust,ignore
++    /// use serde::Deserialize;
++    ///
++    /// #[derive(Deserialize)]
++    /// pub struct Foo {
++    ///     // ..
++    /// }
++    ///
++    /// impl Foo {
++    ///     pub fn new() -> Self {
++    ///         // setup here ..
++    ///     }
++    ///
++    ///     pub unsafe fn parts() -> (&str, &str) {
++    ///         // assumes invariants hold
++    ///     }
++    /// }
++    /// ```
++    pub UNSAFE_DERIVE_DESERIALIZE,
++    pedantic,
++    "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
++}
++
++declare_lint_pass!(Derive => [EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ, UNSAFE_DERIVE_DESERIALIZE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Derive {
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++        if let ItemKind::Impl {
++            of_trait: Some(ref trait_ref),
++            ..
++        } = item.kind
++        {
++            let ty = cx.tcx.type_of(cx.tcx.hir().local_def_id(item.hir_id));
++            let is_automatically_derived = is_automatically_derived(&*item.attrs);
++
++            check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
++
++            if is_automatically_derived {
++                check_unsafe_derive_deserialize(cx, item, trait_ref, ty);
++            } else {
++                check_copy_clone(cx, item, trait_ref, ty);
++            }
++        }
++    }
++}
++
++/// Implementation of the `DERIVE_HASH_XOR_EQ` lint.
++fn check_hash_peq<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    span: Span,
++    trait_ref: &TraitRef<'_>,
++    ty: Ty<'tcx>,
++    hash_is_automatically_derived: bool,
++) {
++    if_chain! {
++        if match_path(&trait_ref.path, &paths::HASH);
++        if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait();
++        if let Some(def_id) = &trait_ref.trait_def_id();
++        if !def_id.is_local();
++        then {
++            // Look for the PartialEq implementations for `ty`
++            cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
++                let peq_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id));
++
++                if peq_is_automatically_derived == hash_is_automatically_derived {
++                    return;
++                }
++
++                let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
++
++                // Only care about `impl PartialEq<Foo> for Foo`
++                // For `impl PartialEq<B> for A, input_types is [A, B]
++                if trait_ref.substs.type_at(1) == ty {
++                    let mess = if peq_is_automatically_derived {
++                        "you are implementing `Hash` explicitly but have derived `PartialEq`"
++                    } else {
++                        "you are deriving `Hash` but have implemented `PartialEq` explicitly"
++                    };
++
++                    span_lint_and_then(
++                        cx,
++                        DERIVE_HASH_XOR_EQ,
++                        span,
++                        mess,
++                        |diag| {
++                            if let Some(local_def_id) = impl_id.as_local() {
++                                let hir_id = cx.tcx.hir().as_local_hir_id(local_def_id);
++                                diag.span_note(
++                                    cx.tcx.hir().span(hir_id),
++                                    "`PartialEq` implemented here"
++                                );
++                            }
++                        }
++                    );
++                }
++            });
++        }
++    }
++}
++
++/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
++fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) {
++    if match_path(&trait_ref.path, &paths::CLONE_TRAIT) {
++        if !is_copy(cx, ty) {
++            return;
++        }
++
++        match ty.kind {
++            ty::Adt(def, _) if def.is_union() => return,
++
++            // Some types are not Clone by default but could be cloned “by hand” if necessary
++            ty::Adt(def, substs) => {
++                for variant in &def.variants {
++                    for field in &variant.fields {
++                        if let ty::FnDef(..) = field.ty(cx.tcx, substs).kind {
++                            return;
++                        }
++                    }
++                    for subst in substs {
++                        if let ty::subst::GenericArgKind::Type(subst) = subst.unpack() {
++                            if let ty::Param(_) = subst.kind {
++                                return;
++                            }
++                        }
++                    }
++                }
++            },
++            _ => (),
++        }
++
++        span_lint_and_note(
++            cx,
++            EXPL_IMPL_CLONE_ON_COPY,
++            item.span,
++            "you are implementing `Clone` explicitly on a `Copy` type",
++            Some(item.span),
++            "consider deriving `Clone` or removing `Copy`",
++        );
++    }
++}
++
++/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.
++fn check_unsafe_derive_deserialize<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    item: &Item<'_>,
++    trait_ref: &TraitRef<'_>,
++    ty: Ty<'tcx>,
++) {
++    fn item_from_def_id<'tcx>(cx: &LateContext<'_, 'tcx>, def_id: DefId) -> &'tcx Item<'tcx> {
++        let hir_id = cx.tcx.hir().as_local_hir_id(def_id.expect_local());
++        cx.tcx.hir().expect_item(hir_id)
++    }
++
++    fn has_unsafe<'tcx>(cx: &LateContext<'_, 'tcx>, item: &'tcx Item<'_>) -> bool {
++        let mut visitor = UnsafeVisitor { cx, has_unsafe: false };
++        walk_item(&mut visitor, item);
++        visitor.has_unsafe
++    }
++
++    if_chain! {
++        if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE);
++        if let ty::Adt(def, _) = ty.kind;
++        if def.did.is_local();
++        if cx.tcx.inherent_impls(def.did)
++            .iter()
++            .map(|imp_did| item_from_def_id(cx, *imp_did))
++            .any(|imp| has_unsafe(cx, imp));
++        then {
++            span_lint_and_help(
++                cx,
++                UNSAFE_DERIVE_DESERIALIZE,
++                item.span,
++                "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
++                None,
++                "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html"
++            );
++        }
++    }
++}
++
++struct UnsafeVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    has_unsafe: bool,
++}
++
++impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, span: Span, id: HirId) {
++        if self.has_unsafe {
++            return;
++        }
++
++        if_chain! {
++            if let Some(header) = kind.header();
++            if let Unsafety::Unsafe = header.unsafety;
++            then {
++                self.has_unsafe = true;
++            }
++        }
++
++        walk_fn(self, kind, decl, body_id, span, id);
++    }
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        if self.has_unsafe {
++            return;
++        }
++
++        if let ExprKind::Block(block, _) = expr.kind {
++            match block.rules {
++                BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
++                | BlockCheckMode::PushUnsafeBlock(UnsafeSource::UserProvided)
++                | BlockCheckMode::PopUnsafeBlock(UnsafeSource::UserProvided) => {
++                    self.has_unsafe = true;
++                },
++                _ => {},
++            }
++        }
++
++        walk_expr(self, expr);
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::All(self.cx.tcx.hir())
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8d1e91f9adbd61f5aa97e0f896832c099e3d63b0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,525 @@@
++use crate::utils::{implements_trait, is_entrypoint_fn, is_type_diagnostic_item, return_ty, span_lint};
++use if_chain::if_chain;
++use itertools::Itertools;
++use rustc_ast::ast::{AttrKind, Attribute};
++use rustc_data_structures::fx::FxHashSet;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty;
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::{BytePos, MultiSpan, Span};
++use rustc_span::Pos;
++use std::ops::Range;
++use url::Url;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for the presence of `_`, `::` or camel-case words
++    /// outside ticks in documentation.
++    ///
++    /// **Why is this bad?** *Rustdoc* supports markdown formatting, `_`, `::` and
++    /// camel-case probably indicates some code which should be included between
++    /// ticks. `_` can also be used for emphasis in markdown, this lint tries to
++    /// consider that.
++    ///
++    /// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks
++    /// for is limited, and there are still false positives.
++    ///
++    /// **Examples:**
++    /// ```rust
++    /// /// Do something with the foo_bar parameter. See also
++    /// /// that::other::module::foo.
++    /// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
++    /// fn doit(foo_bar: usize) {}
++    /// ```
++    pub DOC_MARKDOWN,
++    pedantic,
++    "presence of `_`, `::` or camel-case outside backticks in documentation"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for the doc comments of publicly visible
++    /// unsafe functions and warns if there is no `# Safety` section.
++    ///
++    /// **Why is this bad?** Unsafe functions should document their safety
++    /// preconditions, so that users can be sure they are using them safely.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Examples:**
++    /// ```rust
++    ///# type Universe = ();
++    /// /// This function should really be documented
++    /// pub unsafe fn start_apocalypse(u: &mut Universe) {
++    ///     unimplemented!();
++    /// }
++    /// ```
++    ///
++    /// At least write a line about safety:
++    ///
++    /// ```rust
++    ///# type Universe = ();
++    /// /// # Safety
++    /// ///
++    /// /// This function should not be called before the horsemen are ready.
++    /// pub unsafe fn start_apocalypse(u: &mut Universe) {
++    ///     unimplemented!();
++    /// }
++    /// ```
++    pub MISSING_SAFETY_DOC,
++    style,
++    "`pub unsafe fn` without `# Safety` docs"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks the doc comments of publicly visible functions that
++    /// return a `Result` type and warns if there is no `# Errors` section.
++    ///
++    /// **Why is this bad?** Documenting the type of errors that can be returned from a
++    /// function can help callers write code to handle the errors appropriately.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Examples:**
++    ///
++    /// Since the following function returns a `Result` it has an `# Errors` section in
++    /// its doc comment:
++    ///
++    /// ```rust
++    ///# use std::io;
++    /// /// # Errors
++    /// ///
++    /// /// Will return `Err` if `filename` does not exist or the user does not have
++    /// /// permission to read it.
++    /// pub fn read(filename: String) -> io::Result<String> {
++    ///     unimplemented!();
++    /// }
++    /// ```
++    pub MISSING_ERRORS_DOC,
++    pedantic,
++    "`pub fn` returns `Result` without `# Errors` in doc comment"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `fn main() { .. }` in doctests
++    ///
++    /// **Why is this bad?** The test can be shorter (and likely more readable)
++    /// if the `fn main()` is left implicit.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Examples:**
++    /// ``````rust
++    /// /// An example of a doctest with a `main()` function
++    /// ///
++    /// /// # Examples
++    /// ///
++    /// /// ```
++    /// /// fn main() {
++    /// ///     // this needs not be in an `fn`
++    /// /// }
++    /// /// ```
++    /// fn needless_main() {
++    ///     unimplemented!();
++    /// }
++    /// ``````
++    pub NEEDLESS_DOCTEST_MAIN,
++    style,
++    "presence of `fn main() {` in code examples"
++}
++
++#[allow(clippy::module_name_repetitions)]
++#[derive(Clone)]
++pub struct DocMarkdown {
++    valid_idents: FxHashSet<String>,
++    in_trait_impl: bool,
++}
++
++impl DocMarkdown {
++    pub fn new(valid_idents: FxHashSet<String>) -> Self {
++        Self {
++            valid_idents,
++            in_trait_impl: false,
++        }
++    }
++}
++
++impl_lint_pass!(DocMarkdown => [DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, NEEDLESS_DOCTEST_MAIN]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DocMarkdown {
++    fn check_crate(&mut self, cx: &LateContext<'a, 'tcx>, krate: &'tcx hir::Crate<'_>) {
++        check_attrs(cx, &self.valid_idents, &krate.item.attrs);
++    }
++
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) {
++        let headers = check_attrs(cx, &self.valid_idents, &item.attrs);
++        match item.kind {
++            hir::ItemKind::Fn(ref sig, _, body_id) => {
++                if !(is_entrypoint_fn(cx, cx.tcx.hir().local_def_id(item.hir_id).to_def_id())
++                    || in_external_macro(cx.tcx.sess, item.span))
++                {
++                    lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, Some(body_id));
++                }
++            },
++            hir::ItemKind::Impl {
++                of_trait: ref trait_ref,
++                ..
++            } => {
++                self.in_trait_impl = trait_ref.is_some();
++            },
++            _ => {},
++        }
++    }
++
++    fn check_item_post(&mut self, _cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) {
++        if let hir::ItemKind::Impl { .. } = item.kind {
++            self.in_trait_impl = false;
++        }
++    }
++
++    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem<'_>) {
++        let headers = check_attrs(cx, &self.valid_idents, &item.attrs);
++        if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
++            if !in_external_macro(cx.tcx.sess, item.span) {
++                lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, None);
++            }
++        }
++    }
++
++    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem<'_>) {
++        let headers = check_attrs(cx, &self.valid_idents, &item.attrs);
++        if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) {
++            return;
++        }
++        if let hir::ImplItemKind::Fn(ref sig, body_id) = item.kind {
++            lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, Some(body_id));
++        }
++    }
++}
++
++fn lint_for_missing_headers<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    hir_id: hir::HirId,
++    span: impl Into<MultiSpan> + Copy,
++    sig: &hir::FnSig<'_>,
++    headers: DocHeaders,
++    body_id: Option<hir::BodyId>,
++) {
++    if !cx.access_levels.is_exported(hir_id) {
++        return; // Private functions do not require doc comments
++    }
++    if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe {
++        span_lint(
++            cx,
++            MISSING_SAFETY_DOC,
++            span,
++            "unsafe function's docs miss `# Safety` section",
++        );
++    }
++    if !headers.errors {
++        if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) {
++            span_lint(
++                cx,
++                MISSING_ERRORS_DOC,
++                span,
++                "docs for function returning `Result` missing `# Errors` section",
++            );
++        } else {
++            if_chain! {
++                if let Some(body_id) = body_id;
++                if let Some(future) = cx.tcx.lang_items().future_trait();
++                let def_id = cx.tcx.hir().body_owner_def_id(body_id);
++                let mir = cx.tcx.optimized_mir(def_id.to_def_id());
++                let ret_ty = mir.return_ty();
++                if implements_trait(cx, ret_ty, future, &[]);
++                if let ty::Opaque(_, subs) = ret_ty.kind;
++                if let Some(gen) = subs.types().next();
++                if let ty::Generator(_, subs, _) = gen.kind;
++                if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym!(result_type));
++                then {
++                    span_lint(
++                        cx,
++                        MISSING_ERRORS_DOC,
++                        span,
++                        "docs for function returning `Result` missing `# Errors` section",
++                    );
++                }
++            }
++        }
++    }
++}
++
++/// Cleanup documentation decoration (`///` and such).
++///
++/// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or
++/// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we
++/// need to keep track of
++/// the spans but this function is inspired from the later.
++#[allow(clippy::cast_possible_truncation)]
++#[must_use]
++pub fn strip_doc_comment_decoration(comment: &str, span: Span) -> (String, Vec<(usize, Span)>) {
++    // one-line comments lose their prefix
++    const ONELINERS: &[&str] = &["///!", "///", "//!", "//"];
++    for prefix in ONELINERS {
++        if comment.starts_with(*prefix) {
++            let doc = &comment[prefix.len()..];
++            let mut doc = doc.to_owned();
++            doc.push('\n');
++            return (
++                doc.to_owned(),
++                vec![(doc.len(), span.with_lo(span.lo() + BytePos(prefix.len() as u32)))],
++            );
++        }
++    }
++
++    if comment.starts_with("/*") {
++        let doc = &comment[3..comment.len() - 2];
++        let mut sizes = vec![];
++        let mut contains_initial_stars = false;
++        for line in doc.lines() {
++            let offset = line.as_ptr() as usize - comment.as_ptr() as usize;
++            debug_assert_eq!(offset as u32 as usize, offset);
++            contains_initial_stars |= line.trim_start().starts_with('*');
++            // +1 for the newline
++            sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(offset as u32))));
++        }
++        if !contains_initial_stars {
++            return (doc.to_string(), sizes);
++        }
++        // remove the initial '*'s if any
++        let mut no_stars = String::with_capacity(doc.len());
++        for line in doc.lines() {
++            let mut chars = line.chars();
++            while let Some(c) = chars.next() {
++                if c.is_whitespace() {
++                    no_stars.push(c);
++                } else {
++                    no_stars.push(if c == '*' { ' ' } else { c });
++                    break;
++                }
++            }
++            no_stars.push_str(chars.as_str());
++            no_stars.push('\n');
++        }
++        return (no_stars, sizes);
++    }
++
++    panic!("not a doc-comment: {}", comment);
++}
++
++#[derive(Copy, Clone)]
++struct DocHeaders {
++    safety: bool,
++    errors: bool,
++}
++
++fn check_attrs<'a>(cx: &LateContext<'_, '_>, valid_idents: &FxHashSet<String>, attrs: &'a [Attribute]) -> DocHeaders {
++    let mut doc = String::new();
++    let mut spans = vec![];
++
++    for attr in attrs {
++        if let AttrKind::DocComment(ref comment) = attr.kind {
++            let comment = comment.to_string();
++            let (comment, current_spans) = strip_doc_comment_decoration(&comment, attr.span);
++            spans.extend_from_slice(&current_spans);
++            doc.push_str(&comment);
++        } else if attr.check_name(sym!(doc)) {
++            // ignore mix of sugared and non-sugared doc
++            // don't trigger the safety or errors check
++            return DocHeaders {
++                safety: true,
++                errors: true,
++            };
++        }
++    }
++
++    let mut current = 0;
++    for &mut (ref mut offset, _) in &mut spans {
++        let offset_copy = *offset;
++        *offset = current;
++        current += offset_copy;
++    }
++
++    if doc.is_empty() {
++        return DocHeaders {
++            safety: false,
++            errors: false,
++        };
++    }
++
++    let parser = pulldown_cmark::Parser::new(&doc).into_offset_iter();
++    // Iterate over all `Events` and combine consecutive events into one
++    let events = parser.coalesce(|previous, current| {
++        use pulldown_cmark::Event::Text;
++
++        let previous_range = previous.1;
++        let current_range = current.1;
++
++        match (previous.0, current.0) {
++            (Text(previous), Text(current)) => {
++                let mut previous = previous.to_string();
++                previous.push_str(&current);
++                Ok((Text(previous.into()), previous_range))
++            },
++            (previous, current) => Err(((previous, previous_range), (current, current_range))),
++        }
++    });
++    check_doc(cx, valid_idents, events, &spans)
++}
++
++const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail", "edition2018"];
++
++fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(
++    cx: &LateContext<'_, '_>,
++    valid_idents: &FxHashSet<String>,
++    events: Events,
++    spans: &[(usize, Span)],
++) -> DocHeaders {
++    // true if a safety header was found
++    use pulldown_cmark::CodeBlockKind;
++    use pulldown_cmark::Event::{
++        Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
++    };
++    use pulldown_cmark::Tag::{CodeBlock, Heading, Link};
++
++    let mut headers = DocHeaders {
++        safety: false,
++        errors: false,
++    };
++    let mut in_code = false;
++    let mut in_link = None;
++    let mut in_heading = false;
++    let mut is_rust = false;
++    for (event, range) in events {
++        match event {
++            Start(CodeBlock(ref kind)) => {
++                in_code = true;
++                if let CodeBlockKind::Fenced(lang) = kind {
++                    is_rust =
++                        lang.is_empty() || !lang.contains("ignore") && lang.split(',').any(|i| RUST_CODE.contains(&i));
++                }
++            },
++            End(CodeBlock(_)) => {
++                in_code = false;
++                is_rust = false;
++            },
++            Start(Link(_, url, _)) => in_link = Some(url),
++            End(Link(..)) => in_link = None,
++            Start(Heading(_)) => in_heading = true,
++            End(Heading(_)) => in_heading = false,
++            Start(_tag) | End(_tag) => (), // We don't care about other tags
++            Html(_html) => (),             // HTML is weird, just ignore it
++            SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
++            FootnoteReference(text) | Text(text) => {
++                if Some(&text) == in_link.as_ref() {
++                    // Probably a link of the form `<http://example.com>`
++                    // Which are represented as a link to "http://example.com" with
++                    // text "http://example.com" by pulldown-cmark
++                    continue;
++                }
++                headers.safety |= in_heading && text.trim() == "Safety";
++                headers.errors |= in_heading && text.trim() == "Errors";
++                let index = match spans.binary_search_by(|c| c.0.cmp(&range.start)) {
++                    Ok(o) => o,
++                    Err(e) => e - 1,
++                };
++                let (begin, span) = spans[index];
++                if in_code {
++                    if is_rust {
++                        check_code(cx, &text, span);
++                    }
++                } else {
++                    // Adjust for the beginning of the current `Event`
++                    let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
++
++                    check_text(cx, valid_idents, &text, span);
++                }
++            },
++        }
++    }
++    headers
++}
++
++static LEAVE_MAIN_PATTERNS: &[&str] = &["static", "fn main() {}", "extern crate", "async fn main() {"];
++
++fn check_code(cx: &LateContext<'_, '_>, text: &str, span: Span) {
++    if text.contains("fn main() {") && !LEAVE_MAIN_PATTERNS.iter().any(|p| text.contains(p)) {
++        span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
++    }
++}
++
++fn check_text(cx: &LateContext<'_, '_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) {
++    for word in text.split(|c: char| c.is_whitespace() || c == '\'') {
++        // Trim punctuation as in `some comment (see foo::bar).`
++        //                                                   ^^
++        // Or even as in `_foo bar_` which is emphasized.
++        let word = word.trim_matches(|c: char| !c.is_alphanumeric());
++
++        if valid_idents.contains(word) {
++            continue;
++        }
++
++        // Adjust for the current word
++        let offset = word.as_ptr() as usize - text.as_ptr() as usize;
++        let span = Span::new(
++            span.lo() + BytePos::from_usize(offset),
++            span.lo() + BytePos::from_usize(offset + word.len()),
++            span.ctxt(),
++        );
++
++        check_word(cx, word, span);
++    }
++}
++
++fn check_word(cx: &LateContext<'_, '_>, word: &str, span: Span) {
++    /// Checks if a string is camel-case, i.e., contains at least two uppercase
++    /// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok).
++    /// Plurals are also excluded (`IDs` is ok).
++    fn is_camel_case(s: &str) -> bool {
++        if s.starts_with(|c: char| c.is_digit(10)) {
++            return false;
++        }
++
++        let s = if s.ends_with('s') { &s[..s.len() - 1] } else { s };
++
++        s.chars().all(char::is_alphanumeric)
++            && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1
++            && s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
++    }
++
++    fn has_underscore(s: &str) -> bool {
++        s != "_" && !s.contains("\\_") && s.contains('_')
++    }
++
++    fn has_hyphen(s: &str) -> bool {
++        s != "-" && s.contains('-')
++    }
++
++    if let Ok(url) = Url::parse(word) {
++        // try to get around the fact that `foo::bar` parses as a valid URL
++        if !url.cannot_be_a_base() {
++            span_lint(
++                cx,
++                DOC_MARKDOWN,
++                span,
++                "you should put bare URLs between `<`/`>` or make a proper Markdown link",
++            );
++
++            return;
++        }
++    }
++
++    // We assume that mixed-case words are not meant to be put inside bacticks. (Issue #2343)
++    if has_underscore(word) && has_hyphen(word) {
++        return;
++    }
++
++    if has_underscore(word) || word.contains("::") || is_camel_case(word) {
++        span_lint(
++            cx,
++            DOC_MARKDOWN,
++            span,
++            &format!("you should put `{}` between ticks in the documentation", word),
++        );
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..44f85d1ea6e19cd77709f87fddeccf0b93db1139
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,95 @@@
++//! Lint on unnecessary double comparisons. Some examples:
++
++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::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for double comparisons that could be simplified to a single expression.
++    ///
++    ///
++    /// **Why is this bad?** Readability.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let x = 1;
++    /// # let y = 2;
++    /// if x == y || x < y {}
++    /// ```
++    ///
++    /// Could be written as:
++    ///
++    /// ```rust
++    /// # let x = 1;
++    /// # let y = 2;
++    /// if x <= y {}
++    /// ```
++    pub DOUBLE_COMPARISONS,
++    complexity,
++    "unnecessary double comparisons that can be simplified"
++}
++
++declare_lint_pass!(DoubleComparisons => [DOUBLE_COMPARISONS]);
++
++impl<'a, 'tcx> DoubleComparisons {
++    #[allow(clippy::similar_names)]
++    fn check_binop(cx: &LateContext<'a, 'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, span: Span) {
++        let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) {
++            (ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => {
++                (lb.node, llhs, lrhs, rb.node, rlhs, rrhs)
++            },
++            _ => return,
++        };
++        let mut spanless_eq = SpanlessEq::new(cx).ignore_fn();
++        if !(spanless_eq.eq_expr(&llhs, &rlhs) && spanless_eq.eq_expr(&lrhs, &rrhs)) {
++            return;
++        }
++        macro_rules! lint_double_comparison {
++            ($op:tt) => {{
++                let mut applicability = Applicability::MachineApplicable;
++                let lhs_str = snippet_with_applicability(cx, llhs.span, "", &mut applicability);
++                let rhs_str = snippet_with_applicability(cx, lrhs.span, "", &mut applicability);
++                let sugg = format!("{} {} {}", lhs_str, stringify!($op), rhs_str);
++                span_lint_and_sugg(
++                    cx,
++                    DOUBLE_COMPARISONS,
++                    span,
++                    "This binary expression can be simplified",
++                    "try",
++                    sugg,
++                    applicability,
++                );
++            }};
++        }
++        #[rustfmt::skip]
++        match (op, lkind, rkind) {
++            (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => {
++                lint_double_comparison!(<=)
++            },
++            (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Eq) => {
++                lint_double_comparison!(>=)
++            },
++            (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Lt) => {
++                lint_double_comparison!(!=)
++            },
++            (BinOpKind::And, BinOpKind::Le, BinOpKind::Ge) | (BinOpKind::And, BinOpKind::Ge, BinOpKind::Le) => {
++                lint_double_comparison!(==)
++            },
++            _ => (),
++        };
++    }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DoubleComparisons {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = expr.kind {
++            Self::check_binop(cx, kind.node, lhs, rhs, expr.span);
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7f2ff8b9b26f62bb42252aca0293c3e49e3ca410
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,75 @@@
++use crate::utils::span_lint;
++use rustc_ast::ast::{Expr, ExprKind};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for unnecessary double parentheses.
++    ///
++    /// **Why is this bad?** This makes code harder to read and might indicate a
++    /// mistake.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # fn foo(bar: usize) {}
++    /// ((0));
++    /// foo((0));
++    /// ((1, 2));
++    /// ```
++    pub DOUBLE_PARENS,
++    complexity,
++    "Warn on unnecessary double parentheses"
++}
++
++declare_lint_pass!(DoubleParens => [DOUBLE_PARENS]);
++
++impl EarlyLintPass for DoubleParens {
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++        if expr.span.from_expansion() {
++            return;
++        }
++
++        match expr.kind {
++            ExprKind::Paren(ref in_paren) => match in_paren.kind {
++                ExprKind::Paren(_) | ExprKind::Tup(_) => {
++                    span_lint(
++                        cx,
++                        DOUBLE_PARENS,
++                        expr.span,
++                        "Consider removing unnecessary double parentheses",
++                    );
++                },
++                _ => {},
++            },
++            ExprKind::Call(_, ref params) => {
++                if params.len() == 1 {
++                    let param = &params[0];
++                    if let ExprKind::Paren(_) = param.kind {
++                        span_lint(
++                            cx,
++                            DOUBLE_PARENS,
++                            param.span,
++                            "Consider removing unnecessary double parentheses",
++                        );
++                    }
++                }
++            },
++            ExprKind::MethodCall(_, ref params) => {
++                if params.len() == 2 {
++                    let param = &params[1];
++                    if let ExprKind::Paren(_) = param.kind {
++                        span_lint(
++                            cx,
++                            DOUBLE_PARENS,
++                            param.span,
++                            "Consider removing unnecessary double parentheses",
++                        );
++                    }
++                }
++            },
++            _ => {},
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f49668082791392167b0ebf70af75a4e81ae433b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,69 @@@
++use crate::utils::{match_def_path, paths, span_lint};
++use if_chain::if_chain;
++use rustc_hir::{GenericBound, GenericParam, WhereBoundPredicate, WherePredicate};
++use rustc_lint::LateLintPass;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for generics with `std::ops::Drop` as bounds.
++    ///
++    /// **Why is this bad?** `Drop` bounds do not really accomplish anything.
++    /// A type may have compiler-generated drop glue without implementing the
++    /// `Drop` trait itself. The `Drop` trait also only has one method,
++    /// `Drop::drop`, and that function is by fiat not callable in user code.
++    /// So there is really no use case for using `Drop` in trait bounds.
++    ///
++    /// The most likely use case of a drop bound is to distinguish between types
++    /// that have destructors and types that don't. Combined with specialization,
++    /// a naive coder would write an implementation that assumed a type could be
++    /// trivially dropped, then write a specialization for `T: Drop` that actually
++    /// calls the destructor. Except that doing so is not correct; String, for
++    /// example, doesn't actually implement Drop, but because String contains a
++    /// Vec, assuming it can be trivially dropped will leak memory.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// fn foo<T: Drop>() {}
++    /// ```
++    pub DROP_BOUNDS,
++    correctness,
++    "Bounds of the form `T: Drop` are useless"
++}
++
++const DROP_BOUNDS_SUMMARY: &str = "Bounds of the form `T: Drop` are useless. \
++                                   Use `std::mem::needs_drop` to detect if a type has drop glue.";
++
++declare_lint_pass!(DropBounds => [DROP_BOUNDS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DropBounds {
++    fn check_generic_param(&mut self, cx: &rustc_lint::LateContext<'a, 'tcx>, p: &'tcx GenericParam<'_>) {
++        for bound in p.bounds.iter() {
++            lint_bound(cx, bound);
++        }
++    }
++    fn check_where_predicate(&mut self, cx: &rustc_lint::LateContext<'a, 'tcx>, p: &'tcx WherePredicate<'_>) {
++        if let WherePredicate::BoundPredicate(WhereBoundPredicate { bounds, .. }) = p {
++            for bound in *bounds {
++                lint_bound(cx, bound);
++            }
++        }
++    }
++}
++
++fn lint_bound<'a, 'tcx>(cx: &rustc_lint::LateContext<'a, 'tcx>, bound: &'tcx GenericBound<'_>) {
++    if_chain! {
++        if let GenericBound::Trait(t, _) = bound;
++        if let Some(def_id) = t.trait_ref.path.res.opt_def_id();
++        if match_def_path(cx, def_id, &paths::DROP_TRAIT);
++        then {
++            span_lint(
++                cx,
++                DROP_BOUNDS,
++                t.span,
++                DROP_BOUNDS_SUMMARY
++            );
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9de9056c14029a8d1bb6bd7a8b4244068ca889d6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,160 @@@
++use crate::utils::{is_copy, match_def_path, paths, qpath_res, span_lint_and_note};
++use if_chain::if_chain;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for calls to `std::mem::drop` with a reference
++    /// instead of an owned value.
++    ///
++    /// **Why is this bad?** Calling `drop` on a reference will only drop the
++    /// reference itself, which is a no-op. It will not call the `drop` method (from
++    /// the `Drop` trait implementation) on the underlying referenced value, which
++    /// is likely what was intended.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// let mut lock_guard = mutex.lock();
++    /// std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex
++    /// // still locked
++    /// operation_that_requires_mutex_to_be_unlocked();
++    /// ```
++    pub DROP_REF,
++    correctness,
++    "calls to `std::mem::drop` with a reference instead of an owned value"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for calls to `std::mem::forget` with a reference
++    /// instead of an owned value.
++    ///
++    /// **Why is this bad?** Calling `forget` on a reference will only forget the
++    /// reference itself, which is a no-op. It will not forget the underlying
++    /// referenced
++    /// value, which is likely what was intended.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x = Box::new(1);
++    /// std::mem::forget(&x) // Should have been forget(x), x will still be dropped
++    /// ```
++    pub FORGET_REF,
++    correctness,
++    "calls to `std::mem::forget` with a reference instead of an owned value"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for calls to `std::mem::drop` with a value
++    /// that derives the Copy trait
++    ///
++    /// **Why is this bad?** Calling `std::mem::drop` [does nothing for types that
++    /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the
++    /// value will be copied and moved into the function on invocation.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x: i32 = 42; // i32 implements Copy
++    /// std::mem::drop(x) // A copy of x is passed to the function, leaving the
++    ///                   // original unaffected
++    /// ```
++    pub DROP_COPY,
++    correctness,
++    "calls to `std::mem::drop` with a value that implements Copy"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for calls to `std::mem::forget` with a value that
++    /// derives the Copy trait
++    ///
++    /// **Why is this bad?** Calling `std::mem::forget` [does nothing for types that
++    /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the
++    /// value will be copied and moved into the function on invocation.
++    ///
++    /// An alternative, but also valid, explanation is that Copy types do not
++    /// implement
++    /// the Drop trait, which means they have no destructors. Without a destructor,
++    /// there
++    /// is nothing for `std::mem::forget` to ignore.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x: i32 = 42; // i32 implements Copy
++    /// std::mem::forget(x) // A copy of x is passed to the function, leaving the
++    ///                     // original unaffected
++    /// ```
++    pub FORGET_COPY,
++    correctness,
++    "calls to `std::mem::forget` with a value that implements Copy"
++}
++
++const DROP_REF_SUMMARY: &str = "calls to `std::mem::drop` with a reference instead of an owned value. \
++                                Dropping a reference does nothing.";
++const FORGET_REF_SUMMARY: &str = "calls to `std::mem::forget` with a reference instead of an owned value. \
++                                  Forgetting a reference does nothing.";
++const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that implements `Copy`. \
++                                 Dropping a copy leaves the original intact.";
++const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \
++                                   Forgetting a copy leaves the original intact.";
++
++declare_lint_pass!(DropForgetRef => [DROP_REF, FORGET_REF, DROP_COPY, FORGET_COPY]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DropForgetRef {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            if let ExprKind::Call(ref path, ref args) = expr.kind;
++            if let ExprKind::Path(ref qpath) = path.kind;
++            if args.len() == 1;
++            if let Some(def_id) = qpath_res(cx, qpath, path.hir_id).opt_def_id();
++            then {
++                let lint;
++                let msg;
++                let arg = &args[0];
++                let arg_ty = cx.tables.expr_ty(arg);
++
++                if let ty::Ref(..) = arg_ty.kind {
++                    if match_def_path(cx, def_id, &paths::DROP) {
++                        lint = DROP_REF;
++                        msg = DROP_REF_SUMMARY.to_string();
++                    } else if match_def_path(cx, def_id, &paths::MEM_FORGET) {
++                        lint = FORGET_REF;
++                        msg = FORGET_REF_SUMMARY.to_string();
++                    } else {
++                        return;
++                    }
++                    span_lint_and_note(cx,
++                                       lint,
++                                       expr.span,
++                                       &msg,
++                                       Some(arg.span),
++                                       &format!("argument has type `{}`", arg_ty));
++                } else if is_copy(cx, arg_ty) {
++                    if match_def_path(cx, def_id, &paths::DROP) {
++                        lint = DROP_COPY;
++                        msg = DROP_COPY_SUMMARY.to_string();
++                    } else if match_def_path(cx, def_id, &paths::MEM_FORGET) {
++                        lint = FORGET_COPY;
++                        msg = FORGET_COPY_SUMMARY.to_string();
++                    } else {
++                        return;
++                    }
++                    span_lint_and_note(cx,
++                                       lint,
++                                       expr.span,
++                                       &msg,
++                                       Some(arg.span),
++                                       &format!("argument has type {}", arg_ty));
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b35a8facf8b9951cef7d316e9829fe00fac3f75a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,65 @@@
++use if_chain::if_chain;
++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::Spanned;
++
++use crate::consts::{constant, Constant};
++use crate::utils::paths;
++use crate::utils::{match_type, snippet_with_applicability, span_lint_and_sugg, walk_ptrs_ty};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for calculation of subsecond microseconds or milliseconds
++    /// from other `Duration` methods.
++    ///
++    /// **Why is this bad?** It's more concise to call `Duration::subsec_micros()` or
++    /// `Duration::subsec_millis()` than to calculate them.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # use std::time::Duration;
++    /// let dur = Duration::new(5, 0);
++    /// let _micros = dur.subsec_nanos() / 1_000;
++    /// let _millis = dur.subsec_nanos() / 1_000_000;
++    /// ```
++    pub DURATION_SUBSEC,
++    complexity,
++    "checks for calculation of subsecond microseconds or milliseconds"
++}
++
++declare_lint_pass!(DurationSubsec => [DURATION_SUBSEC]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DurationSubsec {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            if let ExprKind::Binary(Spanned { node: BinOpKind::Div, .. }, ref left, ref right) = expr.kind;
++            if let ExprKind::MethodCall(ref method_path, _ , ref args) = left.kind;
++            if match_type(cx, walk_ptrs_ty(cx.tables.expr_ty(&args[0])), &paths::DURATION);
++            if let Some((Constant::Int(divisor), _)) = constant(cx, cx.tables, right);
++            then {
++                let suggested_fn = match (method_path.ident.as_str().as_ref(), divisor) {
++                    ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis",
++                    ("subsec_nanos", 1_000) => "subsec_micros",
++                    _ => return,
++                };
++                let mut applicability = Applicability::MachineApplicable;
++                span_lint_and_sugg(
++                    cx,
++                    DURATION_SUBSEC,
++                    expr.span,
++                    &format!("Calling `{}()` is more concise than this calculation", suggested_fn),
++                    "try",
++                    format!(
++                        "{}.{}()",
++                        snippet_with_applicability(cx, args[0].span, "_", &mut applicability),
++                        suggested_fn
++                    ),
++                    applicability,
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..95123e6ff6fe2740b912e8438f426592dc3834d2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,72 @@@
++//! Lint on if expressions with an else if, but without a final else branch.
++
++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.
++    ///
++    /// **Why is this bad?** Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10).
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # fn a() {}
++    /// # fn b() {}
++    /// # let x: i32 = 1;
++    /// if x.is_positive() {
++    ///     a();
++    /// } else if x.is_negative() {
++    ///     b();
++    /// }
++    /// ```
++    ///
++    /// Could be written:
++    ///
++    /// ```rust
++    /// # fn a() {}
++    /// # fn b() {}
++    /// # let x: i32 = 1;
++    /// if x.is_positive() {
++    ///     a();
++    /// } else if x.is_negative() {
++    ///     b();
++    /// } else {
++    ///     // We don't care about zero.
++    /// }
++    /// ```
++    pub ELSE_IF_WITHOUT_ELSE,
++    restriction,
++    "`if` expression with an `else if`, but without a final `else` branch"
++}
++
++declare_lint_pass!(ElseIfWithoutElse => [ELSE_IF_WITHOUT_ELSE]);
++
++impl EarlyLintPass for ElseIfWithoutElse {
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, mut item: &Expr) {
++        if in_external_macro(cx.sess(), item.span) {
++            return;
++        }
++
++        while let ExprKind::If(_, _, Some(ref els)) = item.kind {
++            if let ExprKind::If(_, _, None) = els.kind {
++                span_lint_and_help(
++                    cx,
++                    ELSE_IF_WITHOUT_ELSE,
++                    els.span,
++                    "`if` expression with an `else if`, but without a final `else`",
++                    None,
++                    "add an `else` block here",
++                );
++            }
++
++            item = els;
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3bfef6f4bed129bc2756dce58b9dca2bc3e7e938
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,60 @@@
++//! lint when there is an enum with no variants
++
++use crate::utils::span_lint_and_help;
++use rustc_hir::{Item, ItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `enum`s with no variants.
++    ///
++    /// **Why is this bad?** If you want to introduce a type which
++    /// can't be instantiated, you should use `!` (the never type),
++    /// or a wrapper around it, because `!` has more extensive
++    /// compiler support (type inference, etc...) and wrappers
++    /// around it are the conventional way to define an uninhabited type.
++    /// For further information visit [never type documentation](https://doc.rust-lang.org/std/primitive.never.html)
++    ///
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// Bad:
++    /// ```rust
++    /// enum Test {}
++    /// ```
++    ///
++    /// Good:
++    /// ```rust
++    /// #![feature(never_type)]
++    ///
++    /// struct Test(!);
++    /// ```
++    pub EMPTY_ENUM,
++    pedantic,
++    "enum with no variants"
++}
++
++declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EmptyEnum {
++    fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) {
++        let did = cx.tcx.hir().local_def_id(item.hir_id);
++        if let ItemKind::Enum(..) = item.kind {
++            let ty = cx.tcx.type_of(did);
++            let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
++            if adt.variants.is_empty() {
++                span_lint_and_help(
++                    cx,
++                    EMPTY_ENUM,
++                    item.span,
++                    "enum with no variants",
++                    None,
++                    "consider using the uninhabited type `!` (never type) or a wrapper \
++                    around it to introduce a type which can't be instantiated",
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7b332c761a0c40b17ff3243e4457136c30d31887
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,187 @@@
++use crate::utils::SpanlessEq;
++use crate::utils::{get_item_name, higher, is_type_diagnostic_item, match_type, paths, snippet, snippet_opt};
++use crate::utils::{snippet_with_applicability, span_lint_and_then, walk_ptrs_ty};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_hir::{BorrowKind, Expr, ExprKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for uses of `contains_key` + `insert` on `HashMap`
++    /// or `BTreeMap`.
++    ///
++    /// **Why is this bad?** Using `entry` is more efficient.
++    ///
++    /// **Known problems:** Some false negatives, eg.:
++    /// ```rust
++    /// # use std::collections::HashMap;
++    /// # let mut map = HashMap::new();
++    /// # let v = 1;
++    /// # let k = 1;
++    /// if !map.contains_key(&k) {
++    ///     map.insert(k.clone(), v);
++    /// }
++    /// ```
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # use std::collections::HashMap;
++    /// # let mut map = HashMap::new();
++    /// # let k = 1;
++    /// # let v = 1;
++    /// if !map.contains_key(&k) {
++    ///     map.insert(k, v);
++    /// }
++    /// ```
++    /// can both be rewritten as:
++    /// ```rust
++    /// # use std::collections::HashMap;
++    /// # let mut map = HashMap::new();
++    /// # let k = 1;
++    /// # let v = 1;
++    /// map.entry(k).or_insert(v);
++    /// ```
++    pub MAP_ENTRY,
++    perf,
++    "use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`"
++}
++
++declare_lint_pass!(HashMapPass => [MAP_ENTRY]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for HashMapPass {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if let Some((ref check, ref then_block, ref else_block)) = higher::if_block(&expr) {
++            if let ExprKind::Unary(UnOp::UnNot, ref check) = check.kind {
++                if let Some((ty, map, key)) = check_cond(cx, check) {
++                    // in case of `if !m.contains_key(&k) { m.insert(k, v); }`
++                    // we can give a better error message
++                    let sole_expr = {
++                        else_block.is_none()
++                            && if let ExprKind::Block(ref then_block, _) = then_block.kind {
++                                (then_block.expr.is_some() as usize) + then_block.stmts.len() == 1
++                            } else {
++                                true
++                            }
++                        // XXXManishearth we can also check for if/else blocks containing `None`.
++                    };
++
++                    let mut visitor = InsertVisitor {
++                        cx,
++                        span: expr.span,
++                        ty,
++                        map,
++                        key,
++                        sole_expr,
++                    };
++
++                    walk_expr(&mut visitor, &**then_block);
++                }
++            } else if let Some(ref else_block) = *else_block {
++                if let Some((ty, map, key)) = check_cond(cx, check) {
++                    let mut visitor = InsertVisitor {
++                        cx,
++                        span: expr.span,
++                        ty,
++                        map,
++                        key,
++                        sole_expr: false,
++                    };
++
++                    walk_expr(&mut visitor, else_block);
++                }
++            }
++        }
++    }
++}
++
++fn check_cond<'a, 'tcx, 'b>(
++    cx: &'a LateContext<'a, 'tcx>,
++    check: &'b Expr<'b>,
++) -> Option<(&'static str, &'b Expr<'b>, &'b Expr<'b>)> {
++    if_chain! {
++        if let ExprKind::MethodCall(ref path, _, ref params) = check.kind;
++        if params.len() >= 2;
++        if path.ident.name == sym!(contains_key);
++        if let ExprKind::AddrOf(BorrowKind::Ref, _, ref key) = params[1].kind;
++        then {
++            let map = &params[0];
++            let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(map));
++
++            return if match_type(cx, obj_ty, &paths::BTREEMAP) {
++                Some(("BTreeMap", map, key))
++            }
++            else if is_type_diagnostic_item(cx, obj_ty, sym!(hashmap_type)) {
++                Some(("HashMap", map, key))
++            }
++            else {
++                None
++            };
++        }
++    }
++
++    None
++}
++
++struct InsertVisitor<'a, 'tcx, 'b> {
++    cx: &'a LateContext<'a, 'tcx>,
++    span: Span,
++    ty: &'static str,
++    map: &'b Expr<'b>,
++    key: &'b Expr<'b>,
++    sole_expr: bool,
++}
++
++impl<'a, 'tcx, 'b> Visitor<'tcx> for InsertVisitor<'a, 'tcx, 'b> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            if let ExprKind::MethodCall(ref path, _, ref params) = expr.kind;
++            if params.len() == 3;
++            if path.ident.name == sym!(insert);
++            if get_item_name(self.cx, self.map) == get_item_name(self.cx, &params[0]);
++            if SpanlessEq::new(self.cx).eq_expr(self.key, &params[1]);
++            if snippet_opt(self.cx, self.map.span) == snippet_opt(self.cx, params[0].span);
++            then {
++                span_lint_and_then(self.cx, MAP_ENTRY, self.span,
++                                   &format!("usage of `contains_key` followed by `insert` on a `{}`", self.ty), |diag| {
++                    if self.sole_expr {
++                        let mut app = Applicability::MachineApplicable;
++                        let help = format!("{}.entry({}).or_insert({});",
++                                           snippet_with_applicability(self.cx, self.map.span, "map", &mut app),
++                                           snippet_with_applicability(self.cx, params[1].span, "..", &mut app),
++                                           snippet_with_applicability(self.cx, params[2].span, "..", &mut app));
++
++                        diag.span_suggestion(
++                            self.span,
++                            "consider using",
++                            help,
++                            Applicability::MachineApplicable, // snippet
++                        );
++                    }
++                    else {
++                        let help = format!("consider using `{}.entry({})`",
++                                           snippet(self.cx, self.map.span, "map"),
++                                           snippet(self.cx, params[1].span, ".."));
++
++                        diag.span_label(
++                            self.span,
++                            &help,
++                        );
++                    }
++                });
++            }
++        }
++
++        if !self.sole_expr {
++            walk_expr(self, expr);
++        }
++    }
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a1fed3fb6e205595ae9edccff2e49124ace435fb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,82 @@@
++//! lint on C-like enums that are `repr(isize/usize)` and have values that
++//! don't fit into an `i32`
++
++use crate::consts::{miri_to_const, Constant};
++use crate::utils::span_lint;
++use rustc_ast::ast::{IntTy, UintTy};
++use rustc_hir::{Item, ItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_middle::ty::util::IntTypeExt;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use std::convert::TryFrom;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for C-like enumerations that are
++    /// `repr(isize/usize)` and have values that don't fit into an `i32`.
++    ///
++    /// **Why is this bad?** This will truncate the variant value on 32 bit
++    /// architectures, but works fine on 64 bit.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # #[cfg(target_pointer_width = "64")]
++    /// #[repr(usize)]
++    /// enum NonPortable {
++    ///     X = 0x1_0000_0000,
++    ///     Y = 0,
++    /// }
++    /// ```
++    pub ENUM_CLIKE_UNPORTABLE_VARIANT,
++    correctness,
++    "C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32`"
++}
++
++declare_lint_pass!(UnportableVariant => [ENUM_CLIKE_UNPORTABLE_VARIANT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnportableVariant {
++    #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_sign_loss)]
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++        if cx.tcx.data_layout.pointer_size.bits() != 64 {
++            return;
++        }
++        if let ItemKind::Enum(def, _) = &item.kind {
++            for var in def.variants {
++                if let Some(anon_const) = &var.disr_expr {
++                    let def_id = cx.tcx.hir().body_owner_def_id(anon_const.body);
++                    let mut ty = cx.tcx.type_of(def_id.to_def_id());
++                    let constant = cx
++                        .tcx
++                        .const_eval_poly(def_id.to_def_id())
++                        .ok()
++                        .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty));
++                    if let Some(Constant::Int(val)) = constant.and_then(miri_to_const) {
++                        if let ty::Adt(adt, _) = ty.kind {
++                            if adt.is_enum() {
++                                ty = adt.repr.discr_type().to_ty(cx.tcx);
++                            }
++                        }
++                        match ty.kind {
++                            ty::Int(IntTy::Isize) => {
++                                let val = ((val as i128) << 64) >> 64;
++                                if i32::try_from(val).is_ok() {
++                                    continue;
++                                }
++                            },
++                            ty::Uint(UintTy::Usize) if val > u128::from(u32::max_value()) => {},
++                            _ => continue,
++                        }
++                        span_lint(
++                            cx,
++                            ENUM_CLIKE_UNPORTABLE_VARIANT,
++                            var.span,
++                            "Clike enum variant discriminant is not portable to 32-bit targets",
++                        );
++                    };
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a5871cf0cd4ddefda14a9341d4eb5803fd81dbd8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,305 @@@
++//! 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 rustc_ast::ast::{EnumDef, Item, ItemKind, VisibilityKind};
++use rustc_lint::{EarlyContext, EarlyLintPass, Lint};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++use rustc_span::symbol::Symbol;
++
++declare_clippy_lint! {
++    /// **What it does:** Detects enumeration variants that are prefixed or suffixed
++    /// by the same characters.
++    ///
++    /// **Why is this bad?** Enumeration variant names should specify their variant,
++    /// not repeat the enumeration name.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// enum Cake {
++    ///     BlackForestCake,
++    ///     HummingbirdCake,
++    ///     BattenbergCake,
++    /// }
++    /// ```
++    pub ENUM_VARIANT_NAMES,
++    style,
++    "enums where all variants share a prefix/postfix"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Detects enumeration variants that are prefixed or suffixed
++    /// by the same characters.
++    ///
++    /// **Why is this bad?** Enumeration variant names should specify their variant,
++    /// not repeat the enumeration name.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// enum Cake {
++    ///     BlackForestCake,
++    ///     HummingbirdCake,
++    ///     BattenbergCake,
++    /// }
++    /// ```
++    pub PUB_ENUM_VARIANT_NAMES,
++    pedantic,
++    "enums where all variants share a prefix/postfix"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Detects type names that are prefixed or suffixed by the
++    /// containing module's name.
++    ///
++    /// **Why is this bad?** It requires the user to type the module name twice.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// mod cake {
++    ///     struct BlackForestCake;
++    /// }
++    /// ```
++    pub MODULE_NAME_REPETITIONS,
++    pedantic,
++    "type names prefixed/postfixed with their containing module's name"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for modules that have the same name as their
++    /// parent module
++    ///
++    /// **Why is this bad?** A typical beginner mistake is to have `mod foo;` and
++    /// again `mod foo { ..
++    /// }` in `foo.rs`.
++    /// The expectation is that items inside the inner `mod foo { .. }` are then
++    /// available
++    /// through `foo::x`, but they are only available through
++    /// `foo::foo::x`.
++    /// If this is done on purpose, it would be better to choose a more
++    /// representative module name.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// // lib.rs
++    /// mod foo;
++    /// // foo.rs
++    /// mod foo {
++    ///     ...
++    /// }
++    /// ```
++    pub MODULE_INCEPTION,
++    style,
++    "modules that have the same name as their parent module"
++}
++
++pub struct EnumVariantNames {
++    modules: Vec<(Symbol, String)>,
++    threshold: u64,
++}
++
++impl EnumVariantNames {
++    #[must_use]
++    pub fn new(threshold: u64) -> Self {
++        Self {
++            modules: Vec::new(),
++            threshold,
++        }
++    }
++}
++
++impl_lint_pass!(EnumVariantNames => [
++    ENUM_VARIANT_NAMES,
++    PUB_ENUM_VARIANT_NAMES,
++    MODULE_NAME_REPETITIONS,
++    MODULE_INCEPTION
++]);
++
++/// Returns the number of chars that match from the start
++#[must_use]
++fn partial_match(pre: &str, name: &str) -> usize {
++    let mut name_iter = name.chars();
++    let _ = name_iter.next_back(); // make sure the name is never fully matched
++    pre.chars().zip(name_iter).take_while(|&(l, r)| l == r).count()
++}
++
++/// Returns the number of chars that match from the end
++#[must_use]
++fn partial_rmatch(post: &str, name: &str) -> usize {
++    let mut name_iter = name.chars();
++    let _ = name_iter.next(); // make sure the name is never fully matched
++    post.chars()
++        .rev()
++        .zip(name_iter.rev())
++        .take_while(|&(l, r)| l == r)
++        .count()
++}
++
++fn check_variant(
++    cx: &EarlyContext<'_>,
++    threshold: u64,
++    def: &EnumDef,
++    item_name: &str,
++    item_name_chars: usize,
++    span: Span,
++    lint: &'static Lint,
++) {
++    if (def.variants.len() as u64) < threshold {
++        return;
++    }
++    for var in &def.variants {
++        let name = var.ident.name.as_str();
++        if partial_match(item_name, &name) == item_name_chars
++            && name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase())
++            && name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric())
++        {
++            span_lint(cx, lint, var.span, "Variant name starts with the enum's name");
++        }
++        if partial_rmatch(item_name, &name) == item_name_chars {
++            span_lint(cx, lint, var.span, "Variant name ends with the enum's name");
++        }
++    }
++    let first = &def.variants[0].ident.name.as_str();
++    let mut pre = &first[..camel_case::until(&*first)];
++    let mut post = &first[camel_case::from(&*first)..];
++    for var in &def.variants {
++        let name = var.ident.name.as_str();
++
++        let pre_match = partial_match(pre, &name);
++        pre = &pre[..pre_match];
++        let pre_camel = camel_case::until(pre);
++        pre = &pre[..pre_camel];
++        while let Some((next, last)) = name[pre.len()..].chars().zip(pre.chars().rev()).next() {
++            if next.is_numeric() {
++                return;
++            }
++            if next.is_lowercase() {
++                let last = pre.len() - last.len_utf8();
++                let last_camel = camel_case::until(&pre[..last]);
++                pre = &pre[..last_camel];
++            } else {
++                break;
++            }
++        }
++
++        let post_match = partial_rmatch(post, &name);
++        let post_end = post.len() - post_match;
++        post = &post[post_end..];
++        let post_camel = camel_case::from(post);
++        post = &post[post_camel..];
++    }
++    let (what, value) = match (pre.is_empty(), post.is_empty()) {
++        (true, true) => return,
++        (false, _) => ("pre", pre),
++        (true, false) => ("post", post),
++    };
++    span_lint_and_help(
++        cx,
++        lint,
++        span,
++        &format!("All variants have the same {}fix: `{}`", what, value),
++        None,
++        &format!(
++            "remove the {}fixes and use full paths to \
++             the variants instead of glob imports",
++            what
++        ),
++    );
++}
++
++#[must_use]
++fn to_camel_case(item_name: &str) -> String {
++    let mut s = String::new();
++    let mut up = true;
++    for c in item_name.chars() {
++        if c.is_uppercase() {
++            // we only turn snake case text into CamelCase
++            return item_name.to_string();
++        }
++        if c == '_' {
++            up = true;
++            continue;
++        }
++        if up {
++            up = false;
++            s.extend(c.to_uppercase());
++        } else {
++            s.push(c);
++        }
++    }
++    s
++}
++
++impl EarlyLintPass for EnumVariantNames {
++    fn check_item_post(&mut self, _cx: &EarlyContext<'_>, _item: &Item) {
++        let last = self.modules.pop();
++        assert!(last.is_some());
++    }
++
++    #[allow(clippy::similar_names)]
++    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
++        let item_name = item.ident.name.as_str();
++        let item_name_chars = item_name.chars().count();
++        let item_camel = to_camel_case(&item_name);
++        if !item.span.from_expansion() && is_present_in_source(cx, item.span) {
++            if let Some(&(ref mod_name, ref mod_camel)) = self.modules.last() {
++                // constants don't have surrounding modules
++                if !mod_camel.is_empty() {
++                    if mod_name == &item.ident.name {
++                        if let ItemKind::Mod(..) = item.kind {
++                            span_lint(
++                                cx,
++                                MODULE_INCEPTION,
++                                item.span,
++                                "module has the same name as its containing module",
++                            );
++                        }
++                    }
++                    if item.vis.node.is_pub() {
++                        let matching = partial_match(mod_camel, &item_camel);
++                        let rmatching = partial_rmatch(mod_camel, &item_camel);
++                        let nchars = mod_camel.chars().count();
++
++                        let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric();
++
++                        if matching == nchars {
++                            match item_camel.chars().nth(nchars) {
++                                Some(c) if is_word_beginning(c) => span_lint(
++                                    cx,
++                                    MODULE_NAME_REPETITIONS,
++                                    item.span,
++                                    "item name starts with its containing module's name",
++                                ),
++                                _ => (),
++                            }
++                        }
++                        if rmatching == nchars {
++                            span_lint(
++                                cx,
++                                MODULE_NAME_REPETITIONS,
++                                item.span,
++                                "item name ends with its containing module's name",
++                            );
++                        }
++                    }
++                }
++            }
++        }
++        if let ItemKind::Enum(ref def, _) = item.kind {
++            let lint = match item.vis.node {
++                VisibilityKind::Public => PUB_ENUM_VARIANT_NAMES,
++                _ => ENUM_VARIANT_NAMES,
++            };
++            check_variant(cx, self.threshold, def, &item_name, item_name_chars, item.span, lint);
++        }
++        self.modules.push((item.ident.name, item_camel));
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..098d47bdd40cb10aba75123be872c7e28aeae5af
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,229 @@@
++use crate::utils::{
++    implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, SpanlessEq,
++};
++use rustc_errors::Applicability;
++use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for equal operands to comparison, logical and
++    /// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
++    /// `||`, `&`, `|`, `^`, `-` and `/`).
++    ///
++    /// **Why is this bad?** This is usually just a typo or a copy and paste error.
++    ///
++    /// **Known problems:** False negatives: We had some false positives regarding
++    /// calls (notably [racer](https://github.com/phildawes/racer) had one instance
++    /// of `x.pop() && x.pop()`), so we removed matching any function or method
++    /// calls. We may introduce a whitelist of known pure functions in the future.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let x = 1;
++    /// if x + 1 == x + 1 {}
++    /// ```
++    pub EQ_OP,
++    correctness,
++    "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for arguments to `==` which have their address
++    /// taken to satisfy a bound
++    /// and suggests to dereference the other argument instead
++    ///
++    /// **Why is this bad?** It is more idiomatic to dereference the other argument.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// &x == y
++    /// ```
++    pub OP_REF,
++    style,
++    "taking a reference to satisfy the type constraints on `==`"
++}
++
++declare_lint_pass!(EqOp => [EQ_OP, OP_REF]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EqOp {
++    #[allow(clippy::similar_names, clippy::too_many_lines)]
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        if let ExprKind::Binary(op, ref left, ref right) = e.kind {
++            if e.span.from_expansion() {
++                return;
++            }
++            let macro_with_not_op = |expr_kind: &ExprKind<'_>| {
++                if let ExprKind::Unary(_, ref expr) = *expr_kind {
++                    in_macro(expr.span)
++                } else {
++                    false
++                }
++            };
++            if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) {
++                return;
++            }
++            if is_valid_operator(op) && SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) {
++                span_lint(
++                    cx,
++                    EQ_OP,
++                    e.span,
++                    &format!("equal expressions as operands to `{}`", op.node.as_str()),
++                );
++                return;
++            }
++            let (trait_id, requires_ref) = match op.node {
++                BinOpKind::Add => (cx.tcx.lang_items().add_trait(), false),
++                BinOpKind::Sub => (cx.tcx.lang_items().sub_trait(), false),
++                BinOpKind::Mul => (cx.tcx.lang_items().mul_trait(), false),
++                BinOpKind::Div => (cx.tcx.lang_items().div_trait(), false),
++                BinOpKind::Rem => (cx.tcx.lang_items().rem_trait(), false),
++                // don't lint short circuiting ops
++                BinOpKind::And | BinOpKind::Or => return,
++                BinOpKind::BitXor => (cx.tcx.lang_items().bitxor_trait(), false),
++                BinOpKind::BitAnd => (cx.tcx.lang_items().bitand_trait(), false),
++                BinOpKind::BitOr => (cx.tcx.lang_items().bitor_trait(), false),
++                BinOpKind::Shl => (cx.tcx.lang_items().shl_trait(), false),
++                BinOpKind::Shr => (cx.tcx.lang_items().shr_trait(), false),
++                BinOpKind::Ne | BinOpKind::Eq => (cx.tcx.lang_items().eq_trait(), true),
++                BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ge | BinOpKind::Gt => {
++                    (cx.tcx.lang_items().partial_ord_trait(), true)
++                },
++            };
++            if let Some(trait_id) = trait_id {
++                #[allow(clippy::match_same_arms)]
++                match (&left.kind, &right.kind) {
++                    // do not suggest to dereference literals
++                    (&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {},
++                    // &foo == &bar
++                    (&ExprKind::AddrOf(BorrowKind::Ref, _, ref l), &ExprKind::AddrOf(BorrowKind::Ref, _, ref r)) => {
++                        let lty = cx.tables.expr_ty(l);
++                        let rty = cx.tables.expr_ty(r);
++                        let lcpy = is_copy(cx, lty);
++                        let rcpy = is_copy(cx, rty);
++                        // either operator autorefs or both args are copyable
++                        if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) {
++                            span_lint_and_then(
++                                cx,
++                                OP_REF,
++                                e.span,
++                                "needlessly taken reference of both operands",
++                                |diag| {
++                                    let lsnip = snippet(cx, l.span, "...").to_string();
++                                    let rsnip = snippet(cx, r.span, "...").to_string();
++                                    multispan_sugg(
++                                        diag,
++                                        "use the values directly".to_string(),
++                                        vec![(left.span, lsnip), (right.span, rsnip)],
++                                    );
++                                },
++                            )
++                        } else if lcpy
++                            && !rcpy
++                            && implements_trait(cx, lty, trait_id, &[cx.tables.expr_ty(right).into()])
++                        {
++                            span_lint_and_then(
++                                cx,
++                                OP_REF,
++                                e.span,
++                                "needlessly taken reference of left operand",
++                                |diag| {
++                                    let lsnip = snippet(cx, l.span, "...").to_string();
++                                    diag.span_suggestion(
++                                        left.span,
++                                        "use the left value directly",
++                                        lsnip,
++                                        Applicability::MaybeIncorrect, // FIXME #2597
++                                    );
++                                },
++                            )
++                        } else if !lcpy
++                            && rcpy
++                            && implements_trait(cx, cx.tables.expr_ty(left), trait_id, &[rty.into()])
++                        {
++                            span_lint_and_then(
++                                cx,
++                                OP_REF,
++                                e.span,
++                                "needlessly taken reference of right operand",
++                                |diag| {
++                                    let rsnip = snippet(cx, r.span, "...").to_string();
++                                    diag.span_suggestion(
++                                        right.span,
++                                        "use the right value directly",
++                                        rsnip,
++                                        Applicability::MaybeIncorrect, // FIXME #2597
++                                    );
++                                },
++                            )
++                        }
++                    },
++                    // &foo == bar
++                    (&ExprKind::AddrOf(BorrowKind::Ref, _, ref l), _) => {
++                        let lty = cx.tables.expr_ty(l);
++                        let lcpy = is_copy(cx, lty);
++                        if (requires_ref || lcpy)
++                            && implements_trait(cx, lty, trait_id, &[cx.tables.expr_ty(right).into()])
++                        {
++                            span_lint_and_then(
++                                cx,
++                                OP_REF,
++                                e.span,
++                                "needlessly taken reference of left operand",
++                                |diag| {
++                                    let lsnip = snippet(cx, l.span, "...").to_string();
++                                    diag.span_suggestion(
++                                        left.span,
++                                        "use the left value directly",
++                                        lsnip,
++                                        Applicability::MaybeIncorrect, // FIXME #2597
++                                    );
++                                },
++                            )
++                        }
++                    },
++                    // foo == &bar
++                    (_, &ExprKind::AddrOf(BorrowKind::Ref, _, ref r)) => {
++                        let rty = cx.tables.expr_ty(r);
++                        let rcpy = is_copy(cx, rty);
++                        if (requires_ref || rcpy)
++                            && implements_trait(cx, cx.tables.expr_ty(left), trait_id, &[rty.into()])
++                        {
++                            span_lint_and_then(cx, OP_REF, e.span, "taken reference of right operand", |diag| {
++                                let rsnip = snippet(cx, r.span, "...").to_string();
++                                diag.span_suggestion(
++                                    right.span,
++                                    "use the right value directly",
++                                    rsnip,
++                                    Applicability::MaybeIncorrect, // FIXME #2597
++                                );
++                            })
++                        }
++                    },
++                    _ => {},
++                }
++            }
++        }
++    }
++}
++
++fn is_valid_operator(op: BinOp) -> bool {
++    match op.node {
++        BinOpKind::Sub
++        | BinOpKind::Div
++        | BinOpKind::Eq
++        | BinOpKind::Lt
++        | BinOpKind::Le
++        | BinOpKind::Gt
++        | BinOpKind::Ge
++        | BinOpKind::Ne
++        | BinOpKind::And
++        | BinOpKind::Or
++        | BinOpKind::BitXor
++        | BinOpKind::BitAnd
++        | BinOpKind::BitOr => true,
++        _ => false,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3ff0506e28d007ffeefdcb784b2685a3029a1a0e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,59 @@@
++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`.
++    ///
++    /// **Why is this bad?** The whole expression can be replaced by zero.
++    /// This is most likely not the intended outcome and should probably be
++    /// corrected
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x = 1;
++    /// 0 / x;
++    /// 0 * x;
++    /// x & 0;
++    /// ```
++    pub ERASING_OP,
++    correctness,
++    "using erasing operations, e.g., `x * 0` or `y & 0`"
++}
++
++declare_lint_pass!(ErasingOp => [ERASING_OP]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ErasingOp {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        if e.span.from_expansion() {
++            return;
++        }
++        if let ExprKind::Binary(ref cmp, ref left, ref right) = e.kind {
++            match cmp.node {
++                BinOpKind::Mul | BinOpKind::BitAnd => {
++                    check(cx, left, e.span);
++                    check(cx, right, e.span);
++                },
++                BinOpKind::Div => check(cx, left, e.span),
++                _ => (),
++            }
++        }
++    }
++}
++
++fn check(cx: &LateContext<'_, '_>, e: &Expr<'_>, span: Span) {
++    if let Some(Constant::Int(0)) = constant_simple(cx, cx.tables, e) {
++        span_lint(
++            cx,
++            ERASING_OP,
++            span,
++            "this operation will always return zero. This is likely not the intended outcome",
++        );
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1ec60a0e6e67ac9e00c42e4920d438fafd25ad8c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,164 @@@
++use rustc_hir::intravisit;
++use rustc_hir::{self, Body, FnDecl, HirId, HirIdSet, ItemKind, Node};
++use rustc_infer::infer::TyCtxtInferExt;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++use rustc_target::abi::LayoutOf;
++use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase};
++
++use crate::utils::span_lint;
++
++#[derive(Copy, Clone)]
++pub struct BoxedLocal {
++    pub too_large_for_stack: u64,
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `Box<T>` where an unboxed `T` would
++    /// work fine.
++    ///
++    /// **Why is this bad?** This is an unnecessary allocation, and bad for
++    /// performance. It is only necessary to allocate if you wish to move the box
++    /// into something.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # fn foo(bar: usize) {}
++    /// let x = Box::new(1);
++    /// foo(*x);
++    /// println!("{}", *x);
++    /// ```
++    pub BOXED_LOCAL,
++    perf,
++    "using `Box<T>` where unnecessary"
++}
++
++fn is_non_trait_box(ty: Ty<'_>) -> bool {
++    ty.is_box() && !ty.boxed_ty().is_trait()
++}
++
++struct EscapeDelegate<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    set: HirIdSet,
++    too_large_for_stack: u64,
++}
++
++impl_lint_pass!(BoxedLocal => [BOXED_LOCAL]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxedLocal {
++    fn check_fn(
++        &mut self,
++        cx: &LateContext<'a, 'tcx>,
++        _: intravisit::FnKind<'tcx>,
++        _: &'tcx FnDecl<'_>,
++        body: &'tcx Body<'_>,
++        _: Span,
++        hir_id: HirId,
++    ) {
++        // If the method is an impl for a trait, don't warn.
++        let parent_id = cx.tcx.hir().get_parent_item(hir_id);
++        let parent_node = cx.tcx.hir().find(parent_id);
++
++        if let Some(Node::Item(item)) = parent_node {
++            if let ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
++                return;
++            }
++        }
++
++        let mut v = EscapeDelegate {
++            cx,
++            set: HirIdSet::default(),
++            too_large_for_stack: self.too_large_for_stack,
++        };
++
++        let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
++        cx.tcx.infer_ctxt().enter(|infcx| {
++            ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.tables).consume_body(body);
++        });
++
++        for node in v.set {
++            span_lint(
++                cx,
++                BOXED_LOCAL,
++                cx.tcx.hir().span(node),
++                "local variable doesn't need to be boxed here",
++            );
++        }
++    }
++}
++
++// TODO: Replace with Map::is_argument(..) when it's fixed
++fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool {
++    match map.find(id) {
++        Some(Node::Binding(_)) => (),
++        _ => return false,
++    }
++
++    match map.find(map.get_parent_node(id)) {
++        Some(Node::Param(_)) => true,
++        _ => false,
++    }
++}
++
++impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
++    fn consume(&mut self, cmt: &Place<'tcx>, mode: ConsumeMode) {
++        if cmt.projections.is_empty() {
++            if let PlaceBase::Local(lid) = cmt.base {
++                if let ConsumeMode::Move = mode {
++                    // moved out or in. clearly can't be localized
++                    self.set.remove(&lid);
++                }
++                let map = &self.cx.tcx.hir();
++                if let Some(Node::Binding(_)) = map.find(cmt.hir_id) {
++                    if self.set.contains(&lid) {
++                        // let y = x where x is known
++                        // remove x, insert y
++                        self.set.insert(cmt.hir_id);
++                        self.set.remove(&lid);
++                    }
++                }
++            }
++        }
++    }
++
++    fn borrow(&mut self, cmt: &Place<'tcx>, _: ty::BorrowKind) {
++        if cmt.projections.is_empty() {
++            if let PlaceBase::Local(lid) = cmt.base {
++                self.set.remove(&lid);
++            }
++        }
++    }
++
++    fn mutate(&mut self, cmt: &Place<'tcx>) {
++        if cmt.projections.is_empty() {
++            let map = &self.cx.tcx.hir();
++            if is_argument(*map, cmt.hir_id) {
++                // Skip closure arguments
++                let parent_id = map.get_parent_node(cmt.hir_id);
++                if let Some(Node::Expr(..)) = map.find(map.get_parent_node(parent_id)) {
++                    return;
++                }
++
++                if is_non_trait_box(cmt.ty) && !self.is_large_box(cmt.ty) {
++                    self.set.insert(cmt.hir_id);
++                }
++                return;
++            }
++        }
++    }
++}
++
++impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {
++    fn is_large_box(&self, ty: Ty<'tcx>) -> bool {
++        // Large types need to be boxed to avoid stack overflows.
++        if ty.is_box() {
++            self.cx.layout_of(ty.boxed_ty()).map_or(0, |l| l.size.bytes()) > self.too_large_for_stack
++        } else {
++            false
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e3e1136b67693aa856500090f28503b2f097bcef
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,227 @@@
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++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,
++};
++
++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
++    /// get adjusted are ignored.
++    ///
++    /// **Why is this bad?** Needlessly creating a closure adds code for no benefit
++    /// and gives the optimizer more work.
++    ///
++    /// **Known problems:** If creating the closure inside the closure has a side-
++    /// effect then moving the closure creation out will change when that side-
++    /// effect runs.
++    /// See rust-lang/rust-clippy#1439 for more details.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// xs.map(|x| foo(x))
++    /// ```
++    /// where `foo(_)` is a plain function that takes the exact argument type of
++    /// `x`.
++    pub REDUNDANT_CLOSURE,
++    style,
++    "redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for closures which only invoke a method on the closure
++    /// argument and can be replaced by referencing the method directly.
++    ///
++    /// **Why is this bad?** It's unnecessary to create the closure.
++    ///
++    /// **Known problems:** rust-lang/rust-clippy#3071, rust-lang/rust-clippy#4002,
++    /// rust-lang/rust-clippy#3942
++    ///
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// Some('a').map(|s| s.to_uppercase());
++    /// ```
++    /// may be rewritten as
++    /// ```rust,ignore
++    /// Some('a').map(char::to_uppercase);
++    /// ```
++    pub REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
++    pedantic,
++    "redundant closures for method calls"
++}
++
++declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EtaReduction {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if in_external_macro(cx.sess(), expr.span) {
++            return;
++        }
++
++        match expr.kind {
++            ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args) => {
++                for arg in args {
++                    check_closure(cx, arg)
++                }
++            },
++            _ => (),
++        }
++    }
++}
++
++fn check_closure(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++    if let ExprKind::Closure(_, ref decl, eid, _, _) = expr.kind {
++        let body = cx.tcx.hir().body(eid);
++        let ex = &body.value;
++
++        if_chain!(
++            if let ExprKind::Call(ref caller, ref args) = ex.kind;
++
++            if let ExprKind::Path(_) = caller.kind;
++
++            // Not the same number of arguments, there is no way the closure is the same as the function return;
++            if args.len() == decl.inputs.len();
++
++            // Are the expression or the arguments type-adjusted? Then we need the closure
++            if !(is_adjusted(cx, ex) || args.iter().any(|arg| is_adjusted(cx, arg)));
++
++            let fn_ty = cx.tables.expr_ty(caller);
++
++            if matches!(fn_ty.kind, ty::FnDef(_, _) | ty::FnPtr(_) | ty::Closure(_, _));
++
++            if !type_is_unsafe_function(cx, fn_ty);
++
++            if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter());
++
++            then {
++                span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure found", |diag| {
++                    if let Some(snippet) = snippet_opt(cx, caller.span) {
++                        diag.span_suggestion(
++                            expr.span,
++                            "remove closure as shown",
++                            snippet,
++                            Applicability::MachineApplicable,
++                        );
++                    }
++                });
++            }
++        );
++
++        if_chain!(
++            if let ExprKind::MethodCall(ref path, _, ref args) = ex.kind;
++
++            // Not the same number of arguments, there is no way the closure is the same as the function return;
++            if args.len() == decl.inputs.len();
++
++            // Are the expression or the arguments type-adjusted? Then we need the closure
++            if !(is_adjusted(cx, ex) || args.iter().skip(1).any(|arg| is_adjusted(cx, arg)));
++
++            let method_def_id = cx.tables.type_dependent_def_id(ex.hir_id).unwrap();
++            if !type_is_unsafe_function(cx, cx.tcx.type_of(method_def_id));
++
++            if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter());
++
++            if let Some(name) = get_ufcs_type_name(cx, method_def_id, &args[0]);
++
++            then {
++                span_lint_and_sugg(
++                    cx,
++                    REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
++                    expr.span,
++                    "redundant closure found",
++                    "remove closure as shown",
++                    format!("{}::{}", name, path.ident.name),
++                    Applicability::MachineApplicable,
++                );
++            }
++        );
++    }
++}
++
++/// Tries to determine the type for universal function call to be used instead of the closure
++fn get_ufcs_type_name(cx: &LateContext<'_, '_>, method_def_id: def_id::DefId, self_arg: &Expr<'_>) -> Option<String> {
++    let expected_type_of_self = &cx.tcx.fn_sig(method_def_id).inputs_and_output().skip_binder()[0];
++    let actual_type_of_self = &cx.tables.node_type(self_arg.hir_id);
++
++    if let Some(trait_id) = cx.tcx.trait_of_item(method_def_id) {
++        if match_borrow_depth(expected_type_of_self, &actual_type_of_self)
++            && implements_trait(cx, actual_type_of_self, trait_id, &[])
++        {
++            return Some(cx.tcx.def_path_str(trait_id));
++        }
++    }
++
++    cx.tcx.impl_of_method(method_def_id).and_then(|_| {
++        //a type may implicitly implement other type's methods (e.g. Deref)
++        if match_types(expected_type_of_self, &actual_type_of_self) {
++            return Some(get_type_name(cx, &actual_type_of_self));
++        }
++        None
++    })
++}
++
++fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
++    match (&lhs.kind, &rhs.kind) {
++        (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(&t1, &t2),
++        (l, r) => match (l, r) {
++            (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _)) => false,
++            (_, _) => true,
++        },
++    }
++}
++
++fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
++    match (&lhs.kind, &rhs.kind) {
++        (ty::Bool, ty::Bool)
++        | (ty::Char, ty::Char)
++        | (ty::Int(_), ty::Int(_))
++        | (ty::Uint(_), ty::Uint(_))
++        | (ty::Str, ty::Str) => true,
++        (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_types(t1, t2),
++        (ty::Array(t1, _), ty::Array(t2, _)) | (ty::Slice(t1), ty::Slice(t2)) => match_types(t1, t2),
++        (ty::Adt(def1, _), ty::Adt(def2, _)) => def1 == def2,
++        (_, _) => false,
++    }
++}
++
++fn get_type_name(cx: &LateContext<'_, '_>, ty: Ty<'_>) -> String {
++    match ty.kind {
++        ty::Adt(t, _) => cx.tcx.def_path_str(t.did),
++        ty::Ref(_, r, _) => get_type_name(cx, &r),
++        _ => ty.to_string(),
++    }
++}
++
++fn compare_inputs(
++    closure_inputs: &mut dyn Iterator<Item = &Param<'_>>,
++    call_args: &mut dyn Iterator<Item = &Expr<'_>>,
++) -> bool {
++    for (closure_input, function_arg) in closure_inputs.zip(call_args) {
++        if let PatKind::Binding(_, _, ident, _) = closure_input.pat.kind {
++            // XXXManishearth Should I be checking the binding mode here?
++            if let ExprKind::Path(QPath::Resolved(None, ref p)) = function_arg.kind {
++                if p.segments.len() != 1 {
++                    // If it's a proper path, it can't be a local variable
++                    return false;
++                }
++                if p.segments[0].ident.name != ident.name {
++                    // The two idents should be the same
++                    return false;
++                }
++            } else {
++                return false;
++            }
++        } else {
++            return false;
++        }
++    }
++    true
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5206266ccf2a6d5e76d11b2c3be52c24c0537b63
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,355 @@@
++use crate::utils::{get_parent_expr, span_lint, span_lint_and_note};
++use if_chain::if_chain;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_hir::{def, BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, QPath, Stmt, StmtKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for a read and a write to the same variable where
++    /// whether the read occurs before or after the write depends on the evaluation
++    /// order of sub-expressions.
++    ///
++    /// **Why is this bad?** It is often confusing to read. In addition, the
++    /// sub-expression evaluation order for Rust is not well documented.
++    ///
++    /// **Known problems:** Code which intentionally depends on the evaluation
++    /// order, or which is correct for any evaluation order.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let mut x = 0;
++    /// let a = {
++    ///     x = 1;
++    ///     1
++    /// } + x;
++    /// // Unclear whether a is 1 or 2.
++    /// ```
++    pub EVAL_ORDER_DEPENDENCE,
++    complexity,
++    "whether a variable read occurs before a write depends on sub-expression evaluation order"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for diverging calls that are not match arms or
++    /// statements.
++    ///
++    /// **Why is this bad?** It is often confusing to read. In addition, the
++    /// sub-expression evaluation order for Rust is not well documented.
++    ///
++    /// **Known problems:** Someone might want to use `some_bool || panic!()` as a
++    /// shorthand.
++    ///
++    /// **Example:**
++    /// ```rust,no_run
++    /// # fn b() -> bool { true }
++    /// # fn c() -> bool { true }
++    /// let a = b() || panic!() || c();
++    /// // `c()` is dead, `panic!()` is only called if `b()` returns `false`
++    /// let x = (a, b, c, panic!());
++    /// // can simply be replaced by `panic!()`
++    /// ```
++    pub DIVERGING_SUB_EXPRESSION,
++    complexity,
++    "whether an expression contains a diverging sub expression"
++}
++
++declare_lint_pass!(EvalOrderDependence => [EVAL_ORDER_DEPENDENCE, DIVERGING_SUB_EXPRESSION]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EvalOrderDependence {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        // Find a write to a local variable.
++        match expr.kind {
++            ExprKind::Assign(ref lhs, ..) | ExprKind::AssignOp(_, ref lhs, _) => {
++                if let ExprKind::Path(ref qpath) = lhs.kind {
++                    if let QPath::Resolved(_, ref path) = *qpath {
++                        if path.segments.len() == 1 {
++                            if let def::Res::Local(var) = cx.tables.qpath_res(qpath, lhs.hir_id) {
++                                let mut visitor = ReadVisitor {
++                                    cx,
++                                    var,
++                                    write_expr: expr,
++                                    last_expr: expr,
++                                };
++                                check_for_unsequenced_reads(&mut visitor);
++                            }
++                        }
++                    }
++                }
++            },
++            _ => {},
++        }
++    }
++    fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) {
++        match stmt.kind {
++            StmtKind::Local(ref local) => {
++                if let Local { init: Some(ref e), .. } = **local {
++                    DivergenceVisitor { cx }.visit_expr(e);
++                }
++            },
++            StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => DivergenceVisitor { cx }.maybe_walk_expr(e),
++            StmtKind::Item(..) => {},
++        }
++    }
++}
++
++struct DivergenceVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++}
++
++impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
++    fn maybe_walk_expr(&mut self, e: &'tcx Expr<'_>) {
++        match e.kind {
++            ExprKind::Closure(..) => {},
++            ExprKind::Match(ref e, arms, _) => {
++                self.visit_expr(e);
++                for arm in arms {
++                    if let Some(Guard::If(if_expr)) = arm.guard {
++                        self.visit_expr(if_expr)
++                    }
++                    // make sure top level arm expressions aren't linted
++                    self.maybe_walk_expr(&*arm.body);
++                }
++            },
++            _ => walk_expr(self, e),
++        }
++    }
++    fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) {
++        span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges");
++    }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
++        match e.kind {
++            ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => self.report_diverging_sub_expr(e),
++            ExprKind::Call(ref func, _) => {
++                let typ = self.cx.tables.expr_ty(func);
++                match typ.kind {
++                    ty::FnDef(..) | ty::FnPtr(_) => {
++                        let sig = typ.fn_sig(self.cx.tcx);
++                        if let ty::Never = self.cx.tcx.erase_late_bound_regions(&sig).output().kind {
++                            self.report_diverging_sub_expr(e);
++                        }
++                    },
++                    _ => {},
++                }
++            },
++            ExprKind::MethodCall(..) => {
++                let borrowed_table = self.cx.tables;
++                if borrowed_table.expr_ty(e).is_never() {
++                    self.report_diverging_sub_expr(e);
++                }
++            },
++            _ => {
++                // do not lint expressions referencing objects of type `!`, as that required a
++                // diverging expression
++                // to begin with
++            },
++        }
++        self.maybe_walk_expr(e);
++    }
++    fn visit_block(&mut self, _: &'tcx Block<'_>) {
++        // don't continue over blocks, LateLintPass already does that
++    }
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++/// Walks up the AST from the given write expression (`vis.write_expr`) looking
++/// for reads to the same variable that are unsequenced relative to the write.
++///
++/// This means reads for which there is a common ancestor between the read and
++/// the write such that
++///
++/// * evaluating the ancestor necessarily evaluates both the read and the write (for example, `&x`
++///   and `|| x = 1` don't necessarily evaluate `x`), and
++///
++/// * which one is evaluated first depends on the order of sub-expression evaluation. Blocks, `if`s,
++///   loops, `match`es, and the short-circuiting logical operators are considered to have a defined
++///   evaluation order.
++///
++/// When such a read is found, the lint is triggered.
++fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) {
++    let map = &vis.cx.tcx.hir();
++    let mut cur_id = vis.write_expr.hir_id;
++    loop {
++        let parent_id = map.get_parent_node(cur_id);
++        if parent_id == cur_id {
++            break;
++        }
++        let parent_node = match map.find(parent_id) {
++            Some(parent) => parent,
++            None => break,
++        };
++
++        let stop_early = match parent_node {
++            Node::Expr(expr) => check_expr(vis, expr),
++            Node::Stmt(stmt) => check_stmt(vis, stmt),
++            Node::Item(_) => {
++                // We reached the top of the function, stop.
++                break;
++            },
++            _ => StopEarly::KeepGoing,
++        };
++        match stop_early {
++            StopEarly::Stop => break,
++            StopEarly::KeepGoing => {},
++        }
++
++        cur_id = parent_id;
++    }
++}
++
++/// Whether to stop early for the loop in `check_for_unsequenced_reads`. (If
++/// `check_expr` weren't an independent function, this would be unnecessary and
++/// we could just use `break`).
++enum StopEarly {
++    KeepGoing,
++    Stop,
++}
++
++fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) -> StopEarly {
++    if expr.hir_id == vis.last_expr.hir_id {
++        return StopEarly::KeepGoing;
++    }
++
++    match expr.kind {
++        ExprKind::Array(_)
++        | ExprKind::Tup(_)
++        | ExprKind::MethodCall(..)
++        | ExprKind::Call(_, _)
++        | ExprKind::Assign(..)
++        | ExprKind::Index(_, _)
++        | ExprKind::Repeat(_, _)
++        | ExprKind::Struct(_, _, _) => {
++            walk_expr(vis, expr);
++        },
++        ExprKind::Binary(op, _, _) | ExprKind::AssignOp(op, _, _) => {
++            if op.node == BinOpKind::And || op.node == BinOpKind::Or {
++                // x && y and x || y always evaluate x first, so these are
++                // strictly sequenced.
++            } else {
++                walk_expr(vis, expr);
++            }
++        },
++        ExprKind::Closure(_, _, _, _, _) => {
++            // Either
++            //
++            // * `var` is defined in the closure body, in which case we've reached the top of the enclosing
++            //   function and can stop, or
++            //
++            // * `var` is captured by the closure, in which case, because evaluating a closure does not evaluate
++            //   its body, we don't necessarily have a write, so we need to stop to avoid generating false
++            //   positives.
++            //
++            // This is also the only place we need to stop early (grrr).
++            return StopEarly::Stop;
++        },
++        // All other expressions either have only one child or strictly
++        // sequence the evaluation order of their sub-expressions.
++        _ => {},
++    }
++
++    vis.last_expr = expr;
++
++    StopEarly::KeepGoing
++}
++
++fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly {
++    match stmt.kind {
++        StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => check_expr(vis, expr),
++        // If the declaration is of a local variable, check its initializer
++        // expression if it has one. Otherwise, keep going.
++        StmtKind::Local(ref local) => local
++            .init
++            .as_ref()
++            .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)),
++        _ => StopEarly::KeepGoing,
++    }
++}
++
++/// A visitor that looks for reads from a variable.
++struct ReadVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    /// The ID of the variable we're looking for.
++    var: HirId,
++    /// The expressions where the write to the variable occurred (for reporting
++    /// in the lint).
++    write_expr: &'tcx Expr<'tcx>,
++    /// The last (highest in the AST) expression we've checked, so we know not
++    /// to recheck it.
++    last_expr: &'tcx Expr<'tcx>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        if expr.hir_id == self.last_expr.hir_id {
++            return;
++        }
++
++        match expr.kind {
++            ExprKind::Path(ref qpath) => {
++                if_chain! {
++                    if let QPath::Resolved(None, ref path) = *qpath;
++                    if path.segments.len() == 1;
++                    if let def::Res::Local(local_id) = self.cx.tables.qpath_res(qpath, expr.hir_id);
++                    if local_id == self.var;
++                    // Check that this is a read, not a write.
++                    if !is_in_assignment_position(self.cx, expr);
++                    then {
++                        span_lint_and_note(
++                            self.cx,
++                            EVAL_ORDER_DEPENDENCE,
++                            expr.span,
++                            "unsequenced read of a variable",
++                            Some(self.write_expr.span),
++                            "whether read occurs before this write depends on evaluation order"
++                        );
++                    }
++                }
++            }
++            // We're about to descend a closure. Since we don't know when (or
++            // if) the closure will be evaluated, any reads in it might not
++            // occur here (or ever). Like above, bail to avoid false positives.
++            ExprKind::Closure(_, _, _, _, _) |
++
++            // We want to avoid a false positive when a variable name occurs
++            // only to have its address taken, so we stop here. Technically,
++            // this misses some weird cases, eg.
++            //
++            // ```rust
++            // let mut x = 0;
++            // let a = foo(&{x = 1; x}, x);
++            // ```
++            //
++            // TODO: fix this
++            ExprKind::AddrOf(_, _, _) => {
++                return;
++            }
++            _ => {}
++        }
++
++        walk_expr(self, expr);
++    }
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++/// Returns `true` if `expr` is the LHS of an assignment, like `expr = ...`.
++fn is_in_assignment_position(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++    if let Some(parent) = get_parent_expr(cx, expr) {
++        if let ExprKind::Assign(ref lhs, ..) = parent.kind {
++            return lhs.hir_id == expr.hir_id;
++        }
++    }
++    false
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..82ca4baacb7a9763cd390532f369c36a88924941
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,176 @@@
++use crate::utils::{attr_by_name, in_macro, match_path_ast, span_lint_and_help};
++use rustc_ast::ast::{AssocItemKind, Extern, FnSig, Item, ItemKind, Ty, TyKind};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::Span;
++
++use std::convert::TryInto;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for excessive
++    /// use of bools in structs.
++    ///
++    /// **Why is this bad?** Excessive bools in a struct
++    /// is often a sign that it's used as a state machine,
++    /// which is much better implemented as an enum.
++    /// If it's not the case, excessive bools usually benefit
++    /// from refactoring into two-variant enums for better
++    /// readability and API.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// Bad:
++    /// ```rust
++    /// struct S {
++    ///     is_pending: bool,
++    ///     is_processing: bool,
++    ///     is_finished: bool,
++    /// }
++    /// ```
++    ///
++    /// Good:
++    /// ```rust
++    /// enum S {
++    ///     Pending,
++    ///     Processing,
++    ///     Finished,
++    /// }
++    /// ```
++    pub STRUCT_EXCESSIVE_BOOLS,
++    pedantic,
++    "using too many bools in a struct"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for excessive use of
++    /// bools in function definitions.
++    ///
++    /// **Why is this bad?** Calls to such functions
++    /// are confusing and error prone, because it's
++    /// hard to remember argument order and you have
++    /// no type system support to back you up. Using
++    /// two-variant enums instead of bools often makes
++    /// API easier to use.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// Bad:
++    /// ```rust,ignore
++    /// fn f(is_round: bool, is_hot: bool) { ... }
++    /// ```
++    ///
++    /// Good:
++    /// ```rust,ignore
++    /// enum Shape {
++    ///     Round,
++    ///     Spiky,
++    /// }
++    ///
++    /// enum Temperature {
++    ///     Hot,
++    ///     IceCold,
++    /// }
++    ///
++    /// fn f(shape: Shape, temperature: Temperature) { ... }
++    /// ```
++    pub FN_PARAMS_EXCESSIVE_BOOLS,
++    pedantic,
++    "using too many bools in function parameters"
++}
++
++pub struct ExcessiveBools {
++    max_struct_bools: u64,
++    max_fn_params_bools: u64,
++}
++
++impl ExcessiveBools {
++    #[must_use]
++    pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self {
++        Self {
++            max_struct_bools,
++            max_fn_params_bools,
++        }
++    }
++
++    fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) {
++        match fn_sig.header.ext {
++            Extern::Implicit | Extern::Explicit(_) => return,
++            Extern::None => (),
++        }
++
++        let fn_sig_bools = fn_sig
++            .decl
++            .inputs
++            .iter()
++            .filter(|param| is_bool_ty(&param.ty))
++            .count()
++            .try_into()
++            .unwrap();
++        if self.max_fn_params_bools < fn_sig_bools {
++            span_lint_and_help(
++                cx,
++                FN_PARAMS_EXCESSIVE_BOOLS,
++                span,
++                &format!("more than {} bools in function parameters", self.max_fn_params_bools),
++                None,
++                "consider refactoring bools into two-variant enums",
++            );
++        }
++    }
++}
++
++impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]);
++
++fn is_bool_ty(ty: &Ty) -> bool {
++    if let TyKind::Path(None, path) = &ty.kind {
++        return match_path_ast(path, &["bool"]);
++    }
++    false
++}
++
++impl EarlyLintPass for ExcessiveBools {
++    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
++        if in_macro(item.span) {
++            return;
++        }
++        match &item.kind {
++            ItemKind::Struct(variant_data, _) => {
++                if attr_by_name(&item.attrs, "repr").is_some() {
++                    return;
++                }
++
++                let struct_bools = variant_data
++                    .fields()
++                    .iter()
++                    .filter(|field| is_bool_ty(&field.ty))
++                    .count()
++                    .try_into()
++                    .unwrap();
++                if self.max_struct_bools < struct_bools {
++                    span_lint_and_help(
++                        cx,
++                        STRUCT_EXCESSIVE_BOOLS,
++                        item.span,
++                        &format!("more than {} bools in a struct", self.max_struct_bools),
++                        None,
++                        "consider using a state machine or refactoring bools into two-variant enums",
++                    );
++                }
++            },
++            ItemKind::Impl {
++                of_trait: None, items, ..
++            }
++            | ItemKind::Trait(_, _, _, _, items) => {
++                for item in items {
++                    if let AssocItemKind::Fn(_, fn_sig, _, _) = &item.kind {
++                        self.check_fn_sig(cx, fn_sig, item.span);
++                    }
++                }
++            },
++            ItemKind::Fn(_, fn_sig, _, _) => self.check_fn_sig(cx, fn_sig, item.span),
++            _ => (),
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..621d56185a9dd4fac7ebb73a33f5a359d8959d49
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++use crate::utils::{is_entrypoint_fn, match_def_path, paths, qpath_res, span_lint};
++use if_chain::if_chain;
++use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** `exit()`  terminates the program and doesn't provide a
++    /// stack trace.
++    ///
++    /// **Why is this bad?** Ideally a program is terminated by finishing
++    /// the main function.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// std::process::exit(0)
++    /// ```
++    pub EXIT,
++    restriction,
++    "`std::process::exit` is called, terminating the program"
++}
++
++declare_lint_pass!(Exit => [EXIT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Exit {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        if_chain! {
++            if let ExprKind::Call(ref path_expr, ref _args) = e.kind;
++            if let ExprKind::Path(ref path) = path_expr.kind;
++            if let Some(def_id) = qpath_res(cx, path, path_expr.hir_id).opt_def_id();
++            if match_def_path(cx, def_id, &paths::EXIT);
++            then {
++                let parent = cx.tcx.hir().get_parent_item(e.hir_id);
++                if let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find(parent) {
++                    // If the next item up is a function we check if it is an entry point
++                    // and only then emit a linter warning
++                    let def_id = cx.tcx.hir().local_def_id(parent);
++                    if !is_entrypoint_fn(cx, def_id.to_def_id()) {
++                        span_lint(cx, EXIT, e.span, "usage of `process::exit`");
++                    }
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..320121b277140727d3a190bcacad260e5b46ac54
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,149 @@@
++use crate::utils::{is_expn_of, match_function_call, paths, span_lint, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{BorrowKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be
++    /// replaced with `(e)print!()` / `(e)println!()`
++    ///
++    /// **Why is this bad?** Using `(e)println! is clearer and more concise
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # use std::io::Write;
++    /// # let bar = "furchtbar";
++    /// // this would be clearer as `eprintln!("foo: {:?}", bar);`
++    /// writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap();
++    /// ```
++    pub EXPLICIT_WRITE,
++    complexity,
++    "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work"
++}
++
++declare_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExplicitWrite {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            // match call to unwrap
++            if let ExprKind::MethodCall(ref unwrap_fun, _, ref unwrap_args) = expr.kind;
++            if unwrap_fun.ident.name == sym!(unwrap);
++            // match call to write_fmt
++            if !unwrap_args.is_empty();
++            if let ExprKind::MethodCall(ref write_fun, _, write_args) =
++                unwrap_args[0].kind;
++            if write_fun.ident.name == sym!(write_fmt);
++            // match calls to std::io::stdout() / std::io::stderr ()
++            if !write_args.is_empty();
++            if let Some(dest_name) = if match_function_call(cx, &write_args[0], &paths::STDOUT).is_some() {
++                Some("stdout")
++            } else if match_function_call(cx, &write_args[0], &paths::STDERR).is_some() {
++                Some("stderr")
++            } else {
++                None
++            };
++            then {
++                let write_span = unwrap_args[0].span;
++                let calling_macro =
++                    // ordering is important here, since `writeln!` uses `write!` internally
++                    if is_expn_of(write_span, "writeln").is_some() {
++                        Some("writeln")
++                    } else if is_expn_of(write_span, "write").is_some() {
++                        Some("write")
++                    } else {
++                        None
++                    };
++                let prefix = if dest_name == "stderr" {
++                    "e"
++                } else {
++                    ""
++                };
++
++                // We need to remove the last trailing newline from the string because the
++                // underlying `fmt::write` function doesn't know whether `println!` or `print!` was
++                // used.
++                if let Some(mut write_output) = write_output_string(write_args) {
++                    if write_output.ends_with('\n') {
++                        write_output.pop();
++                    }
++
++                    if let Some(macro_name) = calling_macro {
++                        span_lint_and_sugg(
++                            cx,
++                            EXPLICIT_WRITE,
++                            expr.span,
++                            &format!(
++                                "use of `{}!({}(), ...).unwrap()`",
++                                macro_name,
++                                dest_name
++                            ),
++                            "try this",
++                            format!("{}{}!(\"{}\")", prefix, macro_name.replace("write", "print"), write_output.escape_default()),
++                            Applicability::MachineApplicable
++                        );
++                    } else {
++                        span_lint_and_sugg(
++                            cx,
++                            EXPLICIT_WRITE,
++                            expr.span,
++                            &format!("use of `{}().write_fmt(...).unwrap()`", dest_name),
++                            "try this",
++                            format!("{}print!(\"{}\")", prefix, write_output.escape_default()),
++                            Applicability::MachineApplicable
++                        );
++                    }
++                } else {
++                    // We don't have a proper suggestion
++                    if let Some(macro_name) = calling_macro {
++                        span_lint(
++                            cx,
++                            EXPLICIT_WRITE,
++                            expr.span,
++                            &format!(
++                                "use of `{}!({}(), ...).unwrap()`. Consider using `{}{}!` instead",
++                                macro_name,
++                                dest_name,
++                                prefix,
++                                macro_name.replace("write", "print")
++                            )
++                        );
++                    } else {
++                        span_lint(
++                            cx,
++                            EXPLICIT_WRITE,
++                            expr.span,
++                            &format!("use of `{}().write_fmt(...).unwrap()`. Consider using `{}print!` instead", dest_name, prefix),
++                        );
++                    }
++                }
++
++            }
++        }
++    }
++}
++
++// Extract the output string from the given `write_args`.
++fn write_output_string(write_args: &[Expr<'_>]) -> Option<String> {
++    if_chain! {
++        // Obtain the string that should be printed
++        if write_args.len() > 1;
++        if let ExprKind::Call(_, ref output_args) = write_args[1].kind;
++        if !output_args.is_empty();
++        if let ExprKind::AddrOf(BorrowKind::Ref, _, ref output_string_expr) = output_args[0].kind;
++        if let ExprKind::Array(ref string_exprs) = output_string_expr.kind;
++        // we only want to provide an automatic suggestion for simple (non-format) strings
++        if string_exprs.len() == 1;
++        if let ExprKind::Lit(ref lit) = string_exprs[0].kind;
++        if let LitKind::Str(ref write_output, _) = lit.node;
++        then {
++            return Some(write_output.to_string())
++        }
++    }
++    None
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..17639cc2a0643798c4b2e701d87758869fcebb6b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,130 @@@
++use crate::utils::paths::{BEGIN_PANIC, BEGIN_PANIC_FMT, FROM_TRAIT};
++use crate::utils::{
++    is_expn_of, is_type_diagnostic_item, match_def_path, method_chain_args, span_lint_and_then, walk_ptrs_ty,
++};
++use if_chain::if_chain;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`
++    ///
++    /// **Why is this bad?** `TryFrom` should be used if there's a possibility of failure.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// struct Foo(i32);
++    /// impl From<String> for Foo {
++    ///     fn from(s: String) -> Self {
++    ///         Foo(s.parse().unwrap())
++    ///     }
++    /// }
++    /// ```
++    pub FALLIBLE_IMPL_FROM,
++    nursery,
++    "Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`"
++}
++
++declare_lint_pass!(FallibleImplFrom => [FALLIBLE_IMPL_FROM]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FallibleImplFrom {
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) {
++        // check for `impl From<???> for ..`
++        let impl_def_id = cx.tcx.hir().local_def_id(item.hir_id);
++        if_chain! {
++            if let hir::ItemKind::Impl{ items: impl_items, .. } = item.kind;
++            if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_def_id);
++            if match_def_path(cx, impl_trait_ref.def_id, &FROM_TRAIT);
++            then {
++                lint_impl_body(cx, item.span, impl_items);
++            }
++        }
++    }
++}
++
++fn lint_impl_body<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, impl_span: Span, impl_items: &[hir::ImplItemRef<'_>]) {
++    use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
++    use rustc_hir::{Expr, ExprKind, ImplItemKind, QPath};
++
++    struct FindPanicUnwrap<'a, 'tcx> {
++        lcx: &'a LateContext<'a, 'tcx>,
++        tables: &'tcx ty::TypeckTables<'tcx>,
++        result: Vec<Span>,
++    }
++
++    impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
++        type Map = Map<'tcx>;
++
++        fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++            // check for `begin_panic`
++            if_chain! {
++                if let ExprKind::Call(ref func_expr, _) = expr.kind;
++                if let ExprKind::Path(QPath::Resolved(_, ref path)) = func_expr.kind;
++                if let Some(path_def_id) = path.res.opt_def_id();
++                if match_def_path(self.lcx, path_def_id, &BEGIN_PANIC) ||
++                    match_def_path(self.lcx, path_def_id, &BEGIN_PANIC_FMT);
++                if is_expn_of(expr.span, "unreachable").is_none();
++                then {
++                    self.result.push(expr.span);
++                }
++            }
++
++            // check for `unwrap`
++            if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
++                let reciever_ty = walk_ptrs_ty(self.tables.expr_ty(&arglists[0][0]));
++                if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
++                    || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
++                {
++                    self.result.push(expr.span);
++                }
++            }
++
++            // and check sub-expressions
++            intravisit::walk_expr(self, expr);
++        }
++
++        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++            NestedVisitorMap::None
++        }
++    }
++
++    for impl_item in impl_items {
++        if_chain! {
++            if impl_item.ident.name == sym!(from);
++            if let ImplItemKind::Fn(_, body_id) =
++                cx.tcx.hir().impl_item(impl_item.id).kind;
++            then {
++                // check the body for `begin_panic` or `unwrap`
++                let body = cx.tcx.hir().body(body_id);
++                let impl_item_def_id = cx.tcx.hir().local_def_id(impl_item.id.hir_id);
++                let mut fpu = FindPanicUnwrap {
++                    lcx: cx,
++                    tables: cx.tcx.typeck_tables_of(impl_item_def_id),
++                    result: Vec::new(),
++                };
++                fpu.visit_expr(&body.value);
++
++                // if we've found one, lint
++                if !fpu.result.is_empty() {
++                    span_lint_and_then(
++                        cx,
++                        FALLIBLE_IMPL_FROM,
++                        impl_span,
++                        "consider implementing `TryFrom` instead",
++                        move |diag| {
++                            diag.help(
++                                "`From` is intended for infallible conversions only. \
++                                 Use `TryFrom` if there's a possibility for the conversion to fail.");
++                            diag.span_note(fpu.result, "potential failure(s)");
++                        });
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3a52b1d3fc20bdd2580e38e052fb0614f8313e03
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,182 @@@
++use crate::utils::{numeric_literal, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use std::fmt;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for float literals with a precision greater
++    /// than that supported by the underlying type.
++    ///
++    /// **Why is this bad?** Rust will truncate the literal silently.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// // Bad
++    /// let v: f32 = 0.123_456_789_9;
++    /// println!("{}", v); //  0.123_456_789
++    ///
++    /// // Good
++    /// let v: f64 = 0.123_456_789_9;
++    /// println!("{}", v); //  0.123_456_789_9
++    /// ```
++    pub EXCESSIVE_PRECISION,
++    style,
++    "excessive precision for float literal"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for whole number float literals that
++    /// cannot be represented as the underlying type without loss.
++    ///
++    /// **Why is this bad?** Rust will silently lose precision during
++    /// conversion to a float.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// // Bad
++    /// let _: f32 = 16_777_217.0; // 16_777_216.0
++    ///
++    /// // Good
++    /// let _: f32 = 16_777_216.0;
++    /// let _: f64 = 16_777_217.0;
++    /// ```
++    pub LOSSY_FLOAT_LITERAL,
++    restriction,
++    "lossy whole number float literals"
++}
++
++declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatLiteral {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if_chain! {
++            let ty = cx.tables.expr_ty(expr);
++            if let ty::Float(fty) = ty.kind;
++            if let hir::ExprKind::Lit(ref lit) = expr.kind;
++            if let LitKind::Float(sym, lit_float_ty) = lit.node;
++            then {
++                let sym_str = sym.as_str();
++                let formatter = FloatFormat::new(&sym_str);
++                // Try to bail out if the float is for sure fine.
++                // If its within the 2 decimal digits of being out of precision we
++                // check if the parsed representation is the same as the string
++                // since we'll need the truncated string anyway.
++                let digits = count_digits(&sym_str);
++                let max = max_digits(fty);
++                let type_suffix = match lit_float_ty {
++                    LitFloatType::Suffixed(FloatTy::F32) => Some("f32"),
++                    LitFloatType::Suffixed(FloatTy::F64) => Some("f64"),
++                    _ => None
++                };
++                let (is_whole, mut float_str) = match fty {
++                    FloatTy::F32 => {
++                        let value = sym_str.parse::<f32>().unwrap();
++
++                        (value.fract() == 0.0, formatter.format(value))
++                    },
++                    FloatTy::F64 => {
++                        let value = sym_str.parse::<f64>().unwrap();
++
++                        (value.fract() == 0.0, formatter.format(value))
++                    },
++                };
++
++                if is_whole && !sym_str.contains(|c| c == 'e' || c == 'E') {
++                    // Normalize the literal by stripping the fractional portion
++                    if sym_str.split('.').next().unwrap() != float_str {
++                        // If the type suffix is missing the suggestion would be
++                        // incorrectly interpreted as an integer so adding a `.0`
++                        // suffix to prevent that.
++                        if type_suffix.is_none() {
++                            float_str.push_str(".0");
++                        }
++
++                        span_lint_and_sugg(
++                            cx,
++                            LOSSY_FLOAT_LITERAL,
++                            expr.span,
++                            "literal cannot be represented as the underlying type without loss of precision",
++                            "consider changing the type or replacing it with",
++                            numeric_literal::format(&float_str, type_suffix, true),
++                            Applicability::MachineApplicable,
++                        );
++                    }
++                } else if digits > max as usize && sym_str != float_str {
++                    span_lint_and_sugg(
++                        cx,
++                        EXCESSIVE_PRECISION,
++                        expr.span,
++                        "float has excessive precision",
++                        "consider changing the type or truncating it to",
++                        numeric_literal::format(&float_str, type_suffix, true),
++                        Applicability::MachineApplicable,
++                    );
++                }
++            }
++        }
++    }
++}
++
++#[must_use]
++fn max_digits(fty: FloatTy) -> u32 {
++    match fty {
++        FloatTy::F32 => f32::DIGITS,
++        FloatTy::F64 => f64::DIGITS,
++    }
++}
++
++/// Counts the digits excluding leading zeros
++#[must_use]
++fn count_digits(s: &str) -> usize {
++    // Note that s does not contain the f32/64 suffix, and underscores have been stripped
++    s.chars()
++        .filter(|c| *c != '-' && *c != '.')
++        .take_while(|c| *c != 'e' && *c != 'E')
++        .fold(0, |count, c| {
++            // leading zeros
++            if c == '0' && count == 0 {
++                count
++            } else {
++                count + 1
++            }
++        })
++}
++
++enum FloatFormat {
++    LowerExp,
++    UpperExp,
++    Normal,
++}
++impl FloatFormat {
++    #[must_use]
++    fn new(s: &str) -> Self {
++        s.chars()
++            .find_map(|x| match x {
++                'e' => Some(Self::LowerExp),
++                'E' => Some(Self::UpperExp),
++                _ => None,
++            })
++            .unwrap_or(Self::Normal)
++    }
++    fn format<T>(&self, f: T) -> String
++    where
++        T: fmt::UpperExp + fmt::LowerExp + fmt::Display,
++    {
++        match self {
++            Self::LowerExp => format!("{:e}", f),
++            Self::UpperExp => format!("{:E}", f),
++            Self::Normal => format!("{}", f),
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..86317fb8bd5c4767b9ed5d7a4e9543b7fc8ed17c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,503 @@@
++use crate::consts::{
++    constant, constant_simple, Constant,
++    Constant::{F32, F64},
++};
++use crate::utils::{higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Spanned;
++
++use rustc_ast::ast;
++use std::f32::consts as f32_consts;
++use std::f64::consts as f64_consts;
++use sugg::Sugg;
++
++declare_clippy_lint! {
++    /// **What it does:** Looks for floating-point expressions that
++    /// can be expressed using built-in methods to improve accuracy
++    /// at the cost of performance.
++    ///
++    /// **Why is this bad?** Negatively impacts accuracy.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    ///
++    /// let a = 3f32;
++    /// let _ = a.powf(1.0 / 3.0);
++    /// let _ = (1.0 + a).ln();
++    /// let _ = a.exp() - 1.0;
++    /// ```
++    ///
++    /// is better expressed as
++    ///
++    /// ```rust
++    ///
++    /// let a = 3f32;
++    /// let _ = a.cbrt();
++    /// let _ = a.ln_1p();
++    /// let _ = a.exp_m1();
++    /// ```
++    pub IMPRECISE_FLOPS,
++    nursery,
++    "usage of imprecise floating point operations"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Looks for floating-point expressions that
++    /// can be expressed using built-in methods to improve both
++    /// accuracy and performance.
++    ///
++    /// **Why is this bad?** Negatively impacts accuracy and performance.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// use std::f32::consts::E;
++    ///
++    /// let a = 3f32;
++    /// let _ = (2f32).powf(a);
++    /// let _ = E.powf(a);
++    /// let _ = a.powf(1.0 / 2.0);
++    /// let _ = a.log(2.0);
++    /// let _ = a.log(10.0);
++    /// let _ = a.log(E);
++    /// let _ = a.powf(2.0);
++    /// let _ = a * 2.0 + 4.0;
++    /// let _ = if a < 0.0 {
++    ///     -a
++    /// } else {
++    ///     a
++    /// };
++    /// let _ = if a < 0.0 {
++    ///     a
++    /// } else {
++    ///     -a
++    /// };
++    /// ```
++    ///
++    /// is better expressed as
++    ///
++    /// ```rust
++    /// use std::f32::consts::E;
++    ///
++    /// let a = 3f32;
++    /// let _ = a.exp2();
++    /// let _ = a.exp();
++    /// let _ = a.sqrt();
++    /// let _ = a.log2();
++    /// let _ = a.log10();
++    /// let _ = a.ln();
++    /// let _ = a.powi(2);
++    /// let _ = a.mul_add(2.0, 4.0);
++    /// let _ = a.abs();
++    /// let _ = -a.abs();
++    /// ```
++    pub SUBOPTIMAL_FLOPS,
++    nursery,
++    "usage of sub-optimal floating point operations"
++}
++
++declare_lint_pass!(FloatingPointArithmetic => [
++    IMPRECISE_FLOPS,
++    SUBOPTIMAL_FLOPS
++]);
++
++// Returns the specialized log method for a given base if base is constant
++// and is one of 2, 10 and e
++fn get_specialized_log_method(cx: &LateContext<'_, '_>, base: &Expr<'_>) -> Option<&'static str> {
++    if let Some((value, _)) = constant(cx, cx.tables, base) {
++        if F32(2.0) == value || F64(2.0) == value {
++            return Some("log2");
++        } else if F32(10.0) == value || F64(10.0) == value {
++            return Some("log10");
++        } else if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
++            return Some("ln");
++        }
++    }
++
++    None
++}
++
++// Adds type suffixes and parenthesis to method receivers if necessary
++fn prepare_receiver_sugg<'a>(cx: &LateContext<'_, '_>, mut expr: &'a Expr<'a>) -> Sugg<'a> {
++    let mut suggestion = Sugg::hir(cx, expr, "..");
++
++    if let ExprKind::Unary(UnOp::UnNeg, inner_expr) = &expr.kind {
++        expr = &inner_expr;
++    }
++
++    if_chain! {
++        // if the expression is a float literal and it is unsuffixed then
++        // add a suffix so the suggestion is valid and unambiguous
++        if let ty::Float(float_ty) = cx.tables.expr_ty(expr).kind;
++        if let ExprKind::Lit(lit) = &expr.kind;
++        if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node;
++        then {
++            let op = format!(
++                "{}{}{}",
++                suggestion,
++                // Check for float literals without numbers following the decimal
++                // separator such as `2.` and adds a trailing zero
++                if sym.as_str().ends_with('.') {
++                    "0"
++                } else {
++                    ""
++                },
++                float_ty.name_str()
++            ).into();
++
++            suggestion = match suggestion {
++                Sugg::MaybeParen(_) => Sugg::MaybeParen(op),
++                _ => Sugg::NonParen(op)
++            };
++        }
++    }
++
++    suggestion.maybe_par()
++}
++
++fn check_log_base(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
++    if let Some(method) = get_specialized_log_method(cx, &args[1]) {
++        span_lint_and_sugg(
++            cx,
++            SUBOPTIMAL_FLOPS,
++            expr.span,
++            "logarithm for bases 2, 10 and e can be computed more accurately",
++            "consider using",
++            format!("{}.{}()", Sugg::hir(cx, &args[0], ".."), method),
++            Applicability::MachineApplicable,
++        );
++    }
++}
++
++// TODO: Lint expressions of the form `(x + y).ln()` where y > 1 and
++// suggest usage of `(x + (y - 1)).ln_1p()` instead
++fn check_ln1p(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
++    if let ExprKind::Binary(
++        Spanned {
++            node: BinOpKind::Add, ..
++        },
++        lhs,
++        rhs,
++    ) = &args[0].kind
++    {
++        let recv = match (constant(cx, cx.tables, lhs), constant(cx, cx.tables, rhs)) {
++            (Some((value, _)), _) if F32(1.0) == value || F64(1.0) == value => rhs,
++            (_, Some((value, _))) if F32(1.0) == value || F64(1.0) == value => lhs,
++            _ => return,
++        };
++
++        span_lint_and_sugg(
++            cx,
++            IMPRECISE_FLOPS,
++            expr.span,
++            "ln(1 + x) can be computed more accurately",
++            "consider using",
++            format!("{}.ln_1p()", prepare_receiver_sugg(cx, recv)),
++            Applicability::MachineApplicable,
++        );
++    }
++}
++
++// Returns an integer if the float constant is a whole number and it can be
++// converted to an integer without loss of precision. For now we only check
++// ranges [-16777215, 16777216) for type f32 as whole number floats outside
++// this range are lossy and ambiguous.
++#[allow(clippy::cast_possible_truncation)]
++fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
++    match value {
++        F32(num) if num.fract() == 0.0 => {
++            if (-16_777_215.0..16_777_216.0).contains(num) {
++                Some(num.round() as i32)
++            } else {
++                None
++            }
++        },
++        F64(num) if num.fract() == 0.0 => {
++            if (-2_147_483_648.0..2_147_483_648.0).contains(num) {
++                Some(num.round() as i32)
++            } else {
++                None
++            }
++        },
++        _ => None,
++    }
++}
++
++fn check_powf(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
++    // Check receiver
++    if let Some((value, _)) = constant(cx, cx.tables, &args[0]) {
++        let method = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
++            "exp"
++        } else if F32(2.0) == value || F64(2.0) == value {
++            "exp2"
++        } else {
++            return;
++        };
++
++        span_lint_and_sugg(
++            cx,
++            SUBOPTIMAL_FLOPS,
++            expr.span,
++            "exponent for bases 2 and e can be computed more accurately",
++            "consider using",
++            format!("{}.{}()", prepare_receiver_sugg(cx, &args[1]), method),
++            Applicability::MachineApplicable,
++        );
++    }
++
++    // Check argument
++    if let Some((value, _)) = constant(cx, cx.tables, &args[1]) {
++        let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value {
++            (
++                SUBOPTIMAL_FLOPS,
++                "square-root of a number can be computed more efficiently and accurately",
++                format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..")),
++            )
++        } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
++            (
++                IMPRECISE_FLOPS,
++                "cube-root of a number can be computed more accurately",
++                format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..")),
++            )
++        } else if let Some(exponent) = get_integer_from_float_constant(&value) {
++            (
++                SUBOPTIMAL_FLOPS,
++                "exponentiation with integer powers can be computed more efficiently",
++                format!(
++                    "{}.powi({})",
++                    Sugg::hir(cx, &args[0], ".."),
++                    numeric_literal::format(&exponent.to_string(), None, false)
++                ),
++            )
++        } else {
++            return;
++        };
++
++        span_lint_and_sugg(
++            cx,
++            lint,
++            expr.span,
++            help,
++            "consider using",
++            suggestion,
++            Applicability::MachineApplicable,
++        );
++    }
++}
++
++// TODO: Lint expressions of the form `x.exp() - y` where y > 1
++// and suggest usage of `x.exp_m1() - (y - 1)` instead
++fn check_expm1(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++    if_chain! {
++        if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, ref lhs, ref rhs) = expr.kind;
++        if cx.tables.expr_ty(lhs).is_floating_point();
++        if let Some((value, _)) = constant(cx, cx.tables, rhs);
++        if F32(1.0) == value || F64(1.0) == value;
++        if let ExprKind::MethodCall(ref path, _, ref method_args) = lhs.kind;
++        if cx.tables.expr_ty(&method_args[0]).is_floating_point();
++        if path.ident.name.as_str() == "exp";
++        then {
++            span_lint_and_sugg(
++                cx,
++                IMPRECISE_FLOPS,
++                expr.span,
++                "(e.pow(x) - 1) can be computed more accurately",
++                "consider using",
++                format!(
++                    "{}.exp_m1()",
++                    Sugg::hir(cx, &method_args[0], "..")
++                ),
++                Applicability::MachineApplicable,
++            );
++        }
++    }
++}
++
++fn is_float_mul_expr<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
++    if_chain! {
++        if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lhs, ref rhs) = &expr.kind;
++        if cx.tables.expr_ty(lhs).is_floating_point();
++        if cx.tables.expr_ty(rhs).is_floating_point();
++        then {
++            return Some((lhs, rhs));
++        }
++    }
++
++    None
++}
++
++// TODO: Fix rust-lang/rust-clippy#4735
++fn check_mul_add(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++    if let ExprKind::Binary(
++        Spanned {
++            node: BinOpKind::Add, ..
++        },
++        lhs,
++        rhs,
++    ) = &expr.kind
++    {
++        let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) {
++            (inner_lhs, inner_rhs, rhs)
++        } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) {
++            (inner_lhs, inner_rhs, lhs)
++        } else {
++            return;
++        };
++
++        span_lint_and_sugg(
++            cx,
++            SUBOPTIMAL_FLOPS,
++            expr.span,
++            "multiply and add expressions can be calculated more efficiently and accurately",
++            "consider using",
++            format!(
++                "{}.mul_add({}, {})",
++                prepare_receiver_sugg(cx, recv),
++                Sugg::hir(cx, arg1, ".."),
++                Sugg::hir(cx, arg2, ".."),
++            ),
++            Applicability::MachineApplicable,
++        );
++    }
++}
++
++/// Returns true iff expr is an expression which tests whether or not
++/// test is positive or an expression which tests whether or not test
++/// is nonnegative.
++/// Used for check-custom-abs function below
++fn is_testing_positive(cx: &LateContext<'_, '_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
++    if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
++        match op {
++            BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && are_exprs_equal(cx, left, test),
++            BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && are_exprs_equal(cx, right, test),
++            _ => false,
++        }
++    } else {
++        false
++    }
++}
++
++/// See [`is_testing_positive`]
++fn is_testing_negative(cx: &LateContext<'_, '_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
++    if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
++        match op {
++            BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && are_exprs_equal(cx, right, test),
++            BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && are_exprs_equal(cx, left, test),
++            _ => false,
++        }
++    } else {
++        false
++    }
++}
++
++fn are_exprs_equal(cx: &LateContext<'_, '_>, expr1: &Expr<'_>, expr2: &Expr<'_>) -> bool {
++    SpanlessEq::new(cx).ignore_fn().eq_expr(expr1, expr2)
++}
++
++/// Returns true iff expr is some zero literal
++fn is_zero(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++    match constant_simple(cx, cx.tables, expr) {
++        Some(Constant::Int(i)) => i == 0,
++        Some(Constant::F32(f)) => f == 0.0,
++        Some(Constant::F64(f)) => f == 0.0,
++        _ => false,
++    }
++}
++
++/// If the two expressions are negations of each other, then it returns
++/// a tuple, in which the first element is true iff expr1 is the
++/// positive expressions, and the second element is the positive
++/// one of the two expressions
++/// If the two expressions are not negations of each other, then it
++/// returns None.
++fn are_negated<'a>(cx: &LateContext<'_, '_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> {
++    if let ExprKind::Unary(UnOp::UnNeg, expr1_negated) = &expr1.kind {
++        if are_exprs_equal(cx, expr1_negated, expr2) {
++            return Some((false, expr2));
++        }
++    }
++    if let ExprKind::Unary(UnOp::UnNeg, expr2_negated) = &expr2.kind {
++        if are_exprs_equal(cx, expr1, expr2_negated) {
++            return Some((true, expr1));
++        }
++    }
++    None
++}
++
++fn check_custom_abs(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++    if_chain! {
++        if let Some((cond, body, Some(else_body))) = higher::if_block(&expr);
++        if let ExprKind::Block(block, _) = body.kind;
++        if block.stmts.is_empty();
++        if let Some(if_body_expr) = block.expr;
++        if let ExprKind::Block(else_block, _) = else_body.kind;
++        if else_block.stmts.is_empty();
++        if let Some(else_body_expr) = else_block.expr;
++        if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr);
++        then {
++            let positive_abs_sugg = (
++                "manual implementation of `abs` method",
++                format!("{}.abs()", Sugg::hir(cx, body, "..")),
++            );
++            let negative_abs_sugg = (
++                "manual implementation of negation of `abs` method",
++                format!("-{}.abs()", Sugg::hir(cx, body, "..")),
++            );
++            let sugg = if is_testing_positive(cx, cond, body) {
++                if if_expr_positive {
++                    positive_abs_sugg
++                } else {
++                    negative_abs_sugg
++                }
++            } else if is_testing_negative(cx, cond, body) {
++                if if_expr_positive {
++                    negative_abs_sugg
++                } else {
++                    positive_abs_sugg
++                }
++            } else {
++                return;
++            };
++            span_lint_and_sugg(
++                cx,
++                SUBOPTIMAL_FLOPS,
++                expr.span,
++                sugg.0,
++                "try",
++                sugg.1,
++                Applicability::MachineApplicable,
++            );
++        }
++    }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatingPointArithmetic {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if let ExprKind::MethodCall(ref path, _, args) = &expr.kind {
++            let recv_ty = cx.tables.expr_ty(&args[0]);
++
++            if recv_ty.is_floating_point() {
++                match &*path.ident.name.as_str() {
++                    "ln" => check_ln1p(cx, expr, args),
++                    "log" => check_log_base(cx, expr, args),
++                    "powf" => check_powf(cx, expr, args),
++                    _ => {},
++                }
++            }
++        } else {
++            check_expm1(cx, expr);
++            check_mul_add(cx, expr);
++            check_custom_abs(cx, expr);
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5b092526ce4f2921cafb6bc22747cec1cf3c7dfb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,200 @@@
++use crate::utils::paths;
++use crate::utils::{
++    is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet,
++    span_lint_and_then, walk_ptrs_ty,
++};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, MatchSource, PatKind};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for the use of `format!("string literal with no
++    /// argument")` and `format!("{}", foo)` where `foo` is a string.
++    ///
++    /// **Why is this bad?** There is no point of doing that. `format!("foo")` can
++    /// be replaced by `"foo".to_owned()` if you really need a `String`. The even
++    /// worse `&format!("foo")` is often encountered in the wild. `format!("{}",
++    /// foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()`
++    /// if `foo: &str`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Examples:**
++    /// ```rust
++    /// # let foo = "foo";
++    /// format!("foo");
++    /// format!("{}", foo);
++    /// ```
++    pub USELESS_FORMAT,
++    complexity,
++    "useless use of `format!`"
++}
++
++declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessFormat {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        let span = match is_expn_of(expr.span, "format") {
++            Some(s) if !s.from_expansion() => s,
++            _ => return,
++        };
++
++        // Operate on the only argument of `alloc::fmt::format`.
++        if let Some(sugg) = on_new_v1(cx, expr) {
++            span_useless_format(cx, span, "consider using `.to_string()`", sugg);
++        } else if let Some(sugg) = on_new_v1_fmt(cx, expr) {
++            span_useless_format(cx, span, "consider using `.to_string()`", sugg);
++        }
++    }
++}
++
++fn span_useless_format<T: LintContext>(cx: &T, span: Span, help: &str, mut sugg: String) {
++    let to_replace = span.source_callsite();
++
++    // The callsite span contains the statement semicolon for some reason.
++    let snippet = snippet(cx, to_replace, "..");
++    if snippet.ends_with(';') {
++        sugg.push(';');
++    }
++
++    span_lint_and_then(cx, USELESS_FORMAT, span, "useless use of `format!`", |diag| {
++        diag.span_suggestion(
++            to_replace,
++            help,
++            sugg,
++            Applicability::MachineApplicable, // snippet
++        );
++    });
++}
++
++fn on_argumentv1_new<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &'tcx Expr<'_>,
++    arms: &'tcx [Arm<'_>],
++) -> Option<String> {
++    if_chain! {
++        if let ExprKind::AddrOf(BorrowKind::Ref, _, ref format_args) = expr.kind;
++        if let ExprKind::Array(ref elems) = arms[0].body.kind;
++        if elems.len() == 1;
++        if let Some(args) = match_function_call(cx, &elems[0], &paths::FMT_ARGUMENTV1_NEW);
++        // matches `core::fmt::Display::fmt`
++        if args.len() == 2;
++        if let ExprKind::Path(ref qpath) = args[1].kind;
++        if let Some(did) = cx.tables.qpath_res(qpath, args[1].hir_id).opt_def_id();
++        if match_def_path(cx, did, &paths::DISPLAY_FMT_METHOD);
++        // check `(arg0,)` in match block
++        if let PatKind::Tuple(ref pats, None) = arms[0].pat.kind;
++        if pats.len() == 1;
++        then {
++            let ty = walk_ptrs_ty(cx.tables.pat_ty(&pats[0]));
++            if ty.kind != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym!(string_type)) {
++                return None;
++            }
++            if let ExprKind::Lit(ref lit) = format_args.kind {
++                if let LitKind::Str(ref s, _) = lit.node {
++                    return Some(format!("{:?}.to_string()", s.as_str()));
++                }
++            } else {
++                let snip = snippet(cx, format_args.span, "<arg>");
++                if let ExprKind::MethodCall(ref path, _, _) = format_args.kind {
++                    if path.ident.name == sym!(to_string) {
++                        return Some(format!("{}", snip));
++                    }
++                } else if let ExprKind::Binary(..) = format_args.kind {
++                    return Some(format!("{}", snip));
++                }
++                return Some(format!("{}.to_string()", snip));
++            }
++        }
++    }
++    None
++}
++
++fn on_new_v1<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> Option<String> {
++    if_chain! {
++        if let Some(args) = match_function_call(cx, expr, &paths::FMT_ARGUMENTS_NEW_V1);
++        if args.len() == 2;
++        // Argument 1 in `new_v1()`
++        if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arr) = args[0].kind;
++        if let ExprKind::Array(ref pieces) = arr.kind;
++        if pieces.len() == 1;
++        if let ExprKind::Lit(ref lit) = pieces[0].kind;
++        if let LitKind::Str(ref s, _) = lit.node;
++        // Argument 2 in `new_v1()`
++        if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg1) = args[1].kind;
++        if let ExprKind::Match(ref matchee, ref arms, MatchSource::Normal) = arg1.kind;
++        if arms.len() == 1;
++        if let ExprKind::Tup(ref tup) = matchee.kind;
++        then {
++            // `format!("foo")` expansion contains `match () { () => [], }`
++            if tup.is_empty() {
++                return Some(format!("{:?}.to_string()", s.as_str()));
++            } else if s.as_str().is_empty() {
++                return on_argumentv1_new(cx, &tup[0], arms);
++            }
++        }
++    }
++    None
++}
++
++fn on_new_v1_fmt<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> Option<String> {
++    if_chain! {
++        if let Some(args) = match_function_call(cx, expr, &paths::FMT_ARGUMENTS_NEW_V1_FORMATTED);
++        if args.len() == 3;
++        if check_unformatted(&args[2]);
++        // Argument 1 in `new_v1_formatted()`
++        if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arr) = args[0].kind;
++        if let ExprKind::Array(ref pieces) = arr.kind;
++        if pieces.len() == 1;
++        if let ExprKind::Lit(ref lit) = pieces[0].kind;
++        if let LitKind::Str(..) = lit.node;
++        // Argument 2 in `new_v1_formatted()`
++        if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg1) = args[1].kind;
++        if let ExprKind::Match(ref matchee, ref arms, MatchSource::Normal) = arg1.kind;
++        if arms.len() == 1;
++        if let ExprKind::Tup(ref tup) = matchee.kind;
++        then {
++            return on_argumentv1_new(cx, &tup[0], arms);
++        }
++    }
++    None
++}
++
++/// Checks if the expression matches
++/// ```rust,ignore
++/// &[_ {
++///    format: _ {
++///         width: _::Implied,
++///         precision: _::Implied,
++///         ...
++///    },
++///    ...,
++/// }]
++/// ```
++fn check_unformatted(expr: &Expr<'_>) -> bool {
++    if_chain! {
++        if let ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) = expr.kind;
++        if let ExprKind::Array(ref exprs) = expr.kind;
++        if exprs.len() == 1;
++        // struct `core::fmt::rt::v1::Argument`
++        if let ExprKind::Struct(_, ref fields, _) = exprs[0].kind;
++        if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym!(format));
++        // struct `core::fmt::rt::v1::FormatSpec`
++        if let ExprKind::Struct(_, ref fields, _) = format_field.expr.kind;
++        if let Some(precision_field) = fields.iter().find(|f| f.ident.name == sym!(precision));
++        if let ExprKind::Path(ref precision_path) = precision_field.expr.kind;
++        if last_path_segment(precision_path).ident.name == sym!(Implied);
++        if let Some(width_field) = fields.iter().find(|f| f.ident.name == sym!(width));
++        if let ExprKind::Path(ref width_qpath) = width_field.expr.kind;
++        if last_path_segment(width_qpath).ident.name == sym!(Implied);
++        then {
++            return true;
++        }
++    }
++
++    false
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eb4b7a826f2ce10d8a05c9f2259f5098af96ce96
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,326 @@@
++use crate::utils::{differing_macro_contexts, snippet_opt, span_lint_and_help, span_lint_and_note};
++use if_chain::if_chain;
++use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-`
++    /// operators.
++    ///
++    /// **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or
++    /// confusing.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
++    /// ```
++    pub SUSPICIOUS_ASSIGNMENT_FORMATTING,
++    style,
++    "suspicious formatting of `*=`, `-=` or `!=`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks the formatting of a unary operator on the right hand side
++    /// of a binary operator. It lints if there is no space between the binary and unary operators,
++    /// but there is a space between the unary and its operand.
++    ///
++    /// **Why is this bad?** This is either a typo in the binary operator or confusing.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// if foo <- 30 { // this should be `foo < -30` but looks like a different operator
++    /// }
++    ///
++    /// if foo &&! bar { // this should be `foo && !bar` but looks like a different operator
++    /// }
++    /// ```
++    pub SUSPICIOUS_UNARY_OP_FORMATTING,
++    style,
++    "suspicious formatting of unary `-` or `!` on the RHS of a BinOp"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for formatting of `else`. It lints if the `else`
++    /// is followed immediately by a newline or the `else` seems to be missing.
++    ///
++    /// **Why is this bad?** This is probably some refactoring remnant, even if the
++    /// code is correct, it might look confusing.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// if foo {
++    /// } { // looks like an `else` is missing here
++    /// }
++    ///
++    /// if foo {
++    /// } if bar { // looks like an `else` is missing here
++    /// }
++    ///
++    /// if foo {
++    /// } else
++    ///
++    /// { // this is the `else` block of the previous `if`, but should it be?
++    /// }
++    ///
++    /// if foo {
++    /// } else
++    ///
++    /// if bar { // this is the `else` block of the previous `if`, but should it be?
++    /// }
++    /// ```
++    pub SUSPICIOUS_ELSE_FORMATTING,
++    style,
++    "suspicious formatting of `else`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for possible missing comma in an array. It lints if
++    /// an array element is a binary operator expression and it lies on two lines.
++    ///
++    /// **Why is this bad?** This could lead to unexpected results.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// let a = &[
++    ///     -1, -2, -3 // <= no comma here
++    ///     -4, -5, -6
++    /// ];
++    /// ```
++    pub POSSIBLE_MISSING_COMMA,
++    correctness,
++    "possible missing comma in array"
++}
++
++declare_lint_pass!(Formatting => [
++    SUSPICIOUS_ASSIGNMENT_FORMATTING,
++    SUSPICIOUS_UNARY_OP_FORMATTING,
++    SUSPICIOUS_ELSE_FORMATTING,
++    POSSIBLE_MISSING_COMMA
++]);
++
++impl EarlyLintPass for Formatting {
++    fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
++        for w in block.stmts.windows(2) {
++            match (&w[0].kind, &w[1].kind) {
++                (&StmtKind::Expr(ref first), &StmtKind::Expr(ref second))
++                | (&StmtKind::Expr(ref first), &StmtKind::Semi(ref second)) => {
++                    check_missing_else(cx, first, second);
++                },
++                _ => (),
++            }
++        }
++    }
++
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++        check_assign(cx, expr);
++        check_unop(cx, expr);
++        check_else(cx, expr);
++        check_array(cx, expr);
++    }
++}
++
++/// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint.
++fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
++    if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind {
++        if !differing_macro_contexts(lhs.span, rhs.span) && !lhs.span.from_expansion() {
++            let eq_span = lhs.span.between(rhs.span);
++            if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind {
++                if let Some(eq_snippet) = snippet_opt(cx, eq_span) {
++                    let op = UnOp::to_string(op);
++                    let eqop_span = lhs.span.between(sub_rhs.span);
++                    if eq_snippet.ends_with('=') {
++                        span_lint_and_note(
++                            cx,
++                            SUSPICIOUS_ASSIGNMENT_FORMATTING,
++                            eqop_span,
++                            &format!(
++                                "this looks like you are trying to use `.. {op}= ..`, but you \
++                                 really are doing `.. = ({op} ..)`",
++                                op = op
++                            ),
++                            None,
++                            &format!("to remove this lint, use either `{op}=` or `= {op}`", op = op),
++                        );
++                    }
++                }
++            }
++        }
++    }
++}
++
++/// Implementation of the `SUSPICIOUS_UNARY_OP_FORMATTING` lint.
++fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) {
++    if_chain! {
++        if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind;
++        if !differing_macro_contexts(lhs.span, rhs.span) && !lhs.span.from_expansion();
++        // span between BinOp LHS and RHS
++        let binop_span = lhs.span.between(rhs.span);
++        // if RHS is a UnOp
++        if let ExprKind::Unary(op, ref un_rhs) = rhs.kind;
++        // from UnOp operator to UnOp operand
++        let unop_operand_span = rhs.span.until(un_rhs.span);
++        if let Some(binop_snippet) = snippet_opt(cx, binop_span);
++        if let Some(unop_operand_snippet) = snippet_opt(cx, unop_operand_span);
++        let binop_str = BinOpKind::to_string(&binop.node);
++        // no space after BinOp operator and space after UnOp operator
++        if binop_snippet.ends_with(binop_str) && unop_operand_snippet.ends_with(' ');
++        then {
++            let unop_str = UnOp::to_string(op);
++            let eqop_span = lhs.span.between(un_rhs.span);
++            span_lint_and_help(
++                cx,
++                SUSPICIOUS_UNARY_OP_FORMATTING,
++                eqop_span,
++                &format!(
++                    "by not having a space between `{binop}` and `{unop}` it looks like \
++                     `{binop}{unop}` is a single operator",
++                    binop = binop_str,
++                    unop = unop_str
++                ),
++                None,
++                &format!(
++                    "put a space between `{binop}` and `{unop}` and remove the space after `{unop}`",
++                    binop = binop_str,
++                    unop = unop_str
++                ),
++            );
++        }
++    }
++}
++
++/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
++fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
++    if_chain! {
++        if let ExprKind::If(_, then, Some(else_)) = &expr.kind;
++        if is_block(else_) || is_if(else_);
++        if !differing_macro_contexts(then.span, else_.span);
++        if !then.span.from_expansion() && !in_external_macro(cx.sess, expr.span);
++
++        // workaround for rust-lang/rust#43081
++        if expr.span.lo().0 != 0 && expr.span.hi().0 != 0;
++
++        // this will be a span from the closing ‘}’ of the “then” block (excluding) to
++        // the “if” of the “else if” block (excluding)
++        let else_span = then.span.between(else_.span);
++
++        // the snippet should look like " else \n    " with maybe comments anywhere
++        // it’s bad when there is a ‘\n’ after the “else”
++        if let Some(else_snippet) = snippet_opt(cx, else_span);
++        if let Some(else_pos) = else_snippet.find("else");
++        if else_snippet[else_pos..].contains('\n');
++        let else_desc = if is_if(else_) { "if" } else { "{..}" };
++
++        then {
++            span_lint_and_note(
++                cx,
++                SUSPICIOUS_ELSE_FORMATTING,
++                else_span,
++                &format!("this is an `else {}` but the formatting might hide it", else_desc),
++                None,
++                &format!(
++                    "to remove this lint, remove the `else` or remove the new line between \
++                     `else` and `{}`",
++                    else_desc,
++                ),
++            );
++        }
++    }
++}
++
++#[must_use]
++fn has_unary_equivalent(bin_op: BinOpKind) -> bool {
++    // &, *, -
++    bin_op == BinOpKind::And || bin_op == BinOpKind::Mul || bin_op == BinOpKind::Sub
++}
++
++fn indentation(cx: &EarlyContext<'_>, span: Span) -> usize {
++    cx.sess.source_map().lookup_char_pos(span.lo()).col.0
++}
++
++/// Implementation of the `POSSIBLE_MISSING_COMMA` lint for array
++fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
++    if let ExprKind::Array(ref array) = expr.kind {
++        for element in array {
++            if_chain! {
++                if let ExprKind::Binary(ref op, ref lhs, _) = element.kind;
++                if has_unary_equivalent(op.node) && !differing_macro_contexts(lhs.span, op.span);
++                let space_span = lhs.span.between(op.span);
++                if let Some(space_snippet) = snippet_opt(cx, space_span);
++                let lint_span = lhs.span.with_lo(lhs.span.hi());
++                if space_snippet.contains('\n');
++                if indentation(cx, op.span) <= indentation(cx, lhs.span);
++                then {
++                    span_lint_and_note(
++                        cx,
++                        POSSIBLE_MISSING_COMMA,
++                        lint_span,
++                        "possibly missing a comma here",
++                        None,
++                        "to remove this lint, add a comma or write the expr in a single line",
++                    );
++                }
++            }
++        }
++    }
++}
++
++fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
++    if !differing_macro_contexts(first.span, second.span)
++        && !first.span.from_expansion()
++        && is_if(first)
++        && (is_block(second) || is_if(second))
++    {
++        // where the else would be
++        let else_span = first.span.between(second.span);
++
++        if let Some(else_snippet) = snippet_opt(cx, else_span) {
++            if !else_snippet.contains('\n') {
++                let (looks_like, next_thing) = if is_if(second) {
++                    ("an `else if`", "the second `if`")
++                } else {
++                    ("an `else {..}`", "the next block")
++                };
++
++                span_lint_and_note(
++                    cx,
++                    SUSPICIOUS_ELSE_FORMATTING,
++                    else_span,
++                    &format!("this looks like {} but the `else` is missing", looks_like),
++                    None,
++                    &format!(
++                        "to remove this lint, add the missing `else` or add a new line before {}",
++                        next_thing,
++                    ),
++                );
++            }
++        }
++    }
++}
++
++fn is_block(expr: &Expr) -> bool {
++    if let ExprKind::Block(..) = expr.kind {
++        true
++    } else {
++        false
++    }
++}
++
++/// Check if the expression is an `if` or `if let`
++fn is_if(expr: &Expr) -> bool {
++    if let ExprKind::If(..) = expr.kind {
++        true
++    } else {
++        false
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c24a24733d7f3bfacd908e5a175eaac03a9cdda5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,661 @@@
++use crate::utils::{
++    attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, iter_input_pats, match_def_path,
++    must_use_attr, qpath_res, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help, span_lint_and_then,
++    trait_ref_of_method, type_is_unsafe_function,
++};
++use rustc_ast::ast::Attribute;
++use rustc_data_structures::fx::FxHashSet;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::intravisit;
++use rustc_hir::{def::Res, def_id::DefId};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++use rustc_target::spec::abi::Abi;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for functions with too many parameters.
++    ///
++    /// **Why is this bad?** Functions with lots of parameters are considered bad
++    /// style and reduce readability (“what does the 5th parameter mean?”). Consider
++    /// grouping some parameters into a new type.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # struct Color;
++    /// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
++    ///     // ..
++    /// }
++    /// ```
++    pub TOO_MANY_ARGUMENTS,
++    complexity,
++    "functions with too many arguments"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for functions with a large amount of lines.
++    ///
++    /// **Why is this bad?** Functions with a lot of lines are harder to understand
++    /// due to having to look at a larger amount of code to understand what the
++    /// function is doing. Consider splitting the body of the function into
++    /// multiple functions.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ``` rust
++    /// fn im_too_long() {
++    /// println!("");
++    /// // ... 100 more LoC
++    /// println!("");
++    /// }
++    /// ```
++    pub TOO_MANY_LINES,
++    pedantic,
++    "functions with too many lines"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for public functions that dereference raw pointer
++    /// arguments but are not marked unsafe.
++    ///
++    /// **Why is this bad?** The function should probably be marked `unsafe`, since
++    /// for an arbitrary raw pointer, there is no way of telling for sure if it is
++    /// valid.
++    ///
++    /// **Known problems:**
++    ///
++    /// * It does not check functions recursively so if the pointer is passed to a
++    /// private non-`unsafe` function which does the dereferencing, the lint won't
++    /// trigger.
++    /// * It only checks for arguments whose type are raw pointers, not raw pointers
++    /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or
++    /// `some_argument.get_raw_ptr()`).
++    ///
++    /// **Example:**
++    /// ```rust
++    /// pub fn foo(x: *const u8) {
++    ///     println!("{}", unsafe { *x });
++    /// }
++    /// ```
++    pub NOT_UNSAFE_PTR_ARG_DEREF,
++    correctness,
++    "public functions dereferencing raw pointer arguments but not marked `unsafe`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for a [`#[must_use]`] attribute on
++    /// unit-returning functions and methods.
++    ///
++    /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
++    ///
++    /// **Why is this bad?** Unit values are useless. The attribute is likely
++    /// a remnant of a refactoring that removed the return type.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Examples:**
++    /// ```rust
++    /// #[must_use]
++    /// fn useless() { }
++    /// ```
++    pub MUST_USE_UNIT,
++    style,
++    "`#[must_use]` attribute on a unit-returning function / method"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for a [`#[must_use]`] attribute without
++    /// further information on functions and methods that return a type already
++    /// marked as `#[must_use]`.
++    ///
++    /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
++    ///
++    /// **Why is this bad?** The attribute isn't needed. Not using the result
++    /// will already be reported. Alternatively, one can add some text to the
++    /// attribute to improve the lint message.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Examples:**
++    /// ```rust
++    /// #[must_use]
++    /// fn double_must_use() -> Result<(), ()> {
++    ///     unimplemented!();
++    /// }
++    /// ```
++    pub DOUBLE_MUST_USE,
++    style,
++    "`#[must_use]` attribute on a `#[must_use]`-returning function / method"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for public functions that have no
++    /// [`#[must_use]`] attribute, but return something not already marked
++    /// must-use, have no mutable arg and mutate no statics.
++    ///
++    /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
++    ///
++    /// **Why is this bad?** Not bad at all, this lint just shows places where
++    /// you could add the attribute.
++    ///
++    /// **Known problems:** The lint only checks the arguments for mutable
++    /// types without looking if they are actually changed. On the other hand,
++    /// it also ignores a broad range of potentially interesting side effects,
++    /// because we cannot decide whether the programmer intends the function to
++    /// be called for the side effect or the result. Expect many false
++    /// positives. At least we don't lint if the result type is unit or already
++    /// `#[must_use]`.
++    ///
++    /// **Examples:**
++    /// ```rust
++    /// // this could be annotated with `#[must_use]`.
++    /// fn id<T>(t: T) -> T { t }
++    /// ```
++    pub MUST_USE_CANDIDATE,
++    pedantic,
++    "function or method that could take a `#[must_use]` attribute"
++}
++
++#[derive(Copy, Clone)]
++pub struct Functions {
++    threshold: u64,
++    max_lines: u64,
++}
++
++impl Functions {
++    pub fn new(threshold: u64, max_lines: u64) -> Self {
++        Self { threshold, max_lines }
++    }
++}
++
++impl_lint_pass!(Functions => [
++    TOO_MANY_ARGUMENTS,
++    TOO_MANY_LINES,
++    NOT_UNSAFE_PTR_ARG_DEREF,
++    MUST_USE_UNIT,
++    DOUBLE_MUST_USE,
++    MUST_USE_CANDIDATE,
++]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions {
++    fn check_fn(
++        &mut self,
++        cx: &LateContext<'a, 'tcx>,
++        kind: intravisit::FnKind<'tcx>,
++        decl: &'tcx hir::FnDecl<'_>,
++        body: &'tcx hir::Body<'_>,
++        span: Span,
++        hir_id: hir::HirId,
++    ) {
++        let unsafety = match kind {
++            intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _, _) => unsafety,
++            intravisit::FnKind::Method(_, sig, _, _) => sig.header.unsafety,
++            intravisit::FnKind::Closure(_) => return,
++        };
++
++        // don't warn for implementations, it's not their fault
++        if !is_trait_impl_item(cx, hir_id) {
++            // don't lint extern functions decls, it's not their fault either
++            match kind {
++                intravisit::FnKind::Method(
++                    _,
++                    &hir::FnSig {
++                        header: hir::FnHeader { abi: Abi::Rust, .. },
++                        ..
++                    },
++                    _,
++                    _,
++                )
++                | intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }, _, _) => {
++                    self.check_arg_number(cx, decl, span.with_hi(decl.output.span().hi()))
++                },
++                _ => {},
++            }
++        }
++
++        Self::check_raw_ptr(cx, unsafety, decl, body, hir_id);
++        self.check_line_number(cx, span, body);
++    }
++
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) {
++        let attr = must_use_attr(&item.attrs);
++        if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind {
++            if let Some(attr) = attr {
++                let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
++                check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr);
++                return;
++            }
++            if cx.access_levels.is_exported(item.hir_id)
++                && !is_proc_macro(&item.attrs)
++                && attr_by_name(&item.attrs, "no_mangle").is_none()
++            {
++                check_must_use_candidate(
++                    cx,
++                    &sig.decl,
++                    cx.tcx.hir().body(*body_id),
++                    item.span,
++                    item.hir_id,
++                    item.span.with_hi(sig.decl.output.span().hi()),
++                    "this function could have a `#[must_use]` attribute",
++                );
++            }
++        }
++    }
++
++    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem<'_>) {
++        if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind {
++            let attr = must_use_attr(&item.attrs);
++            if let Some(attr) = attr {
++                let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
++                check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr);
++            } else if cx.access_levels.is_exported(item.hir_id)
++                && !is_proc_macro(&item.attrs)
++                && trait_ref_of_method(cx, item.hir_id).is_none()
++            {
++                check_must_use_candidate(
++                    cx,
++                    &sig.decl,
++                    cx.tcx.hir().body(*body_id),
++                    item.span,
++                    item.hir_id,
++                    item.span.with_hi(sig.decl.output.span().hi()),
++                    "this method could have a `#[must_use]` attribute",
++                );
++            }
++        }
++    }
++
++    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem<'_>) {
++        if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind {
++            // don't lint extern functions decls, it's not their fault
++            if sig.header.abi == Abi::Rust {
++                self.check_arg_number(cx, &sig.decl, item.span.with_hi(sig.decl.output.span().hi()));
++            }
++
++            let attr = must_use_attr(&item.attrs);
++            if let Some(attr) = attr {
++                let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
++                check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr);
++            }
++            if let hir::TraitFn::Provided(eid) = *eid {
++                let body = cx.tcx.hir().body(eid);
++                Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id);
++
++                if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(&item.attrs) {
++                    check_must_use_candidate(
++                        cx,
++                        &sig.decl,
++                        body,
++                        item.span,
++                        item.hir_id,
++                        item.span.with_hi(sig.decl.output.span().hi()),
++                        "this method could have a `#[must_use]` attribute",
++                    );
++                }
++            }
++        }
++    }
++}
++
++impl<'a, 'tcx> Functions {
++    fn check_arg_number(self, cx: &LateContext<'_, '_>, decl: &hir::FnDecl<'_>, fn_span: Span) {
++        let args = decl.inputs.len() as u64;
++        if args > self.threshold {
++            span_lint(
++                cx,
++                TOO_MANY_ARGUMENTS,
++                fn_span,
++                &format!("this function has too many arguments ({}/{})", args, self.threshold),
++            );
++        }
++    }
++
++    fn check_line_number(self, cx: &LateContext<'_, '_>, span: Span, body: &'tcx hir::Body<'_>) {
++        if in_external_macro(cx.sess(), span) {
++            return;
++        }
++
++        let code_snippet = snippet(cx, body.value.span, "..");
++        let mut line_count: u64 = 0;
++        let mut in_comment = false;
++        let mut code_in_line;
++
++        // Skip the surrounding function decl.
++        let start_brace_idx = code_snippet.find('{').map_or(0, |i| i + 1);
++        let end_brace_idx = code_snippet.rfind('}').unwrap_or_else(|| code_snippet.len());
++        let function_lines = code_snippet[start_brace_idx..end_brace_idx].lines();
++
++        for mut line in function_lines {
++            code_in_line = false;
++            loop {
++                line = line.trim_start();
++                if line.is_empty() {
++                    break;
++                }
++                if in_comment {
++                    match line.find("*/") {
++                        Some(i) => {
++                            line = &line[i + 2..];
++                            in_comment = false;
++                            continue;
++                        },
++                        None => break,
++                    }
++                } else {
++                    let multi_idx = line.find("/*").unwrap_or_else(|| line.len());
++                    let single_idx = line.find("//").unwrap_or_else(|| line.len());
++                    code_in_line |= multi_idx > 0 && single_idx > 0;
++                    // Implies multi_idx is below line.len()
++                    if multi_idx < single_idx {
++                        line = &line[multi_idx + 2..];
++                        in_comment = true;
++                        continue;
++                    }
++                    break;
++                }
++            }
++            if code_in_line {
++                line_count += 1;
++            }
++        }
++
++        if line_count > self.max_lines {
++            span_lint(cx, TOO_MANY_LINES, span, "This function has a large number of lines.")
++        }
++    }
++
++    fn check_raw_ptr(
++        cx: &LateContext<'a, 'tcx>,
++        unsafety: hir::Unsafety,
++        decl: &'tcx hir::FnDecl<'_>,
++        body: &'tcx hir::Body<'_>,
++        hir_id: hir::HirId,
++    ) {
++        let expr = &body.value;
++        if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(hir_id) {
++            let raw_ptrs = iter_input_pats(decl, body)
++                .zip(decl.inputs.iter())
++                .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty))
++                .collect::<FxHashSet<_>>();
++
++            if !raw_ptrs.is_empty() {
++                let tables = cx.tcx.body_tables(body.id());
++                let mut v = DerefVisitor {
++                    cx,
++                    ptrs: raw_ptrs,
++                    tables,
++                };
++
++                intravisit::walk_expr(&mut v, expr);
++            }
++        }
++    }
++}
++
++fn check_needless_must_use(
++    cx: &LateContext<'_, '_>,
++    decl: &hir::FnDecl<'_>,
++    item_id: hir::HirId,
++    item_span: Span,
++    fn_header_span: Span,
++    attr: &Attribute,
++) {
++    if in_external_macro(cx.sess(), item_span) {
++        return;
++    }
++    if returns_unit(decl) {
++        span_lint_and_then(
++            cx,
++            MUST_USE_UNIT,
++            fn_header_span,
++            "this unit-returning function has a `#[must_use]` attribute",
++            |diag| {
++                diag.span_suggestion(
++                    attr.span,
++                    "remove the attribute",
++                    "".into(),
++                    Applicability::MachineApplicable,
++                );
++            },
++        );
++    } else if !attr.is_value_str() && is_must_use_ty(cx, return_ty(cx, item_id)) {
++        span_lint_and_help(
++            cx,
++            DOUBLE_MUST_USE,
++            fn_header_span,
++            "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`",
++            None,
++            "either add some descriptive text or remove the attribute",
++        );
++    }
++}
++
++fn check_must_use_candidate<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    decl: &'tcx hir::FnDecl<'_>,
++    body: &'tcx hir::Body<'_>,
++    item_span: Span,
++    item_id: hir::HirId,
++    fn_span: Span,
++    msg: &str,
++) {
++    if has_mutable_arg(cx, body)
++        || mutates_static(cx, body)
++        || in_external_macro(cx.sess(), item_span)
++        || returns_unit(decl)
++        || !cx.access_levels.is_exported(item_id)
++        || is_must_use_ty(cx, return_ty(cx, item_id))
++    {
++        return;
++    }
++    span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| {
++        if let Some(snippet) = snippet_opt(cx, fn_span) {
++            diag.span_suggestion(
++                fn_span,
++                "add the attribute",
++                format!("#[must_use] {}", snippet),
++                Applicability::MachineApplicable,
++            );
++        }
++    });
++}
++
++fn returns_unit(decl: &hir::FnDecl<'_>) -> bool {
++    match decl.output {
++        hir::FnRetTy::DefaultReturn(_) => true,
++        hir::FnRetTy::Return(ref ty) => match ty.kind {
++            hir::TyKind::Tup(ref tys) => tys.is_empty(),
++            hir::TyKind::Never => true,
++            _ => false,
++        },
++    }
++}
++
++fn has_mutable_arg(cx: &LateContext<'_, '_>, body: &hir::Body<'_>) -> bool {
++    let mut tys = FxHashSet::default();
++    body.params.iter().any(|param| is_mutable_pat(cx, &param.pat, &mut tys))
++}
++
++fn is_mutable_pat(cx: &LateContext<'_, '_>, pat: &hir::Pat<'_>, tys: &mut FxHashSet<DefId>) -> bool {
++    if let hir::PatKind::Wild = pat.kind {
++        return false; // ignore `_` patterns
++    }
++    let def_id = pat.hir_id.owner.to_def_id();
++    if cx.tcx.has_typeck_tables(def_id) {
++        is_mutable_ty(
++            cx,
++            &cx.tcx.typeck_tables_of(def_id.expect_local()).pat_ty(pat),
++            pat.span,
++            tys,
++        )
++    } else {
++        false
++    }
++}
++
++static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]];
++
++fn is_mutable_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut FxHashSet<DefId>) -> bool {
++    match ty.kind {
++        // primitive types are never mutable
++        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
++        ty::Adt(ref adt, ref substs) => {
++            tys.insert(adt.did) && !ty.is_freeze(cx.tcx, cx.param_env, span)
++                || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path))
++                    && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys))
++        },
++        ty::Tuple(ref substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)),
++        ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys),
++        ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => {
++            mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys)
++        },
++        // calling something constitutes a side effect, so return true on all callables
++        // also never calls need not be used, so return true for them, too
++        _ => true,
++    }
++}
++
++fn raw_ptr_arg(arg: &hir::Param<'_>, ty: &hir::Ty<'_>) -> Option<hir::HirId> {
++    if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.kind, &ty.kind) {
++        Some(id)
++    } else {
++        None
++    }
++}
++
++struct DerefVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    ptrs: FxHashSet<hir::HirId>,
++    tables: &'a ty::TypeckTables<'tcx>,
++}
++
++impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
++        match expr.kind {
++            hir::ExprKind::Call(ref f, args) => {
++                let ty = self.tables.expr_ty(f);
++
++                if type_is_unsafe_function(self.cx, ty) {
++                    for arg in args {
++                        self.check_arg(arg);
++                    }
++                }
++            },
++            hir::ExprKind::MethodCall(_, _, args) => {
++                let def_id = self.tables.type_dependent_def_id(expr.hir_id).unwrap();
++                let base_type = self.cx.tcx.type_of(def_id);
++
++                if type_is_unsafe_function(self.cx, base_type) {
++                    for arg in args {
++                        self.check_arg(arg);
++                    }
++                }
++            },
++            hir::ExprKind::Unary(hir::UnOp::UnDeref, ref ptr) => self.check_arg(ptr),
++            _ => (),
++        }
++
++        intravisit::walk_expr(self, expr);
++    }
++
++    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
++        intravisit::NestedVisitorMap::None
++    }
++}
++
++impl<'a, 'tcx> DerefVisitor<'a, 'tcx> {
++    fn check_arg(&self, ptr: &hir::Expr<'_>) {
++        if let hir::ExprKind::Path(ref qpath) = ptr.kind {
++            if let Res::Local(id) = qpath_res(self.cx, qpath, ptr.hir_id) {
++                if self.ptrs.contains(&id) {
++                    span_lint(
++                        self.cx,
++                        NOT_UNSAFE_PTR_ARG_DEREF,
++                        ptr.span,
++                        "this public function dereferences a raw pointer but is not marked `unsafe`",
++                    );
++                }
++            }
++        }
++    }
++}
++
++struct StaticMutVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    mutates_static: bool,
++}
++
++impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
++        use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
++
++        if self.mutates_static {
++            return;
++        }
++        match expr.kind {
++            Call(_, args) | MethodCall(_, _, args) => {
++                let mut tys = FxHashSet::default();
++                for arg in args {
++                    let def_id = arg.hir_id.owner.to_def_id();
++                    if self.cx.tcx.has_typeck_tables(def_id)
++                        && is_mutable_ty(
++                            self.cx,
++                            self.cx.tcx.typeck_tables_of(def_id.expect_local()).expr_ty(arg),
++                            arg.span,
++                            &mut tys,
++                        )
++                        && is_mutated_static(self.cx, arg)
++                    {
++                        self.mutates_static = true;
++                        return;
++                    }
++                    tys.clear();
++                }
++            },
++            Assign(ref target, ..) | AssignOp(_, ref target, _) | AddrOf(_, hir::Mutability::Mut, ref target) => {
++                self.mutates_static |= is_mutated_static(self.cx, target)
++            },
++            _ => {},
++        }
++    }
++
++    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
++        intravisit::NestedVisitorMap::None
++    }
++}
++
++fn is_mutated_static(cx: &LateContext<'_, '_>, e: &hir::Expr<'_>) -> bool {
++    use hir::ExprKind::{Field, Index, Path};
++
++    match e.kind {
++        Path(ref qpath) => {
++            if let Res::Local(_) = qpath_res(cx, qpath, e.hir_id) {
++                false
++            } else {
++                true
++            }
++        },
++        Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(cx, inner),
++        _ => false,
++    }
++}
++
++fn mutates_static<'a, 'tcx>(cx: &'a LateContext<'a, 'tcx>, body: &'tcx hir::Body<'_>) -> bool {
++    let mut v = StaticMutVisitor {
++        cx,
++        mutates_static: false,
++    };
++    intravisit::walk_expr(&mut v, &body.value);
++    v.mutates_static
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..704a95ec0a0909a50efc12f84fbec8f7d1ec54de
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,110 @@@
++use crate::utils;
++use rustc_hir::intravisit::FnKind;
++use rustc_hir::{Body, FnDecl, HirId};
++use rustc_infer::infer::TyCtxtInferExt;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::{Opaque, Predicate::Trait, ToPolyTraitRef};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::{sym, Span};
++use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt;
++use rustc_trait_selection::traits::{self, FulfillmentError, TraitEngine};
++
++declare_clippy_lint! {
++    /// **What it does:** This lint requires Future implementations returned from
++    /// functions and methods to implement the `Send` marker trait. It is mostly
++    /// used by library authors (public and internal) that target an audience where
++    /// multithreaded executors are likely to be used for running these Futures.
++    ///
++    /// **Why is this bad?** A Future implementation captures some state that it
++    /// needs to eventually produce its final value. When targeting a multithreaded
++    /// executor (which is the norm on non-embedded devices) this means that this
++    /// state may need to be transported to other threads, in other words the
++    /// whole Future needs to implement the `Send` marker trait. If it does not,
++    /// then the resulting Future cannot be submitted to a thread pool in the
++    /// end user’s code.
++    ///
++    /// Especially for generic functions it can be confusing to leave the
++    /// discovery of this problem to the end user: the reported error location
++    /// will be far from its cause and can in many cases not even be fixed without
++    /// modifying the library where the offending Future implementation is
++    /// produced.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// async fn not_send(bytes: std::rc::Rc<[u8]>) {}
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// async fn is_send(bytes: std::sync::Arc<[u8]>) {}
++    /// ```
++    pub FUTURE_NOT_SEND,
++    nursery,
++    "public Futures must be Send"
++}
++
++declare_lint_pass!(FutureNotSend => [FUTURE_NOT_SEND]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FutureNotSend {
++    fn check_fn(
++        &mut self,
++        cx: &LateContext<'a, 'tcx>,
++        kind: FnKind<'tcx>,
++        decl: &'tcx FnDecl<'tcx>,
++        _: &'tcx Body<'tcx>,
++        _: Span,
++        hir_id: HirId,
++    ) {
++        if let FnKind::Closure(_) = kind {
++            return;
++        }
++        let ret_ty = utils::return_ty(cx, hir_id);
++        if let Opaque(id, subst) = ret_ty.kind {
++            let preds = cx.tcx.predicates_of(id).instantiate(cx.tcx, subst);
++            let mut is_future = false;
++            for p in preds.predicates {
++                if let Some(trait_ref) = p.to_opt_poly_trait_ref() {
++                    if Some(trait_ref.def_id()) == cx.tcx.lang_items().future_trait() {
++                        is_future = true;
++                        break;
++                    }
++                }
++            }
++            if is_future {
++                let send_trait = cx.tcx.get_diagnostic_item(sym::send_trait).unwrap();
++                let span = decl.output.span();
++                let send_result = cx.tcx.infer_ctxt().enter(|infcx| {
++                    let cause = traits::ObligationCause::misc(span, hir_id);
++                    let mut fulfillment_cx = traits::FulfillmentContext::new();
++                    fulfillment_cx.register_bound(&infcx, cx.param_env, ret_ty, send_trait, cause);
++                    fulfillment_cx.select_all_or_error(&infcx)
++                });
++                if let Err(send_errors) = send_result {
++                    utils::span_lint_and_then(
++                        cx,
++                        FUTURE_NOT_SEND,
++                        span,
++                        "future cannot be sent between threads safely",
++                        |db| {
++                            cx.tcx.infer_ctxt().enter(|infcx| {
++                                for FulfillmentError { obligation, .. } in send_errors {
++                                    infcx.maybe_note_obligation_cause_for_async_await(db, &obligation);
++                                    if let Trait(trait_pred, _) = obligation.predicate {
++                                        let trait_ref = trait_pred.to_poly_trait_ref();
++                                        db.note(&*format!(
++                                            "`{}` doesn't implement `{}`",
++                                            trait_ref.self_ty(),
++                                            trait_ref.print_only_trait_path(),
++                                        ));
++                                    }
++                                }
++                            })
++                        },
++                    );
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c32e0a2290d12c10caa16f6f6e18223bd56fe421
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,103 @@@
++//! 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 if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++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::Spanned;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for using `x.get(x.len() - 1)` instead of
++    /// `x.last()`.
++    ///
++    /// **Why is this bad?** Using `x.last()` is easier to read and has the same
++    /// result.
++    ///
++    /// Note that using `x[x.len() - 1]` is semantically different from
++    /// `x.last()`.  Indexing into the array will panic on out-of-bounds
++    /// accesses, while `x.get()` and `x.last()` will return `None`.
++    ///
++    /// There is another lint (get_unwrap) that covers the case of using
++    /// `x.get(index).unwrap()` instead of `x[index]`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// // Bad
++    /// let x = vec![2, 3, 5];
++    /// let last_element = x.get(x.len() - 1);
++    ///
++    /// // Good
++    /// let x = vec![2, 3, 5];
++    /// let last_element = x.last();
++    /// ```
++    pub GET_LAST_WITH_LEN,
++    complexity,
++    "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler"
++}
++
++declare_lint_pass!(GetLastWithLen => [GET_LAST_WITH_LEN]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for GetLastWithLen {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            // Is a method call
++            if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind;
++
++            // Method name is "get"
++            if path.ident.name == sym!(get);
++
++            // Argument 0 (the struct we're calling the method on) is a vector
++            if let Some(struct_calling_on) = args.get(0);
++            let struct_ty = cx.tables.expr_ty(struct_calling_on);
++            if is_type_diagnostic_item(cx, struct_ty, sym!(vec_type));
++
++            // Argument to "get" is a subtraction
++            if let Some(get_index_arg) = args.get(1);
++            if let ExprKind::Binary(
++                Spanned {
++                    node: BinOpKind::Sub,
++                    ..
++                },
++                lhs,
++                rhs,
++            ) = &get_index_arg.kind;
++
++            // LHS of subtraction is "x.len()"
++            if let ExprKind::MethodCall(arg_lhs_path, _, lhs_args) = &lhs.kind;
++            if arg_lhs_path.ident.name == sym!(len);
++            if let Some(arg_lhs_struct) = lhs_args.get(0);
++
++            // The two vectors referenced (x in x.get(...) and in x.len())
++            if SpanlessEq::new(cx).eq_expr(struct_calling_on, arg_lhs_struct);
++
++            // RHS of subtraction is 1
++            if let ExprKind::Lit(rhs_lit) = &rhs.kind;
++            if let LitKind::Int(1, ..) = rhs_lit.node;
++
++            then {
++                let mut applicability = Applicability::MachineApplicable;
++                let vec_name = snippet_with_applicability(
++                    cx,
++                    struct_calling_on.span, "vec",
++                    &mut applicability,
++                );
++
++                span_lint_and_sugg(
++                    cx,
++                    GET_LAST_WITH_LEN,
++                    expr.span,
++                    &format!("accessing last element with `{0}.get({0}.len() - 1)`", vec_name),
++                    "try",
++                    format!("{}.last()", vec_name),
++                    applicability,
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..33a9478f058830adedb7cfea69f26c5f8c50f669
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,124 @@@
++use crate::utils::{
++    match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite, span_lint_and_sugg,
++};
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, HirId, MatchSource};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for always-identical `Into`/`From`/`IntoIter` conversions.
++    ///
++    /// **Why is this bad?** Redundant code.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// // format!() returns a `String`
++    /// let s: String = format!("hello").into();
++    /// ```
++    pub IDENTITY_CONVERSION,
++    complexity,
++    "using always-identical `Into`/`From`/`IntoIter` conversions"
++}
++
++#[derive(Default)]
++pub struct IdentityConversion {
++    try_desugar_arm: Vec<HirId>,
++}
++
++impl_lint_pass!(IdentityConversion => [IDENTITY_CONVERSION]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityConversion {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        if e.span.from_expansion() {
++            return;
++        }
++
++        if Some(&e.hir_id) == self.try_desugar_arm.last() {
++            return;
++        }
++
++        match e.kind {
++            ExprKind::Match(_, ref arms, MatchSource::TryDesugar) => {
++                let e = match arms[0].body.kind {
++                    ExprKind::Ret(Some(ref e)) | ExprKind::Break(_, Some(ref e)) => e,
++                    _ => return,
++                };
++                if let ExprKind::Call(_, ref args) = e.kind {
++                    self.try_desugar_arm.push(args[0].hir_id);
++                }
++            },
++
++            ExprKind::MethodCall(ref name, .., ref args) => {
++                if match_trait_method(cx, e, &paths::INTO) && &*name.ident.as_str() == "into" {
++                    let a = cx.tables.expr_ty(e);
++                    let b = cx.tables.expr_ty(&args[0]);
++                    if same_tys(cx, a, b) {
++                        let sugg = snippet_with_macro_callsite(cx, args[0].span, "<expr>").to_string();
++
++                        span_lint_and_sugg(
++                            cx,
++                            IDENTITY_CONVERSION,
++                            e.span,
++                            "identical conversion",
++                            "consider removing `.into()`",
++                            sugg,
++                            Applicability::MachineApplicable, // snippet
++                        );
++                    }
++                }
++                if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" {
++                    let a = cx.tables.expr_ty(e);
++                    let b = cx.tables.expr_ty(&args[0]);
++                    if same_tys(cx, a, b) {
++                        let sugg = snippet(cx, args[0].span, "<expr>").into_owned();
++                        span_lint_and_sugg(
++                            cx,
++                            IDENTITY_CONVERSION,
++                            e.span,
++                            "identical conversion",
++                            "consider removing `.into_iter()`",
++                            sugg,
++                            Applicability::MachineApplicable, // snippet
++                        );
++                    }
++                }
++            },
++
++            ExprKind::Call(ref path, ref args) => {
++                if let ExprKind::Path(ref qpath) = path.kind {
++                    if let Some(def_id) = cx.tables.qpath_res(qpath, path.hir_id).opt_def_id() {
++                        if match_def_path(cx, def_id, &paths::FROM_FROM) {
++                            let a = cx.tables.expr_ty(e);
++                            let b = cx.tables.expr_ty(&args[0]);
++                            if same_tys(cx, a, b) {
++                                let sugg = snippet(cx, args[0].span.source_callsite(), "<expr>").into_owned();
++                                let sugg_msg =
++                                    format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));
++                                span_lint_and_sugg(
++                                    cx,
++                                    IDENTITY_CONVERSION,
++                                    e.span,
++                                    "identical conversion",
++                                    &sugg_msg,
++                                    sugg,
++                                    Applicability::MachineApplicable, // snippet
++                                );
++                            }
++                        }
++                    }
++                }
++            },
++
++            _ => {},
++        }
++    }
++
++    fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        if Some(&e.hir_id) == self.try_desugar_arm.last() {
++            self.try_desugar_arm.pop();
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..088e4ab1921fb8a31f0d38397521dbb1cbeb2486
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,82 @@@
++use rustc_hir::{BinOpKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++use crate::consts::{constant_simple, Constant};
++use crate::utils::{clip, snippet, span_lint, unsext};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for identity operations, e.g., `x + 0`.
++    ///
++    /// **Why is this bad?** This code can be removed without changing the
++    /// meaning. So it just obscures what's going on. Delete it mercilessly.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let x = 1;
++    /// x / 1 + 0 * 1 - 0 | 0;
++    /// ```
++    pub IDENTITY_OP,
++    complexity,
++    "using identity operations, e.g., `x + 0` or `y / 1`"
++}
++
++declare_lint_pass!(IdentityOp => [IDENTITY_OP]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityOp {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        if e.span.from_expansion() {
++            return;
++        }
++        if let ExprKind::Binary(ref cmp, ref left, ref right) = e.kind {
++            match cmp.node {
++                BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
++                    check(cx, left, 0, e.span, right.span);
++                    check(cx, right, 0, e.span, left.span);
++                },
++                BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => check(cx, right, 0, e.span, left.span),
++                BinOpKind::Mul => {
++                    check(cx, left, 1, e.span, right.span);
++                    check(cx, right, 1, e.span, left.span);
++                },
++                BinOpKind::Div => check(cx, right, 1, e.span, left.span),
++                BinOpKind::BitAnd => {
++                    check(cx, left, -1, e.span, right.span);
++                    check(cx, right, -1, e.span, left.span);
++                },
++                _ => (),
++            }
++        }
++    }
++}
++
++#[allow(clippy::cast_possible_wrap)]
++fn check(cx: &LateContext<'_, '_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) {
++    if let Some(Constant::Int(v)) = constant_simple(cx, cx.tables, e) {
++        let check = match cx.tables.expr_ty(e).kind {
++            ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
++            ty::Uint(uty) => clip(cx.tcx, !0, uty),
++            _ => return,
++        };
++        if match m {
++            0 => v == 0,
++            -1 => v == check,
++            1 => v == 1,
++            _ => unreachable!(),
++        } {
++            span_lint(
++                cx,
++                IDENTITY_OP,
++                span,
++                &format!(
++                    "the operation is ineffective. Consider reducing it to `{}`",
++                    snippet(cx, arg, "..")
++                ),
++            );
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ae92a96d16347646e0b5b7cd28c54cc132399d4a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,160 @@@
++use crate::utils::{is_type_diagnostic_item, span_lint_and_help, SpanlessEq};
++use if_chain::if_chain;
++use rustc_hir::intravisit::{self as visit, NestedVisitorMap, Visitor};
++use rustc_hir::{Expr, ExprKind, MatchSource};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `Mutex::lock` calls in `if let` expression
++    /// with lock calls in any of the else blocks.
++    ///
++    /// **Why is this bad?** The Mutex lock remains held for the whole
++    /// `if let ... else` block and deadlocks.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust,ignore
++    /// if let Ok(thing) = mutex.lock() {
++    ///     do_thing();
++    /// } else {
++    ///     mutex.lock();
++    /// }
++    /// ```
++    /// Should be written
++    /// ```rust,ignore
++    /// let locked = mutex.lock();
++    /// if let Ok(thing) = locked {
++    ///     do_thing(thing);
++    /// } else {
++    ///     use_locked(locked);
++    /// }
++    /// ```
++    pub IF_LET_MUTEX,
++    correctness,
++    "locking a `Mutex` in an `if let` block can cause deadlocks"
++}
++
++declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]);
++
++impl LateLintPass<'_, '_> for IfLetMutex {
++    fn check_expr(&mut self, cx: &LateContext<'_, '_>, ex: &'_ Expr<'_>) {
++        let mut arm_visit = ArmVisitor {
++            mutex_lock_called: false,
++            found_mutex: None,
++            cx,
++        };
++        let mut op_visit = OppVisitor {
++            mutex_lock_called: false,
++            found_mutex: None,
++            cx,
++        };
++        if let ExprKind::Match(
++            ref op,
++            ref arms,
++            MatchSource::IfLetDesugar {
++                contains_else_clause: true,
++            },
++        ) = ex.kind
++        {
++            op_visit.visit_expr(op);
++            if op_visit.mutex_lock_called {
++                for arm in *arms {
++                    arm_visit.visit_arm(arm);
++                }
++
++                if arm_visit.mutex_lock_called && arm_visit.same_mutex(cx, op_visit.found_mutex.unwrap()) {
++                    span_lint_and_help(
++                        cx,
++                        IF_LET_MUTEX,
++                        ex.span,
++                        "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
++                        None,
++                        "move the lock call outside of the `if let ...` expression",
++                    );
++                }
++            }
++        }
++    }
++}
++
++/// Checks if `Mutex::lock` is called in the `if let _ = expr.
++pub struct OppVisitor<'tcx, 'l> {
++    mutex_lock_called: bool,
++    found_mutex: Option<&'tcx Expr<'tcx>>,
++    cx: &'tcx LateContext<'tcx, 'l>,
++}
++
++impl<'tcx, 'l> Visitor<'tcx> for OppVisitor<'tcx, 'l> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            if let Some(mutex) = is_mutex_lock_call(self.cx, expr);
++            then {
++                self.found_mutex = Some(mutex);
++                self.mutex_lock_called = true;
++                return;
++            }
++        }
++        visit::walk_expr(self, expr);
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++/// Checks if `Mutex::lock` is called in any of the branches.
++pub struct ArmVisitor<'tcx, 'l> {
++    mutex_lock_called: bool,
++    found_mutex: Option<&'tcx Expr<'tcx>>,
++    cx: &'tcx LateContext<'tcx, 'l>,
++}
++
++impl<'tcx, 'l> Visitor<'tcx> for ArmVisitor<'tcx, 'l> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
++        if_chain! {
++            if let Some(mutex) = is_mutex_lock_call(self.cx, expr);
++            then {
++                self.found_mutex = Some(mutex);
++                self.mutex_lock_called = true;
++                return;
++            }
++        }
++        visit::walk_expr(self, expr);
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
++    fn same_mutex(&self, cx: &LateContext<'_, '_>, op_mutex: &Expr<'_>) -> bool {
++        if let Some(arm_mutex) = self.found_mutex {
++            SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex)
++        } else {
++            false
++        }
++    }
++}
++
++fn is_mutex_lock_call<'a>(cx: &LateContext<'a, '_>, expr: &'a Expr<'_>) -> Option<&'a Expr<'a>> {
++    if_chain! {
++        if let ExprKind::MethodCall(path, _span, args) = &expr.kind;
++        if path.ident.to_string() == "lock";
++        let ty = cx.tables.expr_ty(&args[0]);
++        if is_type_diagnostic_item(cx, ty, sym!(mutex_type));
++        then {
++            Some(&args[0])
++        } else {
++            None
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9b13f7609247a26f122252b9d97ac34dbd1c8b08
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,72 @@@
++use crate::utils::{is_type_diagnostic_item, method_chain_args, snippet_with_applicability, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, MatchSource, PatKind, QPath};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:*** Checks for unnecessary `ok()` in if let.
++    ///
++    /// **Why is this bad?** Calling `ok()` in if let is unnecessary, instead match
++    /// on `Ok(pat)`
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// for i in iter {
++    ///     if let Some(value) = i.parse().ok() {
++    ///         vec.push(value)
++    ///     }
++    /// }
++    /// ```
++    /// Could be written:
++    ///
++    /// ```ignore
++    /// for i in iter {
++    ///     if let Ok(value) = i.parse() {
++    ///         vec.push(value)
++    ///     }
++    /// }
++    /// ```
++    pub IF_LET_SOME_RESULT,
++    style,
++    "usage of `ok()` in `if let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
++}
++
++declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OkIfLet {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if_chain! { //begin checking variables
++            if let ExprKind::Match(ref op, ref body, source) = expr.kind; //test if expr is a match
++            if let MatchSource::IfLetDesugar { .. } = source; //test if it is an If Let
++            if let ExprKind::MethodCall(_, ok_span, ref result_types) = op.kind; //check is expr.ok() has type Result<T,E>.ok()
++            if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _)  = body[0].pat.kind; //get operation
++            if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
++            if is_type_diagnostic_item(cx, cx.tables.expr_ty(&result_types[0]), sym!(result_type));
++            if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
++
++            then {
++                let mut applicability = Applicability::MachineApplicable;
++                let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
++                let trimmed_ok = snippet_with_applicability(cx, op.span.until(ok_span), "", &mut applicability);
++                let sugg = format!(
++                    "if let Ok({}) = {}",
++                    some_expr_string,
++                    trimmed_ok.trim().trim_end_matches('.'),
++                );
++                span_lint_and_sugg(
++                    cx,
++                    IF_LET_SOME_RESULT,
++                    expr.span.with_hi(op.span.hi()),
++                    "Matching on `Some` with `ok()` is redundant",
++                    &format!("Consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),
++                    sugg,
++                    applicability,
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c11e291f98e4b41766dbf22c8940d8f9fff3b74c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,83 @@@
++//! lint on if branches that could be swapped so no `!` operation is necessary
++//! on the condition
++
++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.
++    ///
++    /// **Why is this bad?** Negations reduce the readability of statements.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let v: Vec<usize> = vec![];
++    /// # fn a() {}
++    /// # fn b() {}
++    /// if !v.is_empty() {
++    ///     a()
++    /// } else {
++    ///     b()
++    /// }
++    /// ```
++    ///
++    /// Could be written:
++    ///
++    /// ```rust
++    /// # let v: Vec<usize> = vec![];
++    /// # fn a() {}
++    /// # fn b() {}
++    /// if v.is_empty() {
++    ///     b()
++    /// } else {
++    ///     a()
++    /// }
++    /// ```
++    pub IF_NOT_ELSE,
++    pedantic,
++    "`if` branches that could be swapped so no negation operation is necessary on the condition"
++}
++
++declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]);
++
++impl EarlyLintPass for IfNotElse {
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) {
++        if in_external_macro(cx.sess(), item.span) {
++            return;
++        }
++        if let ExprKind::If(ref cond, _, Some(ref els)) = item.kind {
++            if let ExprKind::Block(..) = els.kind {
++                match cond.kind {
++                    ExprKind::Unary(UnOp::Not, _) => {
++                        span_lint_and_help(
++                            cx,
++                            IF_NOT_ELSE,
++                            item.span,
++                            "Unnecessary boolean `not` operation",
++                            None,
++                            "remove the `!` and swap the blocks of the `if`/`else`",
++                        );
++                    },
++                    ExprKind::Binary(ref kind, _, _) if kind.node == BinOpKind::Ne => {
++                        span_lint_and_help(
++                            cx,
++                            IF_NOT_ELSE,
++                            item.span,
++                            "Unnecessary `!=` operation",
++                            None,
++                            "change to `==` and swap the blocks of the `if`/`else`",
++                        );
++                    },
++                    _ => (),
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c4308fd26a3021092e9f653a016461e7904760c8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,150 @@@
++use crate::utils::{
++    fn_has_unsatisfiable_preds, match_def_path,
++    paths::{BEGIN_PANIC, BEGIN_PANIC_FMT},
++    snippet_opt, span_lint_and_then,
++};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::FnKind;
++use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for missing return statements at the end of a block.
++    ///
++    /// **Why is this bad?** Actually omitting the return keyword is idiomatic Rust code. Programmers
++    /// coming from other languages might prefer the expressiveness of `return`. It's possible to miss
++    /// the last returning statement because the only difference is a missing `;`. Especially in bigger
++    /// code with multiple return paths having a `return` keyword makes it easier to find the
++    /// corresponding statements.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// fn foo(x: usize) -> usize {
++    ///     x
++    /// }
++    /// ```
++    /// add return
++    /// ```rust
++    /// fn foo(x: usize) -> usize {
++    ///     return x;
++    /// }
++    /// ```
++    pub IMPLICIT_RETURN,
++    restriction,
++    "use a return statement like `return expr` instead of an expression"
++}
++
++declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);
++
++static LINT_BREAK: &str = "change `break` to `return` as shown";
++static LINT_RETURN: &str = "add `return` as shown";
++
++fn lint(cx: &LateContext<'_, '_>, outer_span: Span, inner_span: Span, msg: &str) {
++    let outer_span = outer_span.source_callsite();
++    let inner_span = inner_span.source_callsite();
++
++    span_lint_and_then(cx, IMPLICIT_RETURN, outer_span, "missing `return` statement", |diag| {
++        if let Some(snippet) = snippet_opt(cx, inner_span) {
++            diag.span_suggestion(
++                outer_span,
++                msg,
++                format!("return {}", snippet),
++                Applicability::MachineApplicable,
++            );
++        }
++    });
++}
++
++fn expr_match(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++    match expr.kind {
++        // loops could be using `break` instead of `return`
++        ExprKind::Block(block, ..) | ExprKind::Loop(block, ..) => {
++            if let Some(expr) = &block.expr {
++                expr_match(cx, expr);
++            }
++            // only needed in the case of `break` with `;` at the end
++            else if let Some(stmt) = block.stmts.last() {
++                if_chain! {
++                    if let StmtKind::Semi(expr, ..) = &stmt.kind;
++                    // make sure it's a break, otherwise we want to skip
++                    if let ExprKind::Break(.., break_expr) = &expr.kind;
++                    if let Some(break_expr) = break_expr;
++                    then {
++                            lint(cx, expr.span, break_expr.span, LINT_BREAK);
++                    }
++                }
++            }
++        },
++        // use `return` instead of `break`
++        ExprKind::Break(.., break_expr) => {
++            if let Some(break_expr) = break_expr {
++                lint(cx, expr.span, break_expr.span, LINT_BREAK);
++            }
++        },
++        ExprKind::Match(.., arms, source) => {
++            let check_all_arms = match source {
++                MatchSource::IfLetDesugar {
++                    contains_else_clause: has_else,
++                } => has_else,
++                _ => true,
++            };
++
++            if check_all_arms {
++                for arm in arms {
++                    expr_match(cx, &arm.body);
++                }
++            } else {
++                expr_match(cx, &arms.first().expect("`if let` doesn't have a single arm").body);
++            }
++        },
++        // skip if it already has a return statement
++        ExprKind::Ret(..) => (),
++        // make sure it's not a call that panics
++        ExprKind::Call(expr, ..) => {
++            if_chain! {
++                if let ExprKind::Path(qpath) = &expr.kind;
++                if let Some(path_def_id) = cx.tables.qpath_res(qpath, expr.hir_id).opt_def_id();
++                if match_def_path(cx, path_def_id, &BEGIN_PANIC) ||
++                    match_def_path(cx, path_def_id, &BEGIN_PANIC_FMT);
++                then { }
++                else {
++                    lint(cx, expr.span, expr.span, LINT_RETURN)
++                }
++            }
++        },
++        // everything else is missing `return`
++        _ => lint(cx, expr.span, expr.span, LINT_RETURN),
++    }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitReturn {
++    fn check_fn(
++        &mut self,
++        cx: &LateContext<'a, 'tcx>,
++        _: FnKind<'tcx>,
++        _: &'tcx FnDecl<'_>,
++        body: &'tcx Body<'_>,
++        span: Span,
++        _: HirId,
++    ) {
++        let def_id = cx.tcx.hir().body_owner_def_id(body.id());
++
++        // Building MIR for `fn`s with unsatisfiable preds results in ICE.
++        if fn_has_unsatisfiable_preds(cx, def_id.to_def_id()) {
++            return;
++        }
++
++        let mir = cx.tcx.optimized_mir(def_id.to_def_id());
++
++        // checking return type through MIR, HIR is not able to determine inferred closure return types
++        // make sure it's not a macro
++        if !mir.return_ty().is_unit() && !span.from_expansion() {
++            expr_match(cx, &body.value);
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..155a93de4facfd5aeaef5a3e77397e6bd3c345c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,173 @@@
++use crate::utils::{higher, in_macro, match_qpath, span_lint_and_sugg, SpanlessEq};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, StmtKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for implicit saturating subtraction.
++    ///
++    /// **Why is this bad?** Simplicity and readability. Instead we can easily use an builtin function.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// let end: u32 = 10;
++    /// let start: u32 = 5;
++    ///
++    /// let mut i: u32 = end - start;
++    ///
++    /// // Bad
++    /// if i != 0 {
++    ///     i -= 1;
++    /// }
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// let end: u32 = 10;
++    /// let start: u32 = 5;
++    ///
++    /// let mut i: u32 = end - start;
++    ///
++    /// // Good
++    /// i = i.saturating_sub(1);
++    /// ```
++    pub IMPLICIT_SATURATING_SUB,
++    pedantic,
++    "Perform saturating subtraction instead of implicitly checking lower bound of data type"
++}
++
++declare_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitSaturatingSub {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) {
++        if in_macro(expr.span) {
++            return;
++        }
++        if_chain! {
++            if let Some((ref cond, ref then, None)) = higher::if_block(&expr);
++
++            // Check if the conditional expression is a binary operation
++            if let ExprKind::Binary(ref cond_op, ref cond_left, ref cond_right) = cond.kind;
++
++            // Ensure that the binary operator is >, != and <
++            if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node;
++
++            // Check if the true condition block has only one statement
++            if let ExprKind::Block(ref block, _) = then.kind;
++            if block.stmts.len() == 1 && block.expr.is_none();
++
++            // Check if assign operation is done
++            if let StmtKind::Semi(ref e) = block.stmts[0].kind;
++            if let Some(target) = subtracts_one(cx, e);
++
++            // Extracting out the variable name
++            if let ExprKind::Path(ref assign_path) = target.kind;
++            if let QPath::Resolved(_, ref ares_path) = assign_path;
++
++            then {
++                // Handle symmetric conditions in the if statement
++                let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) {
++                    if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node {
++                        (cond_left, cond_right)
++                    } else {
++                        return;
++                    }
++                } else if SpanlessEq::new(cx).eq_expr(cond_right, target) {
++                    if BinOpKind::Lt == cond_op.node || BinOpKind::Ne == cond_op.node {
++                        (cond_right, cond_left)
++                    } else {
++                        return;
++                    }
++                } else {
++                    return;
++                };
++
++                // Check if the variable in the condition statement is an integer
++                if !cx.tables.expr_ty(cond_var).is_integral() {
++                    return;
++                }
++
++                // Get the variable name
++                let var_name = ares_path.segments[0].ident.name.as_str();
++                const INT_TYPES: [&str; 5] = ["i8", "i16", "i32", "i64", "i128"];
++
++                match cond_num_val.kind {
++                    ExprKind::Lit(ref cond_lit) => {
++                        // Check if the constant is zero
++                        if let LitKind::Int(0, _) = cond_lit.node {
++                            if cx.tables.expr_ty(cond_left).is_signed() {
++                            } else {
++                                print_lint_and_sugg(cx, &var_name, expr);
++                            };
++                        }
++                    },
++                    ExprKind::Path(ref cond_num_path) => {
++                        if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "MIN"])) {
++                            print_lint_and_sugg(cx, &var_name, expr);
++                        };
++                    },
++                    ExprKind::Call(ref func, _) => {
++                        if let ExprKind::Path(ref cond_num_path) = func.kind {
++                            if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "min_value"])) {
++                                print_lint_and_sugg(cx, &var_name, expr);
++                            }
++                        };
++                    },
++                    _ => (),
++                }
++            }
++        }
++    }
++}
++
++fn subtracts_one<'a>(cx: &LateContext<'_, '_>, expr: &Expr<'a>) -> Option<&'a Expr<'a>> {
++    match expr.kind {
++        ExprKind::AssignOp(ref op1, ref target, ref value) => {
++            if_chain! {
++                if BinOpKind::Sub == op1.node;
++                // Check if literal being subtracted is one
++                if let ExprKind::Lit(ref lit1) = value.kind;
++                if let LitKind::Int(1, _) = lit1.node;
++                then {
++                    Some(target)
++                } else {
++                    None
++                }
++            }
++        },
++        ExprKind::Assign(ref target, ref value, _) => {
++            if_chain! {
++                if let ExprKind::Binary(ref op1, ref left1, ref right1) = value.kind;
++                if BinOpKind::Sub == op1.node;
++
++                if SpanlessEq::new(cx).eq_expr(left1, target);
++
++                if let ExprKind::Lit(ref lit1) = right1.kind;
++                if let LitKind::Int(1, _) = lit1.node;
++                then {
++                    Some(target)
++                } else {
++                    None
++                }
++            }
++        },
++        _ => None,
++    }
++}
++
++fn print_lint_and_sugg(cx: &LateContext<'_, '_>, var_name: &str, expr: &Expr<'_>) {
++    span_lint_and_sugg(
++        cx,
++        IMPLICIT_SATURATING_SUB,
++        expr.span,
++        "Implicitly performing saturating subtraction",
++        "try",
++        format!("{} = {}.saturating_sub({});", var_name, var_name, 1.to_string()),
++        Applicability::MachineApplicable,
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c5808dd540b6f43c3940e217d1328c549bf3f08b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,193 @@@
++//! lint on indexing and slicing operations
++
++use crate::consts::{constant, Constant};
++use crate::utils::{higher, span_lint, span_lint_and_help};
++use rustc_ast::ast::RangeLimits;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for out of bounds array indexing with a constant
++    /// index.
++    ///
++    /// **Why is this bad?** This will always panic at runtime.
++    ///
++    /// **Known problems:** Hopefully none.
++    ///
++    /// **Example:**
++    /// ```no_run
++    /// # #![allow(const_err)]
++    /// let x = [1, 2, 3, 4];
++    ///
++    /// // Bad
++    /// x[9];
++    /// &x[2..9];
++    ///
++    /// // Good
++    /// x[0];
++    /// x[3];
++    /// ```
++    pub OUT_OF_BOUNDS_INDEXING,
++    correctness,
++    "out of bounds constant indexing"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of indexing or slicing. Arrays are special cases, this lint
++    /// does report on arrays if we can tell that slicing operations are in bounds and does not
++    /// lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint.
++    ///
++    /// **Why is this bad?** Indexing and slicing can panic at runtime and there are
++    /// safe alternatives.
++    ///
++    /// **Known problems:** Hopefully none.
++    ///
++    /// **Example:**
++    /// ```rust,no_run
++    /// // Vector
++    /// let x = vec![0; 5];
++    ///
++    /// // Bad
++    /// x[2];
++    /// &x[2..100];
++    /// &x[2..];
++    /// &x[..100];
++    ///
++    /// // Good
++    /// x.get(2);
++    /// x.get(2..100);
++    /// x.get(2..);
++    /// x.get(..100);
++    ///
++    /// // Array
++    /// let y = [0, 1, 2, 3];
++    ///
++    /// // Bad
++    /// &y[10..100];
++    /// &y[10..];
++    /// &y[..100];
++    ///
++    /// // Good
++    /// &y[2..];
++    /// &y[..2];
++    /// &y[0..3];
++    /// y.get(10);
++    /// y.get(10..100);
++    /// y.get(10..);
++    /// y.get(..100);
++    /// ```
++    pub INDEXING_SLICING,
++    restriction,
++    "indexing/slicing usage"
++}
++
++declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IndexingSlicing {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if let ExprKind::Index(ref array, ref index) = &expr.kind {
++            let ty = cx.tables.expr_ty(array);
++            if let Some(range) = higher::range(cx, index) {
++                // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
++                if let ty::Array(_, s) = ty.kind {
++                    let size: u128 = if let Some(size) = s.try_eval_usize(cx.tcx, cx.param_env) {
++                        size.into()
++                    } else {
++                        return;
++                    };
++
++                    let const_range = to_const_range(cx, range, size);
++
++                    if let (Some(start), _) = const_range {
++                        if start > size {
++                            span_lint(
++                                cx,
++                                OUT_OF_BOUNDS_INDEXING,
++                                range.start.map_or(expr.span, |start| start.span),
++                                "range is out of bounds",
++                            );
++                            return;
++                        }
++                    }
++
++                    if let (_, Some(end)) = const_range {
++                        if end > size {
++                            span_lint(
++                                cx,
++                                OUT_OF_BOUNDS_INDEXING,
++                                range.end.map_or(expr.span, |end| end.span),
++                                "range is out of bounds",
++                            );
++                            return;
++                        }
++                    }
++
++                    if let (Some(_), Some(_)) = const_range {
++                        // early return because both start and end are constants
++                        // and we have proven above that they are in bounds
++                        return;
++                    }
++                }
++
++                let help_msg = match (range.start, range.end) {
++                    (None, Some(_)) => "Consider using `.get(..n)`or `.get_mut(..n)` instead",
++                    (Some(_), None) => "Consider using `.get(n..)` or .get_mut(n..)` instead",
++                    (Some(_), Some(_)) => "Consider using `.get(n..m)` or `.get_mut(n..m)` instead",
++                    (None, None) => return, // [..] is ok.
++                };
++
++                span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic.", None, help_msg);
++            } else {
++                // Catchall non-range index, i.e., [n] or [n << m]
++                if let ty::Array(..) = ty.kind {
++                    // Index is a constant uint.
++                    if let Some(..) = constant(cx, cx.tables, index) {
++                        // Let rustc's `const_err` lint handle constant `usize` indexing on arrays.
++                        return;
++                    }
++                }
++
++                span_lint_and_help(
++                    cx,
++                    INDEXING_SLICING,
++                    expr.span,
++                    "indexing may panic.",
++                    None,
++                    "Consider using `.get(n)` or `.get_mut(n)` instead",
++                );
++            }
++        }
++    }
++}
++
++/// Returns a tuple of options with the start and end (exclusive) values of
++/// the range. If the start or end is not constant, None is returned.
++fn to_const_range<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    range: higher::Range<'_>,
++    array_size: u128,
++) -> (Option<u128>, Option<u128>) {
++    let s = range.start.map(|expr| constant(cx, cx.tables, expr).map(|(c, _)| c));
++    let start = match s {
++        Some(Some(Constant::Int(x))) => Some(x),
++        Some(_) => None,
++        None => Some(0),
++    };
++
++    let e = range.end.map(|expr| constant(cx, cx.tables, expr).map(|(c, _)| c));
++    let end = match e {
++        Some(Some(Constant::Int(x))) => {
++            if range.limits == RangeLimits::Closed {
++                Some(x + 1)
++            } else {
++                Some(x)
++            }
++        },
++        Some(_) => None,
++        None => Some(array_size),
++    };
++
++    (start, end)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cd989c0ea6f67a0be9f872b89dd8a340def2db94
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,253 @@@
++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.
++    ///
++    /// **Why is this bad?** While there may be places where this is acceptable
++    /// (e.g., in event streams), in most cases this is simply an error.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```no_run
++    /// use std::iter;
++    ///
++    /// iter::repeat(1_u8).collect::<Vec<_>>();
++    /// ```
++    pub INFINITE_ITER,
++    correctness,
++    "infinite iteration"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for iteration that may be infinite.
++    ///
++    /// **Why is this bad?** While there may be places where this is acceptable
++    /// (e.g., in event streams), in most cases this is simply an error.
++    ///
++    /// **Known problems:** The code may have a condition to stop iteration, but
++    /// this lint is not clever enough to analyze it.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let infinite_iter = 0..;
++    /// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
++    /// ```
++    pub MAYBE_INFINITE_ITER,
++    pedantic,
++    "possible infinite iteration"
++}
++
++declare_lint_pass!(InfiniteIter => [INFINITE_ITER, MAYBE_INFINITE_ITER]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InfiniteIter {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        let (lint, msg) = match complete_infinite_iter(cx, expr) {
++            Infinite => (INFINITE_ITER, "infinite iteration detected"),
++            MaybeInfinite => (MAYBE_INFINITE_ITER, "possible infinite iteration detected"),
++            Finite => {
++                return;
++            },
++        };
++        span_lint(cx, lint, expr.span, msg)
++    }
++}
++
++#[derive(Copy, Clone, Debug, PartialEq, Eq)]
++enum Finiteness {
++    Infinite,
++    MaybeInfinite,
++    Finite,
++}
++
++use self::Finiteness::{Finite, Infinite, MaybeInfinite};
++
++impl Finiteness {
++    #[must_use]
++    fn and(self, b: Self) -> Self {
++        match (self, b) {
++            (Finite, _) | (_, Finite) => Finite,
++            (MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite,
++            _ => Infinite,
++        }
++    }
++
++    #[must_use]
++    fn or(self, b: Self) -> Self {
++        match (self, b) {
++            (Infinite, _) | (_, Infinite) => Infinite,
++            (MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite,
++            _ => Finite,
++        }
++    }
++}
++
++impl From<bool> for Finiteness {
++    #[must_use]
++    fn from(b: bool) -> Self {
++        if b {
++            Infinite
++        } else {
++            Finite
++        }
++    }
++}
++
++/// This tells us what to look for to know if the iterator returned by
++/// this method is infinite
++#[derive(Copy, Clone)]
++enum Heuristic {
++    /// infinite no matter what
++    Always,
++    /// infinite if the first argument is
++    First,
++    /// infinite if any of the supplied arguments is
++    Any,
++    /// infinite if all of the supplied arguments are
++    All,
++}
++
++use self::Heuristic::{All, Always, Any, First};
++
++/// a slice of (method name, number of args, heuristic, bounds) tuples
++/// that will be used to determine whether the method in question
++/// returns an infinite or possibly infinite iterator. The finiteness
++/// is an upper bound, e.g., some methods can return a possibly
++/// infinite iterator at worst, e.g., `take_while`.
++const HEURISTICS: [(&str, usize, Heuristic, Finiteness); 19] = [
++    ("zip", 2, All, Infinite),
++    ("chain", 2, Any, Infinite),
++    ("cycle", 1, Always, Infinite),
++    ("map", 2, First, Infinite),
++    ("by_ref", 1, First, Infinite),
++    ("cloned", 1, First, Infinite),
++    ("rev", 1, First, Infinite),
++    ("inspect", 1, First, Infinite),
++    ("enumerate", 1, First, Infinite),
++    ("peekable", 2, First, Infinite),
++    ("fuse", 1, First, Infinite),
++    ("skip", 2, First, Infinite),
++    ("skip_while", 1, First, Infinite),
++    ("filter", 2, First, Infinite),
++    ("filter_map", 2, First, Infinite),
++    ("flat_map", 2, First, Infinite),
++    ("unzip", 1, First, Infinite),
++    ("take_while", 2, First, MaybeInfinite),
++    ("scan", 3, First, MaybeInfinite),
++];
++
++fn is_infinite(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Finiteness {
++    match expr.kind {
++        ExprKind::MethodCall(ref method, _, ref args) => {
++            for &(name, len, heuristic, cap) in &HEURISTICS {
++                if method.ident.name.as_str() == name && args.len() == len {
++                    return (match heuristic {
++                        Always => Infinite,
++                        First => is_infinite(cx, &args[0]),
++                        Any => is_infinite(cx, &args[0]).or(is_infinite(cx, &args[1])),
++                        All => is_infinite(cx, &args[0]).and(is_infinite(cx, &args[1])),
++                    })
++                    .and(cap);
++                }
++            }
++            if method.ident.name == sym!(flat_map) && args.len() == 2 {
++                if let ExprKind::Closure(_, _, body_id, _, _) = args[1].kind {
++                    let body = cx.tcx.hir().body(body_id);
++                    return is_infinite(cx, &body.value);
++                }
++            }
++            Finite
++        },
++        ExprKind::Block(ref block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
++        ExprKind::Box(ref e) | ExprKind::AddrOf(BorrowKind::Ref, _, ref e) => is_infinite(cx, e),
++        ExprKind::Call(ref path, _) => {
++            if let ExprKind::Path(ref qpath) = path.kind {
++                match_qpath(qpath, &paths::REPEAT).into()
++            } else {
++                Finite
++            }
++        },
++        ExprKind::Struct(..) => higher::range(cx, expr).map_or(false, |r| r.end.is_none()).into(),
++        _ => Finite,
++    }
++}
++
++/// the names and argument lengths of methods that *may* exhaust their
++/// iterators
++const POSSIBLY_COMPLETING_METHODS: [(&str, usize); 6] = [
++    ("find", 2),
++    ("rfind", 2),
++    ("position", 2),
++    ("rposition", 2),
++    ("any", 2),
++    ("all", 2),
++];
++
++/// the names and argument lengths of methods that *always* exhaust
++/// their iterators
++const COMPLETING_METHODS: [(&str, usize); 12] = [
++    ("count", 1),
++    ("fold", 3),
++    ("for_each", 2),
++    ("partition", 2),
++    ("max", 1),
++    ("max_by", 2),
++    ("max_by_key", 2),
++    ("min", 1),
++    ("min_by", 2),
++    ("min_by_key", 2),
++    ("sum", 1),
++    ("product", 1),
++];
++
++/// the paths of types that are known to be infinitely allocating
++const INFINITE_COLLECTORS: [&[&str]; 8] = [
++    &paths::BINARY_HEAP,
++    &paths::BTREEMAP,
++    &paths::BTREESET,
++    &paths::HASHMAP,
++    &paths::HASHSET,
++    &paths::LINKED_LIST,
++    &paths::VEC,
++    &paths::VEC_DEQUE,
++];
++
++fn complete_infinite_iter(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Finiteness {
++    match expr.kind {
++        ExprKind::MethodCall(ref method, _, ref args) => {
++            for &(name, len) in &COMPLETING_METHODS {
++                if method.ident.name.as_str() == name && args.len() == len {
++                    return is_infinite(cx, &args[0]);
++                }
++            }
++            for &(name, len) in &POSSIBLY_COMPLETING_METHODS {
++                if method.ident.name.as_str() == name && args.len() == len {
++                    return MaybeInfinite.and(is_infinite(cx, &args[0]));
++                }
++            }
++            if method.ident.name == sym!(last) && args.len() == 1 {
++                let not_double_ended = get_trait_def_id(cx, &paths::DOUBLE_ENDED_ITERATOR)
++                    .map_or(false, |id| !implements_trait(cx, cx.tables.expr_ty(&args[0]), id, &[]));
++                if not_double_ended {
++                    return is_infinite(cx, &args[0]);
++                }
++            } else if method.ident.name == sym!(collect) {
++                let ty = cx.tables.expr_ty(expr);
++                if INFINITE_COLLECTORS.iter().any(|path| match_type(cx, ty, path)) {
++                    return is_infinite(cx, &args[0]);
++                }
++            }
++        },
++        ExprKind::Binary(op, ref l, ref r) => {
++            if op.node.is_comparison() {
++                return is_infinite(cx, l).and(is_infinite(cx, r)).and(MaybeInfinite);
++            }
++        }, // TODO: ExprKind::Loop + Match
++        _ => (),
++    }
++    Finite
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7e2975ac2ae907fe0ab7a4c8ac2111a3450edb46
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,94 @@@
++//! lint on inherent implementations
++
++use crate::utils::{in_macro, span_lint_and_then};
++use rustc_data_structures::fx::FxHashMap;
++use rustc_hir::{def_id, Crate, Item, ItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for multiple inherent implementations of a struct
++    ///
++    /// **Why is this bad?** Splitting the implementation of a type makes the code harder to navigate.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// struct X;
++    /// impl X {
++    ///     fn one() {}
++    /// }
++    /// impl X {
++    ///     fn other() {}
++    /// }
++    /// ```
++    ///
++    /// Could be written:
++    ///
++    /// ```rust
++    /// struct X;
++    /// impl X {
++    ///     fn one() {}
++    ///     fn other() {}
++    /// }
++    /// ```
++    pub MULTIPLE_INHERENT_IMPL,
++    restriction,
++    "Multiple inherent impl that could be grouped"
++}
++
++#[allow(clippy::module_name_repetitions)]
++#[derive(Default)]
++pub struct MultipleInherentImpl {
++    impls: FxHashMap<def_id::DefId, Span>,
++}
++
++impl_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MultipleInherentImpl {
++    fn check_item(&mut self, _: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++        if let ItemKind::Impl {
++            ref generics,
++            of_trait: None,
++            ..
++        } = item.kind
++        {
++            // Remember for each inherent implementation encoutered its span and generics
++            // but filter out implementations that have generic params (type or lifetime)
++            // or are derived from a macro
++            if !in_macro(item.span) && generics.params.is_empty() {
++                self.impls.insert(item.hir_id.owner.to_def_id(), item.span);
++            }
++        }
++    }
++
++    fn check_crate_post(&mut self, cx: &LateContext<'a, 'tcx>, krate: &'tcx Crate<'_>) {
++        if let Some(item) = krate.items.values().next() {
++            // Retrieve all inherent implementations from the crate, grouped by type
++            for impls in cx
++                .tcx
++                .crate_inherent_impls(item.hir_id.owner.to_def_id().krate)
++                .inherent_impls
++                .values()
++            {
++                // Filter out implementations that have generic params (type or lifetime)
++                let mut impl_spans = impls.iter().filter_map(|impl_def| self.impls.get(impl_def));
++                if let Some(initial_span) = impl_spans.next() {
++                    impl_spans.for_each(|additional_span| {
++                        span_lint_and_then(
++                            cx,
++                            MULTIPLE_INHERENT_IMPL,
++                            *additional_span,
++                            "Multiple implementations of this structure",
++                            |diag| {
++                                diag.span_note(*initial_span, "First implementation here");
++                            },
++                        )
++                    })
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..289628a2752af81ded7e8fdbe848b219888f0b0b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,156 @@@
++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 crate::utils::{
++    get_trait_def_id, implements_trait, is_type_diagnostic_item, paths, return_ty, span_lint_and_help,
++    trait_ref_of_method, walk_ptrs_ty,
++};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`.
++    ///
++    /// **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred.
++    ///
++    /// **Known problems:** None
++    ///
++    /// ** Example:**
++    ///
++    /// ```rust
++    /// // Bad
++    /// pub struct A;
++    ///
++    /// impl A {
++    ///     pub fn to_string(&self) -> String {
++    ///         "I am A".to_string()
++    ///     }
++    /// }
++    /// ```
++    ///
++    /// ```rust
++    /// // Good
++    /// use std::fmt;
++    ///
++    /// pub struct A;
++    ///
++    /// impl fmt::Display for A {
++    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++    ///         write!(f, "I am A")
++    ///     }
++    /// }
++    /// ```
++    pub INHERENT_TO_STRING,
++    style,
++    "type implements inherent method `to_string()`, but should instead implement the `Display` trait"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait.
++    ///
++    /// **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`.
++    ///
++    /// **Known problems:** None
++    ///
++    /// ** Example:**
++    ///
++    /// ```rust
++    /// // Bad
++    /// use std::fmt;
++    ///
++    /// pub struct A;
++    ///
++    /// impl A {
++    ///     pub fn to_string(&self) -> String {
++    ///         "I am A".to_string()
++    ///     }
++    /// }
++    ///
++    /// impl fmt::Display for A {
++    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++    ///         write!(f, "I am A, too")
++    ///     }
++    /// }
++    /// ```
++    ///
++    /// ```rust
++    /// // Good
++    /// use std::fmt;
++    ///
++    /// pub struct A;
++    ///
++    /// impl fmt::Display for A {
++    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++    ///         write!(f, "I am A")
++    ///     }
++    /// }
++    /// ```
++    pub INHERENT_TO_STRING_SHADOW_DISPLAY,
++    correctness,
++    "type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait"
++}
++
++declare_lint_pass!(InherentToString => [INHERENT_TO_STRING, INHERENT_TO_STRING_SHADOW_DISPLAY]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InherentToString {
++    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx ImplItem<'_>) {
++        if impl_item.span.from_expansion() {
++            return;
++        }
++
++        if_chain! {
++            // Check if item is a method, called to_string and has a parameter 'self'
++            if let ImplItemKind::Fn(ref signature, _) = impl_item.kind;
++            if impl_item.ident.name.as_str() == "to_string";
++            let decl = &signature.decl;
++            if decl.implicit_self.has_implicit_self();
++            if decl.inputs.len() == 1;
++
++            // Check if return type is String
++            if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type));
++
++            // Filters instances of to_string which are required by a trait
++            if trait_ref_of_method(cx, impl_item.hir_id).is_none();
++
++            then {
++                show_lint(cx, impl_item);
++            }
++        }
++    }
++}
++
++fn show_lint(cx: &LateContext<'_, '_>, item: &ImplItem<'_>) {
++    let display_trait_id = get_trait_def_id(cx, &paths::DISPLAY_TRAIT).expect("Failed to get trait ID of `Display`!");
++
++    // Get the real type of 'self'
++    let fn_def_id = cx.tcx.hir().local_def_id(item.hir_id);
++    let self_type = cx.tcx.fn_sig(fn_def_id).input(0);
++    let self_type = walk_ptrs_ty(self_type.skip_binder());
++
++    // Emit either a warning or an error
++    if implements_trait(cx, self_type, display_trait_id, &[]) {
++        span_lint_and_help(
++            cx,
++            INHERENT_TO_STRING_SHADOW_DISPLAY,
++            item.span,
++            &format!(
++                "type `{}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`",
++                self_type.to_string()
++            ),
++            None,
++            &format!("remove the inherent method from type `{}`", self_type.to_string())
++        );
++    } else {
++        span_lint_and_help(
++            cx,
++            INHERENT_TO_STRING,
++            item.span,
++            &format!(
++                "implementation of inherent method `to_string(&self) -> String` for type `{}`",
++                self_type.to_string()
++            ),
++            None,
++            &format!("implement trait `Display` for type `{}` instead", self_type.to_string()),
++        );
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1ebfb3c8162a1db111635a5428ad895c795798b9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,57 @@@
++//! checks for `#[inline]` on trait methods without bodies
++
++use crate::utils::span_lint_and_then;
++use crate::utils::sugg::DiagnosticBuilderExt;
++use rustc_ast::ast::{Attribute, Name};
++use rustc_errors::Applicability;
++use rustc_hir::{TraitFn, TraitItem, TraitItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `#[inline]` on trait methods without bodies
++    ///
++    /// **Why is this bad?** Only implementations of trait methods may be inlined.
++    /// The inline attribute is ignored for trait methods without bodies.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// trait Animal {
++    ///     #[inline]
++    ///     fn name(&self) -> &'static str;
++    /// }
++    /// ```
++    pub INLINE_FN_WITHOUT_BODY,
++    correctness,
++    "use of `#[inline]` on trait methods without bodies"
++}
++
++declare_lint_pass!(InlineFnWithoutBody => [INLINE_FN_WITHOUT_BODY]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InlineFnWithoutBody {
++    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) {
++        if let TraitItemKind::Fn(_, TraitFn::Required(_)) = item.kind {
++            check_attrs(cx, item.ident.name, &item.attrs);
++        }
++    }
++}
++
++fn check_attrs(cx: &LateContext<'_, '_>, name: Name, attrs: &[Attribute]) {
++    for attr in attrs {
++        if !attr.check_name(sym!(inline)) {
++            continue;
++        }
++
++        span_lint_and_then(
++            cx,
++            INLINE_FN_WITHOUT_BODY,
++            attr.span,
++            &format!("use of `#[inline]` on trait method `{}` which has no body", name),
++            |diag| {
++                diag.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable);
++            },
++        );
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d5dbd495680b2fca928e3a69131e6243127b6899
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,172 @@@
++//! lint on blocks unnecessarily using >= with a + 1 or - 1
++
++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
++    ///
++    ///
++    /// **Why is this bad?** Readability -- better to use `> y` instead of `>= y + 1`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let x = 1;
++    /// # let y = 1;
++    /// if x >= y + 1 {}
++    /// ```
++    ///
++    /// Could be written as:
++    ///
++    /// ```rust
++    /// # let x = 1;
++    /// # let y = 1;
++    /// if x > y {}
++    /// ```
++    pub INT_PLUS_ONE,
++    complexity,
++    "instead of using `x >= y + 1`, use `x > y`"
++}
++
++declare_lint_pass!(IntPlusOne => [INT_PLUS_ONE]);
++
++// cases:
++// BinOpKind::Ge
++// x >= y + 1
++// x - 1 >= y
++//
++// BinOpKind::Le
++// x + 1 <= y
++// x <= y - 1
++
++#[derive(Copy, Clone)]
++enum Side {
++    LHS,
++    RHS,
++}
++
++impl IntPlusOne {
++    #[allow(clippy::cast_sign_loss)]
++    fn check_lit(lit: &Lit, target_value: i128) -> bool {
++        if let LitKind::Int(value, ..) = lit.kind {
++            return value == (target_value as u128);
++        }
++        false
++    }
++
++    fn check_binop(cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr) -> Option<String> {
++        match (binop, &lhs.kind, &rhs.kind) {
++            // case where `x - 1 >= ...` or `-1 + x >= ...`
++            (BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => {
++                match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) {
++                    // `-1 + x`
++                    (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => {
++                        Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS)
++                    },
++                    // `x - 1`
++                    (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
++                        Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS)
++                    },
++                    _ => None,
++                }
++            },
++            // case where `... >= y + 1` or `... >= 1 + y`
++            (BinOpKind::Ge, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs))
++                if rhskind.node == BinOpKind::Add =>
++            {
++                match (&rhslhs.kind, &rhsrhs.kind) {
++                    // `y + 1` and `1 + y`
++                    (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => {
++                        Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS)
++                    },
++                    (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
++                        Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS)
++                    },
++                    _ => None,
++                }
++            }
++            // case where `x + 1 <= ...` or `1 + x <= ...`
++            (BinOpKind::Le, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _)
++                if lhskind.node == BinOpKind::Add =>
++            {
++                match (&lhslhs.kind, &lhsrhs.kind) {
++                    // `1 + x` and `x + 1`
++                    (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => {
++                        Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS)
++                    },
++                    (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
++                        Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS)
++                    },
++                    _ => None,
++                }
++            }
++            // case where `... >= y - 1` or `... >= -1 + y`
++            (BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => {
++                match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) {
++                    // `-1 + y`
++                    (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => {
++                        Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS)
++                    },
++                    // `y - 1`
++                    (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
++                        Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS)
++                    },
++                    _ => None,
++                }
++            },
++            _ => None,
++        }
++    }
++
++    fn generate_recommendation(
++        cx: &EarlyContext<'_>,
++        binop: BinOpKind,
++        node: &Expr,
++        other_side: &Expr,
++        side: Side,
++    ) -> Option<String> {
++        let binop_string = match binop {
++            BinOpKind::Ge => ">",
++            BinOpKind::Le => "<",
++            _ => return None,
++        };
++        if let Some(snippet) = snippet_opt(cx, node.span) {
++            if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) {
++                let rec = match side {
++                    Side::LHS => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)),
++                    Side::RHS => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)),
++                };
++                return rec;
++            }
++        }
++        None
++    }
++
++    fn emit_warning(cx: &EarlyContext<'_>, block: &Expr, recommendation: String) {
++        span_lint_and_sugg(
++            cx,
++            INT_PLUS_ONE,
++            block.span,
++            "Unnecessary `>= y + 1` or `x - 1 >=`",
++            "change it to",
++            recommendation,
++            Applicability::MachineApplicable, // snippet
++        );
++    }
++}
++
++impl EarlyLintPass for IntPlusOne {
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) {
++        if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind {
++            if let Some(ref rec) = Self::check_binop(cx, kind.node, lhs, rhs) {
++                Self::emit_warning(cx, item, rec.clone());
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fe34d33fe652c2cf7f3ac258be6dc80840aa25b8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++use crate::utils::span_lint_and_help;
++use if_chain::if_chain;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for division of integers
++    ///
++    /// **Why is this bad?** When outside of some very specific algorithms,
++    /// integer division is very often a mistake because it discards the
++    /// remainder.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// fn main() {
++    ///     let x = 3 / 2;
++    ///     println!("{}", x);
++    /// }
++    /// ```
++    pub INTEGER_DIVISION,
++    restriction,
++    "integer division may cause loss of precision"
++}
++
++declare_lint_pass!(IntegerDivision => [INTEGER_DIVISION]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IntegerDivision {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if is_integer_division(cx, expr) {
++            span_lint_and_help(
++                cx,
++                INTEGER_DIVISION,
++                expr.span,
++                "integer division",
++                None,
++                "division of integers may cause loss of precision. consider using floats.",
++            );
++        }
++    }
++}
++
++fn is_integer_division<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) -> bool {
++    if_chain! {
++        if let hir::ExprKind::Binary(binop, left, right) = &expr.kind;
++        if let hir::BinOpKind::Div = &binop.node;
++        then {
++            let (left_ty, right_ty) = (cx.tables.expr_ty(left), cx.tables.expr_ty(right));
++            return left_ty.is_integral() && right_ty.is_integral();
++        }
++    }
++
++    false
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e7062b7c16bb9b89905e8605d0471e436d82b31e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,71 @@@
++//! lint when items are used after statements
++
++use crate::utils::span_lint;
++use rustc_ast::ast::{Block, ItemKind, StmtKind};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for items declared after some statement in a block.
++    ///
++    /// **Why is this bad?** Items live for the entire scope they are declared
++    /// in. But statements are processed in order. This might cause confusion as
++    /// it's hard to figure out which item is meant in a statement.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// fn foo() {
++    ///     println!("cake");
++    /// }
++    ///
++    /// fn main() {
++    ///     foo(); // prints "foo"
++    ///     fn foo() {
++    ///         println!("foo");
++    ///     }
++    ///     foo(); // prints "foo"
++    /// }
++    /// ```
++    pub ITEMS_AFTER_STATEMENTS,
++    pedantic,
++    "blocks where an item comes after a statement"
++}
++
++declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]);
++
++impl EarlyLintPass for ItemsAfterStatements {
++    fn check_block(&mut self, cx: &EarlyContext<'_>, item: &Block) {
++        if item.span.from_expansion() {
++            return;
++        }
++
++        // skip initial items
++        let stmts = item
++            .stmts
++            .iter()
++            .map(|stmt| &stmt.kind)
++            .skip_while(|s| matches!(**s, StmtKind::Item(..)));
++
++        // lint on all further items
++        for stmt in stmts {
++            if let StmtKind::Item(ref it) = *stmt {
++                if it.span.from_expansion() {
++                    return;
++                }
++                if let ItemKind::MacroDef(..) = it.kind {
++                    // do not lint `macro_rules`, but continue processing further statements
++                    continue;
++                }
++                span_lint(
++                    cx,
++                    ITEMS_AFTER_STATEMENTS,
++                    it.span,
++                    "adding items after statements is confusing, since items exist from the \
++                     start of the scope",
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c9e12fc535ec0ee3bd73228221679097e727285e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,85 @@@
++use crate::rustc_target::abi::LayoutOf;
++use crate::utils::span_lint_and_then;
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Item, ItemKind};
++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 rustc_span::{BytePos, Pos, Span};
++use rustc_typeck::hir_ty_to_ty;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for large `const` arrays that should
++    /// be defined as `static` instead.
++    ///
++    /// **Why is this bad?** Performance: const variables are inlined upon use.
++    /// Static items result in only one instance and has a fixed location in memory.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// // Bad
++    /// pub const a = [0u32; 1_000_000];
++    ///
++    /// // Good
++    /// pub static a = [0u32; 1_000_000];
++    /// ```
++    pub LARGE_CONST_ARRAYS,
++    perf,
++    "large non-scalar const array may cause performance overhead"
++}
++
++pub struct LargeConstArrays {
++    maximum_allowed_size: u64,
++}
++
++impl LargeConstArrays {
++    #[must_use]
++    pub fn new(maximum_allowed_size: u64) -> Self {
++        Self { maximum_allowed_size }
++    }
++}
++
++impl_lint_pass!(LargeConstArrays => [LARGE_CONST_ARRAYS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeConstArrays {
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++        if_chain! {
++            if !item.span.from_expansion();
++            if let ItemKind::Const(hir_ty, _) = &item.kind;
++            let ty = hir_ty_to_ty(cx.tcx, hir_ty);
++            if let ty::Array(element_type, cst) = ty.kind;
++            if let ConstKind::Value(val) = cst.val;
++            if let ConstValue::Scalar(element_count) = val;
++            if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx);
++            if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes());
++            if self.maximum_allowed_size < element_count * element_size;
++
++            then {
++                let hi_pos = item.ident.span.lo() - BytePos::from_usize(1);
++                let sugg_span = Span::new(
++                    hi_pos - BytePos::from_usize("const".len()),
++                    hi_pos,
++                    item.span.ctxt(),
++                );
++                span_lint_and_then(
++                    cx,
++                    LARGE_CONST_ARRAYS,
++                    item.span,
++                    "large array defined as const",
++                    |diag| {
++                        diag.span_suggestion(
++                            sugg_span,
++                            "make this a static item",
++                            "static".to_string(),
++                            Applicability::MachineApplicable,
++                        );
++                    }
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5bc3234e3252f2a3ca8ef5e323cd40da30f76096
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,134 @@@
++//! lint when there is a large size difference between variants on an enum
++
++use crate::utils::{snippet_opt, span_lint_and_then};
++use rustc_errors::Applicability;
++use rustc_hir::{Item, ItemKind, VariantData};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_target::abi::LayoutOf;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for large size differences between variants on
++    /// `enum`s.
++    ///
++    /// **Why is this bad?** Enum size is bounded by the largest variant. Having a
++    /// large variant can penalize the memory layout of that enum.
++    ///
++    /// **Known problems:** This lint obviously cannot take the distribution of
++    /// variants in your running program into account. It is possible that the
++    /// smaller variants make up less than 1% of all instances, in which case
++    /// the overhead is negligible and the boxing is counter-productive. Always
++    /// measure the change this lint suggests.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// // Bad
++    /// enum Test {
++    ///     A(i32),
++    ///     B([i32; 8000]),
++    /// }
++    ///
++    /// // Possibly better
++    /// enum Test2 {
++    ///     A(i32),
++    ///     B(Box<[i32; 8000]>),
++    /// }
++    /// ```
++    pub LARGE_ENUM_VARIANT,
++    perf,
++    "large size difference between variants on an enum"
++}
++
++#[derive(Copy, Clone)]
++pub struct LargeEnumVariant {
++    maximum_size_difference_allowed: u64,
++}
++
++impl LargeEnumVariant {
++    #[must_use]
++    pub fn new(maximum_size_difference_allowed: u64) -> Self {
++        Self {
++            maximum_size_difference_allowed,
++        }
++    }
++}
++
++impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeEnumVariant {
++    fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) {
++        let did = cx.tcx.hir().local_def_id(item.hir_id);
++        if let ItemKind::Enum(ref def, _) = item.kind {
++            let ty = cx.tcx.type_of(did);
++            let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
++
++            let mut largest_variant: Option<(_, _)> = None;
++            let mut second_variant: Option<(_, _)> = None;
++
++            for (i, variant) in adt.variants.iter().enumerate() {
++                let size: u64 = variant
++                    .fields
++                    .iter()
++                    .filter_map(|f| {
++                        let ty = cx.tcx.type_of(f.did);
++                        // don't count generics by filtering out everything
++                        // that does not have a layout
++                        cx.layout_of(ty).ok().map(|l| l.size.bytes())
++                    })
++                    .sum();
++
++                let grouped = (size, (i, variant));
++
++                if grouped.0 >= largest_variant.map_or(0, |x| x.0) {
++                    second_variant = largest_variant;
++                    largest_variant = Some(grouped);
++                }
++            }
++
++            if let (Some(largest), Some(second)) = (largest_variant, second_variant) {
++                let difference = largest.0 - second.0;
++
++                if difference > self.maximum_size_difference_allowed {
++                    let (i, variant) = largest.1;
++
++                    let help_text = "consider boxing the large fields to reduce the total size of the enum";
++                    span_lint_and_then(
++                        cx,
++                        LARGE_ENUM_VARIANT,
++                        def.variants[i].span,
++                        "large size difference between variants",
++                        |diag| {
++                            diag.span_label(
++                                def.variants[(largest.1).0].span,
++                                &format!("this variant is {} bytes", largest.0),
++                            );
++                            diag.span_note(
++                                def.variants[(second.1).0].span,
++                                &format!("and the second-largest variant is {} bytes:", second.0),
++                            );
++                            if variant.fields.len() == 1 {
++                                let span = match def.variants[i].data {
++                                    VariantData::Struct(ref fields, ..) | VariantData::Tuple(ref fields, ..) => {
++                                        fields[0].ty.span
++                                    },
++                                    VariantData::Unit(..) => unreachable!(),
++                                };
++                                if let Some(snip) = snippet_opt(cx, span) {
++                                    diag.span_suggestion(
++                                        span,
++                                        help_text,
++                                        format!("Box<{}>", snip),
++                                        Applicability::MaybeIncorrect,
++                                    );
++                                    return;
++                                }
++                            }
++                            diag.span_help(def.variants[i].span, help_text);
++                        },
++                    );
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..deb57db1678965e946466bd042adc1d108e6e0e6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,69 @@@
++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.
++    ///
++    /// **Why is this bad?** Large local arrays may cause stack overflow.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// let a = [0u32; 1_000_000];
++    /// ```
++    pub LARGE_STACK_ARRAYS,
++    pedantic,
++    "allocating large arrays on stack may cause stack overflow"
++}
++
++pub struct LargeStackArrays {
++    maximum_allowed_size: u64,
++}
++
++impl LargeStackArrays {
++    #[must_use]
++    pub fn new(maximum_allowed_size: u64) -> Self {
++        Self { maximum_allowed_size }
++    }
++}
++
++impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeStackArrays {
++    fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++        if_chain! {
++            if let ExprKind::Repeat(_, _) = expr.kind;
++            if let ty::Array(element_type, cst) = cx.tables.expr_ty(expr).kind;
++            if let ConstKind::Value(val) = cst.val;
++            if let ConstValue::Scalar(element_count) = val;
++            if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx);
++            if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes());
++            if self.maximum_allowed_size < element_count * element_size;
++            then {
++                span_lint_and_help(
++                    cx,
++                    LARGE_STACK_ARRAYS,
++                    expr.span,
++                    &format!(
++                        "allocating a local array larger than {} bytes",
++                        self.maximum_allowed_size
++                    ),
++                    None,
++                    &format!(
++                        "consider allocating on the heap with `vec!{}.into_boxed_slice()`",
++                        snippet(cx, expr.span, "[...]")
++                    ),
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1d86ca9696f2bb778431b9dfdec0736a4df2f1d5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,304 @@@
++use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty};
++use rustc_ast::ast::{LitKind, Name};
++use rustc_data_structures::fx::FxHashSet;
++use rustc_errors::Applicability;
++use rustc_hir::def_id::DefId;
++use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, ImplItemRef, Item, ItemKind, TraitItemRef};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::{Span, Spanned};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for getting the length of something via `.len()`
++    /// just to compare to zero, and suggests using `.is_empty()` where applicable.
++    ///
++    /// **Why is this bad?** Some structures can answer `.is_empty()` much faster
++    /// than calculating their length. So it is good to get into the habit of using
++    /// `.is_empty()`, and having it is cheap.
++    /// Besides, it makes the intent clearer than a manual comparison in some contexts.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// if x.len() == 0 {
++    ///     ..
++    /// }
++    /// if y.len() != 0 {
++    ///     ..
++    /// }
++    /// ```
++    /// instead use
++    /// ```ignore
++    /// if x.is_empty() {
++    ///     ..
++    /// }
++    /// if !y.is_empty() {
++    ///     ..
++    /// }
++    /// ```
++    pub LEN_ZERO,
++    style,
++    "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for items that implement `.len()` but not
++    /// `.is_empty()`.
++    ///
++    /// **Why is this bad?** It is good custom to have both methods, because for
++    /// some data structures, asking about the length will be a costly operation,
++    /// whereas `.is_empty()` can usually answer in constant time. Also it used to
++    /// lead to false positives on the [`len_zero`](#len_zero) lint – currently that
++    /// lint will ignore such entities.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// impl X {
++    ///     pub fn len(&self) -> usize {
++    ///         ..
++    ///     }
++    /// }
++    /// ```
++    pub LEN_WITHOUT_IS_EMPTY,
++    style,
++    "traits or impls with a public `len` method but no corresponding `is_empty` method"
++}
++
++declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LenZero {
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++        if item.span.from_expansion() {
++            return;
++        }
++
++        match item.kind {
++            ItemKind::Trait(_, _, _, _, ref trait_items) => check_trait_items(cx, item, trait_items),
++            ItemKind::Impl {
++                of_trait: None,
++                items: ref impl_items,
++                ..
++            } => check_impl_items(cx, item, impl_items),
++            _ => (),
++        }
++    }
++
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if expr.span.from_expansion() {
++            return;
++        }
++
++        if let ExprKind::Binary(Spanned { node: cmp, .. }, ref left, ref right) = expr.kind {
++            match cmp {
++                BinOpKind::Eq => {
++                    check_cmp(cx, expr.span, left, right, "", 0); // len == 0
++                    check_cmp(cx, expr.span, right, left, "", 0); // 0 == len
++                },
++                BinOpKind::Ne => {
++                    check_cmp(cx, expr.span, left, right, "!", 0); // len != 0
++                    check_cmp(cx, expr.span, right, left, "!", 0); // 0 != len
++                },
++                BinOpKind::Gt => {
++                    check_cmp(cx, expr.span, left, right, "!", 0); // len > 0
++                    check_cmp(cx, expr.span, right, left, "", 1); // 1 > len
++                },
++                BinOpKind::Lt => {
++                    check_cmp(cx, expr.span, left, right, "", 1); // len < 1
++                    check_cmp(cx, expr.span, right, left, "!", 0); // 0 < len
++                },
++                BinOpKind::Ge => check_cmp(cx, expr.span, left, right, "!", 1), // len >= 1
++                BinOpKind::Le => check_cmp(cx, expr.span, right, left, "!", 1), // 1 <= len
++                _ => (),
++            }
++        }
++    }
++}
++
++fn check_trait_items(cx: &LateContext<'_, '_>, visited_trait: &Item<'_>, trait_items: &[TraitItemRef]) {
++    fn is_named_self(cx: &LateContext<'_, '_>, item: &TraitItemRef, name: &str) -> bool {
++        item.ident.name.as_str() == name
++            && if let AssocItemKind::Fn { has_self } = item.kind {
++                has_self && {
++                    let did = cx.tcx.hir().local_def_id(item.id.hir_id);
++                    cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
++                }
++            } else {
++                false
++            }
++    }
++
++    // fill the set with current and super traits
++    fn fill_trait_set(traitt: DefId, set: &mut FxHashSet<DefId>, cx: &LateContext<'_, '_>) {
++        if set.insert(traitt) {
++            for supertrait in rustc_trait_selection::traits::supertrait_def_ids(cx.tcx, traitt) {
++                fill_trait_set(supertrait, set, cx);
++            }
++        }
++    }
++
++    if cx.access_levels.is_exported(visited_trait.hir_id) && trait_items.iter().any(|i| is_named_self(cx, i, "len")) {
++        let mut current_and_super_traits = FxHashSet::default();
++        let visited_trait_def_id = cx.tcx.hir().local_def_id(visited_trait.hir_id);
++        fill_trait_set(visited_trait_def_id.to_def_id(), &mut current_and_super_traits, cx);
++
++        let is_empty_method_found = current_and_super_traits
++            .iter()
++            .flat_map(|&i| cx.tcx.associated_items(i).in_definition_order())
++            .any(|i| {
++                i.kind == ty::AssocKind::Fn
++                    && i.fn_has_self_parameter
++                    && i.ident.name == sym!(is_empty)
++                    && cx.tcx.fn_sig(i.def_id).inputs().skip_binder().len() == 1
++            });
++
++        if !is_empty_method_found {
++            span_lint(
++                cx,
++                LEN_WITHOUT_IS_EMPTY,
++                visited_trait.span,
++                &format!(
++                    "trait `{}` has a `len` method but no (possibly inherited) `is_empty` method",
++                    visited_trait.ident.name
++                ),
++            );
++        }
++    }
++}
++
++fn check_impl_items(cx: &LateContext<'_, '_>, item: &Item<'_>, impl_items: &[ImplItemRef<'_>]) {
++    fn is_named_self(cx: &LateContext<'_, '_>, item: &ImplItemRef<'_>, name: &str) -> bool {
++        item.ident.name.as_str() == name
++            && if let AssocItemKind::Fn { has_self } = item.kind {
++                has_self && {
++                    let did = cx.tcx.hir().local_def_id(item.id.hir_id);
++                    cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
++                }
++            } else {
++                false
++            }
++    }
++
++    let is_empty = if let Some(is_empty) = impl_items.iter().find(|i| is_named_self(cx, i, "is_empty")) {
++        if cx.access_levels.is_exported(is_empty.id.hir_id) {
++            return;
++        } else {
++            "a private"
++        }
++    } else {
++        "no corresponding"
++    };
++
++    if let Some(i) = impl_items.iter().find(|i| is_named_self(cx, i, "len")) {
++        if cx.access_levels.is_exported(i.id.hir_id) {
++            let def_id = cx.tcx.hir().local_def_id(item.hir_id);
++            let ty = cx.tcx.type_of(def_id);
++
++            span_lint(
++                cx,
++                LEN_WITHOUT_IS_EMPTY,
++                item.span,
++                &format!(
++                    "item `{}` has a public `len` method but {} `is_empty` method",
++                    ty, is_empty
++                ),
++            );
++        }
++    }
++}
++
++fn check_cmp(cx: &LateContext<'_, '_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) {
++    if let (&ExprKind::MethodCall(ref method_path, _, ref args), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) {
++        // check if we are in an is_empty() method
++        if let Some(name) = get_item_name(cx, method) {
++            if name.as_str() == "is_empty" {
++                return;
++            }
++        }
++
++        check_len(cx, span, method_path.ident.name, args, &lit.node, op, compare_to)
++    }
++}
++
++fn check_len(
++    cx: &LateContext<'_, '_>,
++    span: Span,
++    method_name: Name,
++    args: &[Expr<'_>],
++    lit: &LitKind,
++    op: &str,
++    compare_to: u32,
++) {
++    if let LitKind::Int(lit, _) = *lit {
++        // check if length is compared to the specified number
++        if lit != u128::from(compare_to) {
++            return;
++        }
++
++        if method_name.as_str() == "len" && args.len() == 1 && has_is_empty(cx, &args[0]) {
++            let mut applicability = Applicability::MachineApplicable;
++            span_lint_and_sugg(
++                cx,
++                LEN_ZERO,
++                span,
++                &format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }),
++                &format!("using `{}is_empty` is clearer and more explicit", op),
++                format!(
++                    "{}{}.is_empty()",
++                    op,
++                    snippet_with_applicability(cx, args[0].span, "_", &mut applicability)
++                ),
++                applicability,
++            );
++        }
++    }
++}
++
++/// Checks if this type has an `is_empty` method.
++fn has_is_empty(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++    /// Gets an `AssocItem` and return true if it matches `is_empty(self)`.
++    fn is_is_empty(cx: &LateContext<'_, '_>, item: &ty::AssocItem) -> bool {
++        if let ty::AssocKind::Fn = item.kind {
++            if item.ident.name.as_str() == "is_empty" {
++                let sig = cx.tcx.fn_sig(item.def_id);
++                let ty = sig.skip_binder();
++                ty.inputs().len() == 1
++            } else {
++                false
++            }
++        } else {
++            false
++        }
++    }
++
++    /// Checks the inherent impl's items for an `is_empty(self)` method.
++    fn has_is_empty_impl(cx: &LateContext<'_, '_>, id: DefId) -> bool {
++        cx.tcx.inherent_impls(id).iter().any(|imp| {
++            cx.tcx
++                .associated_items(*imp)
++                .in_definition_order()
++                .any(|item| is_is_empty(cx, &item))
++        })
++    }
++
++    let ty = &walk_ptrs_ty(cx.tables.expr_ty(expr));
++    match ty.kind {
++        ty::Dynamic(ref tt, ..) => {
++            if let Some(principal) = tt.principal() {
++                cx.tcx
++                    .associated_items(principal.def_id())
++                    .in_definition_order()
++                    .any(|item| is_is_empty(cx, &item))
++            } else {
++                false
++            }
++        },
++        ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id),
++        ty::Adt(id, _) => has_is_empty_impl(cx, id.did),
++        ty::Array(..) | ty::Slice(..) | ty::Str => true,
++        _ => false,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..398a3103a037194ab5c1ffe557412244405d9cea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,205 @@@
++use crate::utils::{higher, qpath_res, snippet, span_lint_and_then};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::def::Res;
++use rustc_hir::intravisit;
++use rustc_hir::BindingAnnotation;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for variable declarations immediately followed by a
++    /// conditional affectation.
++    ///
++    /// **Why is this bad?** This is not idiomatic Rust.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// let foo;
++    ///
++    /// if bar() {
++    ///     foo = 42;
++    /// } else {
++    ///     foo = 0;
++    /// }
++    ///
++    /// let mut baz = None;
++    ///
++    /// if bar() {
++    ///     baz = Some(42);
++    /// }
++    /// ```
++    ///
++    /// should be written
++    ///
++    /// ```rust,ignore
++    /// let foo = if bar() {
++    ///     42
++    /// } else {
++    ///     0
++    /// };
++    ///
++    /// let baz = if bar() {
++    ///     Some(42)
++    /// } else {
++    ///     None
++    /// };
++    /// ```
++    pub USELESS_LET_IF_SEQ,
++    style,
++    "unidiomatic `let mut` declaration followed by initialization in `if`"
++}
++
++declare_lint_pass!(LetIfSeq => [USELESS_LET_IF_SEQ]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetIfSeq {
++    fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx hir::Block<'_>) {
++        let mut it = block.stmts.iter().peekable();
++        while let Some(stmt) = it.next() {
++            if_chain! {
++                if let Some(expr) = it.peek();
++                if let hir::StmtKind::Local(ref local) = stmt.kind;
++                if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind;
++                if let hir::StmtKind::Expr(ref if_) = expr.kind;
++                if let Some((ref cond, ref then, ref else_)) = higher::if_block(&if_);
++                if !used_in_expr(cx, canonical_id, cond);
++                if let hir::ExprKind::Block(ref then, _) = then.kind;
++                if let Some(value) = check_assign(cx, canonical_id, &*then);
++                if !used_in_expr(cx, canonical_id, value);
++                then {
++                    let span = stmt.span.to(if_.span);
++
++                    let has_interior_mutability = !cx.tables.node_type(canonical_id).is_freeze(
++                        cx.tcx,
++                        cx.param_env,
++                        span
++                    );
++                    if has_interior_mutability { return; }
++
++                    let (default_multi_stmts, default) = if let Some(ref else_) = *else_ {
++                        if let hir::ExprKind::Block(ref else_, _) = else_.kind {
++                            if let Some(default) = check_assign(cx, canonical_id, else_) {
++                                (else_.stmts.len() > 1, default)
++                            } else if let Some(ref default) = local.init {
++                                (true, &**default)
++                            } else {
++                                continue;
++                            }
++                        } else {
++                            continue;
++                        }
++                    } else if let Some(ref default) = local.init {
++                        (false, &**default)
++                    } else {
++                        continue;
++                    };
++
++                    let mutability = match mode {
++                        BindingAnnotation::RefMut | BindingAnnotation::Mutable => "<mut> ",
++                        _ => "",
++                    };
++
++                    // FIXME: this should not suggest `mut` if we can detect that the variable is not
++                    // use mutably after the `if`
++
++                    let sug = format!(
++                        "let {mut}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};",
++                        mut=mutability,
++                        name=ident.name,
++                        cond=snippet(cx, cond.span, "_"),
++                        then=if then.stmts.len() > 1 { " ..;" } else { "" },
++                        else=if default_multi_stmts { " ..;" } else { "" },
++                        value=snippet(cx, value.span, "<value>"),
++                        default=snippet(cx, default.span, "<default>"),
++                    );
++                    span_lint_and_then(cx,
++                                       USELESS_LET_IF_SEQ,
++                                       span,
++                                       "`if _ { .. } else { .. }` is an expression",
++                                       |diag| {
++                                           diag.span_suggestion(
++                                                span,
++                                                "it is more idiomatic to write",
++                                                sug,
++                                                Applicability::HasPlaceholders,
++                                            );
++                                           if !mutability.is_empty() {
++                                               diag.note("you might not need `mut` at all");
++                                           }
++                                       });
++                }
++            }
++        }
++    }
++}
++
++struct UsedVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    id: hir::HirId,
++    used: bool,
++}
++
++impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
++        if_chain! {
++            if let hir::ExprKind::Path(ref qpath) = expr.kind;
++            if let Res::Local(local_id) = qpath_res(self.cx, qpath, expr.hir_id);
++            if self.id == local_id;
++            then {
++                self.used = true;
++                return;
++            }
++        }
++        intravisit::walk_expr(self, expr);
++    }
++    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
++        intravisit::NestedVisitorMap::None
++    }
++}
++
++fn check_assign<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    decl: hir::HirId,
++    block: &'tcx hir::Block<'_>,
++) -> Option<&'tcx hir::Expr<'tcx>> {
++    if_chain! {
++        if block.expr.is_none();
++        if let Some(expr) = block.stmts.iter().last();
++        if let hir::StmtKind::Semi(ref expr) = expr.kind;
++        if let hir::ExprKind::Assign(ref var, ref value, _) = expr.kind;
++        if let hir::ExprKind::Path(ref qpath) = var.kind;
++        if let Res::Local(local_id) = qpath_res(cx, qpath, var.hir_id);
++        if decl == local_id;
++        then {
++            let mut v = UsedVisitor {
++                cx,
++                id: decl,
++                used: false,
++            };
++
++            for s in block.stmts.iter().take(block.stmts.len()-1) {
++                intravisit::walk_stmt(&mut v, s);
++
++                if v.used {
++                    return None;
++                }
++            }
++
++            return Some(value);
++        }
++    }
++
++    None
++}
++
++fn used_in_expr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> bool {
++    let mut v = UsedVisitor { cx, id, used: false };
++    intravisit::walk_expr(&mut v, expr);
++    v.used
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..710dec8d33fc9e085d83582857e7698fb99389a7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,119 @@@
++use if_chain::if_chain;
++use rustc_hir::{Local, PatKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::lint::in_external_macro;
++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]
++    ///
++    /// **Why is this bad?** It's better to explicitly
++    /// handle the value of a #[must_use] expr
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// fn f() -> Result<u32, u32> {
++    ///     Ok(0)
++    /// }
++    ///
++    /// let _ = f();
++    /// // is_ok() is marked #[must_use]
++    /// let _ = f().is_ok();
++    /// ```
++    pub LET_UNDERSCORE_MUST_USE,
++    restriction,
++    "non-binding let on a `#[must_use]` expression"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `let _ = sync_lock`
++    ///
++    /// **Why is this bad?** This statement immediately drops the lock instead of
++    /// extending it's lifetime to the end of the scope, which is often not intended.
++    /// To extend lock lifetime to the end of the scope, use an underscore-prefixed
++    /// name instead (i.e. _lock). If you want to explicitly drop the lock,
++    /// `std::mem::drop` conveys your intention better and is less error-prone.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// Bad:
++    /// ```rust,ignore
++    /// let _ = mutex.lock();
++    /// ```
++    ///
++    /// Good:
++    /// ```rust,ignore
++    /// let _lock = mutex.lock();
++    /// ```
++    pub LET_UNDERSCORE_LOCK,
++    correctness,
++    "non-binding let on a synchronization lock"
++}
++
++declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK]);
++
++const SYNC_GUARD_PATHS: [&[&str]; 3] = [
++    &paths::MUTEX_GUARD,
++    &paths::RWLOCK_READ_GUARD,
++    &paths::RWLOCK_WRITE_GUARD,
++];
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetUnderscore {
++    fn check_local(&mut self, cx: &LateContext<'_, '_>, local: &Local<'_>) {
++        if in_external_macro(cx.tcx.sess, local.span) {
++            return;
++        }
++
++        if_chain! {
++            if let PatKind::Wild = local.pat.kind;
++            if let Some(ref init) = local.init;
++            then {
++                let init_ty = cx.tables.expr_ty(init);
++                let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
++                    GenericArgKind::Type(inner_ty) => {
++                        SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path))
++                    },
++
++                    GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
++                });
++                if contains_sync_guard {
++                    span_lint_and_help(
++                        cx,
++                        LET_UNDERSCORE_LOCK,
++                        local.span,
++                        "non-binding let on a synchronization lock",
++                        None,
++                        "consider using an underscore-prefixed named \
++                            binding or dropping explicitly with `std::mem::drop`"
++                    )
++                } else if is_must_use_ty(cx, cx.tables.expr_ty(init)) {
++                    span_lint_and_help(
++                        cx,
++                        LET_UNDERSCORE_MUST_USE,
++                        local.span,
++                        "non-binding let on an expression with `#[must_use]` type",
++                        None,
++                        "consider explicitly using expression value"
++                    )
++                } else if is_must_use_func_call(cx, init) {
++                    span_lint_and_help(
++                        cx,
++                        LET_UNDERSCORE_MUST_USE,
++                        local.span,
++                        "non-binding let on a result of a `#[must_use]` function",
++                        None,
++                        "consider explicitly using function result"
++                    )
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c995be5edc25cacc01b05afd9072f0484f6e6c9b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1795 @@@
++// error-pattern:cargo-clippy
++
++#![feature(box_syntax)]
++#![feature(box_patterns)]
++#![feature(or_patterns)]
++#![feature(rustc_private)]
++#![feature(stmt_expr_attributes)]
++#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
++#![recursion_limit = "512"]
++#![warn(rust_2018_idioms, trivial_casts, trivial_numeric_casts)]
++#![deny(rustc::internal)]
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++#![feature(crate_visibility_modifier)]
++#![feature(concat_idents)]
++
++// FIXME: switch to something more ergonomic here, once available.
++// (Currently there is no way to opt into sysroot crates without `extern crate`.)
++#[allow(unused_extern_crates)]
++extern crate fmt_macros;
++#[allow(unused_extern_crates)]
++extern crate rustc_ast;
++#[allow(unused_extern_crates)]
++extern crate rustc_ast_pretty;
++#[allow(unused_extern_crates)]
++extern crate rustc_attr;
++#[allow(unused_extern_crates)]
++extern crate rustc_data_structures;
++#[allow(unused_extern_crates)]
++extern crate rustc_driver;
++#[allow(unused_extern_crates)]
++extern crate rustc_errors;
++#[allow(unused_extern_crates)]
++extern crate rustc_hir;
++#[allow(unused_extern_crates)]
++extern crate rustc_hir_pretty;
++#[allow(unused_extern_crates)]
++extern crate rustc_index;
++#[allow(unused_extern_crates)]
++extern crate rustc_infer;
++#[allow(unused_extern_crates)]
++extern crate rustc_lexer;
++#[allow(unused_extern_crates)]
++extern crate rustc_lint;
++#[allow(unused_extern_crates)]
++extern crate rustc_middle;
++#[allow(unused_extern_crates)]
++extern crate rustc_mir;
++#[allow(unused_extern_crates)]
++extern crate rustc_parse;
++#[allow(unused_extern_crates)]
++extern crate rustc_session;
++#[allow(unused_extern_crates)]
++extern crate rustc_span;
++#[allow(unused_extern_crates)]
++extern crate rustc_target;
++#[allow(unused_extern_crates)]
++extern crate rustc_trait_selection;
++#[allow(unused_extern_crates)]
++extern crate rustc_typeck;
++
++use rustc_data_structures::fx::FxHashSet;
++use rustc_lint::LintId;
++use rustc_session::Session;
++
++/// Macro used to declare a Clippy lint.
++///
++/// Every lint declaration consists of 4 parts:
++///
++/// 1. The documentation, which is used for the website
++/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions.
++/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or
++///    `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of.
++/// 4. The `description` that contains a short explanation on what's wrong with code where the
++///    lint is triggered.
++///
++/// Currently the categories `style`, `correctness`, `complexity` and `perf` are enabled by default.
++/// As said in the README.md of this repository, if the lint level mapping changes, please update
++/// README.md.
++///
++/// # Example
++///
++/// ```
++/// # #![feature(rustc_private)]
++/// # #[allow(unused_extern_crates)]
++/// # extern crate rustc_middle;
++/// # #[allow(unused_extern_crates)]
++/// # extern crate rustc_session;
++/// # #[macro_use]
++/// # use clippy_lints::declare_clippy_lint;
++/// use rustc_session::declare_tool_lint;
++///
++/// declare_clippy_lint! {
++///     /// **What it does:** Checks for ... (describe what the lint matches).
++///     ///
++///     /// **Why is this bad?** Supply the reason for linting the code.
++///     ///
++///     /// **Known problems:** None. (Or describe where it could go wrong.)
++///     ///
++///     /// **Example:**
++///     ///
++///     /// ```rust
++///     /// // Bad
++///     /// Insert a short example of code that triggers the lint
++///     ///
++///     /// // Good
++///     /// Insert a short example of improved code that doesn't trigger the lint
++///     /// ```
++///     pub LINT_NAME,
++///     pedantic,
++///     "description"
++/// }
++/// ```
++/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
++#[macro_export]
++macro_rules! declare_clippy_lint {
++    { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => {
++        declare_tool_lint! {
++            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
++        }
++    };
++    { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => {
++        declare_tool_lint! {
++            $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true
++        }
++    };
++    { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
++        declare_tool_lint! {
++            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
++        }
++    };
++    { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => {
++        declare_tool_lint! {
++            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
++        }
++    };
++    { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => {
++        declare_tool_lint! {
++            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
++        }
++    };
++    { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => {
++        declare_tool_lint! {
++            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
++        }
++    };
++    { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => {
++        declare_tool_lint! {
++            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
++        }
++    };
++    { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => {
++        declare_tool_lint! {
++            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
++        }
++    };
++    { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => {
++        declare_tool_lint! {
++            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
++        }
++    };
++    { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => {
++        declare_tool_lint! {
++            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
++        }
++    };
++}
++
++mod consts;
++#[macro_use]
++mod utils;
++
++// begin lints modules, do not remove this comment, it’s used in `update_lints`
++mod approx_const;
++mod arithmetic;
++mod as_conversions;
++mod assertions_on_constants;
++mod assign_ops;
++mod atomic_ordering;
++mod attrs;
++mod await_holding_lock;
++mod bit_mask;
++mod blacklisted_name;
++mod block_in_if_condition;
++mod booleans;
++mod bytecount;
++mod cargo_common_metadata;
++mod checked_conversions;
++mod cognitive_complexity;
++mod collapsible_if;
++mod comparison_chain;
++mod copies;
++mod copy_iterator;
++mod dbg_macro;
++mod default_trait_access;
++mod dereference;
++mod derive;
++mod doc;
++mod double_comparison;
++mod double_parens;
++mod drop_bounds;
++mod drop_forget_ref;
++mod duration_subsec;
++mod else_if_without_else;
++mod empty_enum;
++mod entry;
++mod enum_clike;
++mod enum_variants;
++mod eq_op;
++mod erasing_op;
++mod escape;
++mod eta_reduction;
++mod eval_order_dependence;
++mod excessive_bools;
++mod exit;
++mod explicit_write;
++mod fallible_impl_from;
++mod float_literal;
++mod floating_point_arithmetic;
++mod format;
++mod formatting;
++mod functions;
++mod future_not_send;
++mod get_last_with_len;
++mod identity_conversion;
++mod identity_op;
++mod if_let_mutex;
++mod if_let_some_result;
++mod if_not_else;
++mod implicit_return;
++mod implicit_saturating_sub;
++mod indexing_slicing;
++mod infinite_iter;
++mod inherent_impl;
++mod inherent_to_string;
++mod inline_fn_without_body;
++mod int_plus_one;
++mod integer_division;
++mod items_after_statements;
++mod large_const_arrays;
++mod large_enum_variant;
++mod large_stack_arrays;
++mod len_zero;
++mod let_if_seq;
++mod let_underscore;
++mod lifetimes;
++mod literal_representation;
++mod loops;
++mod macro_use;
++mod main_recursion;
++mod map_clone;
++mod map_unit_fn;
++mod match_on_vec_items;
++mod matches;
++mod mem_discriminant;
++mod mem_forget;
++mod mem_replace;
++mod methods;
++mod minmax;
++mod misc;
++mod misc_early;
++mod missing_const_for_fn;
++mod missing_doc;
++mod missing_inline;
++mod modulo_arithmetic;
++mod multiple_crate_versions;
++mod mut_key;
++mod mut_mut;
++mod mut_reference;
++mod mutable_debug_assertion;
++mod mutex_atomic;
++mod needless_bool;
++mod needless_borrow;
++mod needless_borrowed_ref;
++mod needless_continue;
++mod needless_pass_by_value;
++mod needless_update;
++mod neg_cmp_op_on_partial_ord;
++mod neg_multiply;
++mod new_without_default;
++mod no_effect;
++mod non_copy_const;
++mod non_expressive_names;
++mod open_options;
++mod option_env_unwrap;
++mod overflow_check_conditional;
++mod panic_unimplemented;
++mod partialeq_ne_impl;
++mod path_buf_push_overwrite;
++mod precedence;
++mod ptr;
++mod ptr_offset_with_cast;
++mod question_mark;
++mod ranges;
++mod redundant_clone;
++mod redundant_field_names;
++mod redundant_pattern_matching;
++mod redundant_pub_crate;
++mod redundant_static_lifetimes;
++mod reference;
++mod regex;
++mod returns;
++mod serde_api;
++mod shadow;
++mod single_component_path_imports;
++mod slow_vector_initialization;
++mod strings;
++mod suspicious_trait_impl;
++mod swap;
++mod tabs_in_doc_comments;
++mod temporary_assignment;
++mod to_digit_is_some;
++mod trait_bounds;
++mod transmute;
++mod transmuting_null;
++mod trivially_copy_pass_by_ref;
++mod try_err;
++mod types;
++mod unicode;
++mod unnamed_address;
++mod unsafe_removed_from_name;
++mod unused_io_amount;
++mod unused_self;
++mod unwrap;
++mod use_self;
++mod vec;
++mod verbose_file_reads;
++mod wildcard_dependencies;
++mod wildcard_imports;
++mod write;
++mod zero_div_zero;
++// end lints modules, do not remove this comment, it’s used in `update_lints`
++
++pub use crate::utils::conf::Conf;
++
++mod reexport {
++    pub use rustc_ast::ast::Name;
++}
++
++/// Register all pre expansion lints
++///
++/// Pre-expansion lints run before any macro expansion has happened.
++///
++/// Note that due to the architecture of the compiler, currently `cfg_attr` attributes on crate
++/// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
++///
++/// Used in `./src/driver.rs`.
++pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &Conf) {
++    store.register_pre_expansion_pass(|| box write::Write::default());
++    store.register_pre_expansion_pass(|| box redundant_field_names::RedundantFieldNames);
++    let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
++    store.register_pre_expansion_pass(move || box non_expressive_names::NonExpressiveNames {
++        single_char_binding_names_threshold,
++    });
++    store.register_pre_expansion_pass(|| box attrs::EarlyAttributes);
++    store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro);
++}
++
++#[doc(hidden)]
++pub fn read_conf(args: &[rustc_ast::ast::NestedMetaItem], sess: &Session) -> Conf {
++    use std::path::Path;
++    match utils::conf::file_from_args(args) {
++        Ok(file_name) => {
++            // if the user specified a file, it must exist, otherwise default to `clippy.toml` but
++            // do not require the file to exist
++            let file_name = match file_name {
++                Some(file_name) => file_name,
++                None => match utils::conf::lookup_conf_file() {
++                    Ok(Some(path)) => path,
++                    Ok(None) => return Conf::default(),
++                    Err(error) => {
++                        sess.struct_err(&format!("error finding Clippy's configuration file: {}", error))
++                            .emit();
++                        return Conf::default();
++                    },
++                },
++            };
++
++            let file_name = if file_name.is_relative() {
++                sess.local_crate_source_file
++                    .as_deref()
++                    .and_then(Path::parent)
++                    .unwrap_or_else(|| Path::new(""))
++                    .join(file_name)
++            } else {
++                file_name
++            };
++
++            let (conf, errors) = utils::conf::read(&file_name);
++
++            // all conf errors are non-fatal, we just use the default conf in case of error
++            for error in errors {
++                sess.struct_err(&format!(
++                    "error reading Clippy's configuration file `{}`: {}",
++                    file_name.display(),
++                    error
++                ))
++                .emit();
++            }
++
++            conf
++        },
++        Err((err, span)) => {
++            sess.struct_span_err(span, err)
++                .span_note(span, "Clippy will use default configuration")
++                .emit();
++            Conf::default()
++        },
++    }
++}
++
++/// Register all lints and lint groups with the rustc plugin registry
++///
++/// Used in `./src/driver.rs`.
++#[allow(clippy::too_many_lines)]
++#[rustfmt::skip]
++pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
++    register_removed_non_tool_lints(store);
++
++    // begin deprecated lints, do not remove this comment, it’s used in `update_lints`
++    store.register_removed(
++        "clippy::should_assert_eq",
++        "`assert!()` will be more flexible with RFC 2011",
++    );
++    store.register_removed(
++        "clippy::extend_from_slice",
++        "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice",
++    );
++    store.register_removed(
++        "clippy::range_step_by_zero",
++        "`iterator.step_by(0)` panics nowadays",
++    );
++    store.register_removed(
++        "clippy::unstable_as_slice",
++        "`Vec::as_slice` has been stabilized in 1.7",
++    );
++    store.register_removed(
++        "clippy::unstable_as_mut_slice",
++        "`Vec::as_mut_slice` has been stabilized in 1.7",
++    );
++    store.register_removed(
++        "clippy::str_to_string",
++        "using `str::to_string` is common even today and specialization will likely happen soon",
++    );
++    store.register_removed(
++        "clippy::string_to_string",
++        "using `string::to_string` is common even today and specialization will likely happen soon",
++    );
++    store.register_removed(
++        "clippy::misaligned_transmute",
++        "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
++    );
++    store.register_removed(
++        "clippy::assign_ops",
++        "using compound assignment operators (e.g., `+=`) is harmless",
++    );
++    store.register_removed(
++        "clippy::if_let_redundant_pattern_matching",
++        "this lint has been changed to redundant_pattern_matching",
++    );
++    store.register_removed(
++        "clippy::unsafe_vector_initialization",
++        "the replacement suggested by this lint had substantially different behavior",
++    );
++    store.register_removed(
++        "clippy::invalid_ref",
++        "superseded by rustc lint `invalid_value`",
++    );
++    store.register_removed(
++        "clippy::unused_collect",
++        "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint",
++    );
++    store.register_removed(
++        "clippy::into_iter_on_array",
++        "this lint has been uplifted to rustc and is now called `array_into_iter`",
++    );
++    store.register_removed(
++        "clippy::unused_label",
++        "this lint has been uplifted to rustc and is now called `unused_labels`",
++    );
++    store.register_removed(
++        "clippy::replace_consts",
++        "associated-constants `MIN`/`MAX` of integers are prefer to `{min,max}_value()` and module constants",
++    );
++    // end deprecated lints, do not remove this comment, it’s used in `update_lints`
++
++    // begin register lints, do not remove this comment, it’s used in `update_lints`
++    store.register_lints(&[
++        &approx_const::APPROX_CONSTANT,
++        &arithmetic::FLOAT_ARITHMETIC,
++        &arithmetic::INTEGER_ARITHMETIC,
++        &as_conversions::AS_CONVERSIONS,
++        &assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
++        &assign_ops::ASSIGN_OP_PATTERN,
++        &assign_ops::MISREFACTORED_ASSIGN_OP,
++        &atomic_ordering::INVALID_ATOMIC_ORDERING,
++        &attrs::DEPRECATED_CFG_ATTR,
++        &attrs::DEPRECATED_SEMVER,
++        &attrs::EMPTY_LINE_AFTER_OUTER_ATTR,
++        &attrs::INLINE_ALWAYS,
++        &attrs::MISMATCHED_TARGET_OS,
++        &attrs::UNKNOWN_CLIPPY_LINTS,
++        &attrs::USELESS_ATTRIBUTE,
++        &await_holding_lock::AWAIT_HOLDING_LOCK,
++        &bit_mask::BAD_BIT_MASK,
++        &bit_mask::INEFFECTIVE_BIT_MASK,
++        &bit_mask::VERBOSE_BIT_MASK,
++        &blacklisted_name::BLACKLISTED_NAME,
++        &block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR,
++        &block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT,
++        &booleans::LOGIC_BUG,
++        &booleans::NONMINIMAL_BOOL,
++        &bytecount::NAIVE_BYTECOUNT,
++        &cargo_common_metadata::CARGO_COMMON_METADATA,
++        &checked_conversions::CHECKED_CONVERSIONS,
++        &cognitive_complexity::COGNITIVE_COMPLEXITY,
++        &collapsible_if::COLLAPSIBLE_IF,
++        &comparison_chain::COMPARISON_CHAIN,
++        &copies::IFS_SAME_COND,
++        &copies::IF_SAME_THEN_ELSE,
++        &copies::MATCH_SAME_ARMS,
++        &copies::SAME_FUNCTIONS_IN_IF_CONDITION,
++        &copy_iterator::COPY_ITERATOR,
++        &dbg_macro::DBG_MACRO,
++        &default_trait_access::DEFAULT_TRAIT_ACCESS,
++        &dereference::EXPLICIT_DEREF_METHODS,
++        &derive::DERIVE_HASH_XOR_EQ,
++        &derive::EXPL_IMPL_CLONE_ON_COPY,
++        &derive::UNSAFE_DERIVE_DESERIALIZE,
++        &doc::DOC_MARKDOWN,
++        &doc::MISSING_ERRORS_DOC,
++        &doc::MISSING_SAFETY_DOC,
++        &doc::NEEDLESS_DOCTEST_MAIN,
++        &double_comparison::DOUBLE_COMPARISONS,
++        &double_parens::DOUBLE_PARENS,
++        &drop_bounds::DROP_BOUNDS,
++        &drop_forget_ref::DROP_COPY,
++        &drop_forget_ref::DROP_REF,
++        &drop_forget_ref::FORGET_COPY,
++        &drop_forget_ref::FORGET_REF,
++        &duration_subsec::DURATION_SUBSEC,
++        &else_if_without_else::ELSE_IF_WITHOUT_ELSE,
++        &empty_enum::EMPTY_ENUM,
++        &entry::MAP_ENTRY,
++        &enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
++        &enum_variants::ENUM_VARIANT_NAMES,
++        &enum_variants::MODULE_INCEPTION,
++        &enum_variants::MODULE_NAME_REPETITIONS,
++        &enum_variants::PUB_ENUM_VARIANT_NAMES,
++        &eq_op::EQ_OP,
++        &eq_op::OP_REF,
++        &erasing_op::ERASING_OP,
++        &escape::BOXED_LOCAL,
++        &eta_reduction::REDUNDANT_CLOSURE,
++        &eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
++        &eval_order_dependence::DIVERGING_SUB_EXPRESSION,
++        &eval_order_dependence::EVAL_ORDER_DEPENDENCE,
++        &excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
++        &excessive_bools::STRUCT_EXCESSIVE_BOOLS,
++        &exit::EXIT,
++        &explicit_write::EXPLICIT_WRITE,
++        &fallible_impl_from::FALLIBLE_IMPL_FROM,
++        &float_literal::EXCESSIVE_PRECISION,
++        &float_literal::LOSSY_FLOAT_LITERAL,
++        &floating_point_arithmetic::IMPRECISE_FLOPS,
++        &floating_point_arithmetic::SUBOPTIMAL_FLOPS,
++        &format::USELESS_FORMAT,
++        &formatting::POSSIBLE_MISSING_COMMA,
++        &formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
++        &formatting::SUSPICIOUS_ELSE_FORMATTING,
++        &formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
++        &functions::DOUBLE_MUST_USE,
++        &functions::MUST_USE_CANDIDATE,
++        &functions::MUST_USE_UNIT,
++        &functions::NOT_UNSAFE_PTR_ARG_DEREF,
++        &functions::TOO_MANY_ARGUMENTS,
++        &functions::TOO_MANY_LINES,
++        &future_not_send::FUTURE_NOT_SEND,
++        &get_last_with_len::GET_LAST_WITH_LEN,
++        &identity_conversion::IDENTITY_CONVERSION,
++        &identity_op::IDENTITY_OP,
++        &if_let_mutex::IF_LET_MUTEX,
++        &if_let_some_result::IF_LET_SOME_RESULT,
++        &if_not_else::IF_NOT_ELSE,
++        &implicit_return::IMPLICIT_RETURN,
++        &implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
++        &indexing_slicing::INDEXING_SLICING,
++        &indexing_slicing::OUT_OF_BOUNDS_INDEXING,
++        &infinite_iter::INFINITE_ITER,
++        &infinite_iter::MAYBE_INFINITE_ITER,
++        &inherent_impl::MULTIPLE_INHERENT_IMPL,
++        &inherent_to_string::INHERENT_TO_STRING,
++        &inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY,
++        &inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
++        &int_plus_one::INT_PLUS_ONE,
++        &integer_division::INTEGER_DIVISION,
++        &items_after_statements::ITEMS_AFTER_STATEMENTS,
++        &large_const_arrays::LARGE_CONST_ARRAYS,
++        &large_enum_variant::LARGE_ENUM_VARIANT,
++        &large_stack_arrays::LARGE_STACK_ARRAYS,
++        &len_zero::LEN_WITHOUT_IS_EMPTY,
++        &len_zero::LEN_ZERO,
++        &let_if_seq::USELESS_LET_IF_SEQ,
++        &let_underscore::LET_UNDERSCORE_LOCK,
++        &let_underscore::LET_UNDERSCORE_MUST_USE,
++        &lifetimes::EXTRA_UNUSED_LIFETIMES,
++        &lifetimes::NEEDLESS_LIFETIMES,
++        &literal_representation::DECIMAL_LITERAL_REPRESENTATION,
++        &literal_representation::INCONSISTENT_DIGIT_GROUPING,
++        &literal_representation::LARGE_DIGIT_GROUPS,
++        &literal_representation::MISTYPED_LITERAL_SUFFIXES,
++        &literal_representation::UNREADABLE_LITERAL,
++        &loops::EMPTY_LOOP,
++        &loops::EXPLICIT_COUNTER_LOOP,
++        &loops::EXPLICIT_INTO_ITER_LOOP,
++        &loops::EXPLICIT_ITER_LOOP,
++        &loops::FOR_KV_MAP,
++        &loops::FOR_LOOP_OVER_OPTION,
++        &loops::FOR_LOOP_OVER_RESULT,
++        &loops::ITER_NEXT_LOOP,
++        &loops::MANUAL_MEMCPY,
++        &loops::MUT_RANGE_BOUND,
++        &loops::NEEDLESS_COLLECT,
++        &loops::NEEDLESS_RANGE_LOOP,
++        &loops::NEVER_LOOP,
++        &loops::REVERSE_RANGE_LOOP,
++        &loops::WHILE_IMMUTABLE_CONDITION,
++        &loops::WHILE_LET_LOOP,
++        &loops::WHILE_LET_ON_ITERATOR,
++        &macro_use::MACRO_USE_IMPORTS,
++        &main_recursion::MAIN_RECURSION,
++        &map_clone::MAP_CLONE,
++        &map_unit_fn::OPTION_MAP_UNIT_FN,
++        &map_unit_fn::RESULT_MAP_UNIT_FN,
++        &match_on_vec_items::MATCH_ON_VEC_ITEMS,
++        &matches::INFALLIBLE_DESTRUCTURING_MATCH,
++        &matches::MATCH_AS_REF,
++        &matches::MATCH_BOOL,
++        &matches::MATCH_OVERLAPPING_ARM,
++        &matches::MATCH_REF_PATS,
++        &matches::MATCH_SINGLE_BINDING,
++        &matches::MATCH_WILD_ERR_ARM,
++        &matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
++        &matches::SINGLE_MATCH,
++        &matches::SINGLE_MATCH_ELSE,
++        &matches::WILDCARD_ENUM_MATCH_ARM,
++        &matches::WILDCARD_IN_OR_PATTERNS,
++        &mem_discriminant::MEM_DISCRIMINANT_NON_ENUM,
++        &mem_forget::MEM_FORGET,
++        &mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
++        &mem_replace::MEM_REPLACE_WITH_DEFAULT,
++        &mem_replace::MEM_REPLACE_WITH_UNINIT,
++        &methods::CHARS_LAST_CMP,
++        &methods::CHARS_NEXT_CMP,
++        &methods::CLONE_DOUBLE_REF,
++        &methods::CLONE_ON_COPY,
++        &methods::CLONE_ON_REF_PTR,
++        &methods::EXPECT_FUN_CALL,
++        &methods::FILETYPE_IS_FILE,
++        &methods::FILTER_MAP,
++        &methods::FILTER_MAP_NEXT,
++        &methods::FILTER_NEXT,
++        &methods::FIND_MAP,
++        &methods::FLAT_MAP_IDENTITY,
++        &methods::GET_UNWRAP,
++        &methods::INEFFICIENT_TO_STRING,
++        &methods::INTO_ITER_ON_REF,
++        &methods::ITERATOR_STEP_BY_ZERO,
++        &methods::ITER_CLONED_COLLECT,
++        &methods::ITER_NTH,
++        &methods::ITER_NTH_ZERO,
++        &methods::ITER_SKIP_NEXT,
++        &methods::MANUAL_SATURATING_ARITHMETIC,
++        &methods::MAP_FLATTEN,
++        &methods::NEW_RET_NO_SELF,
++        &methods::OK_EXPECT,
++        &methods::OPTION_AND_THEN_SOME,
++        &methods::OPTION_AS_REF_DEREF,
++        &methods::OPTION_EXPECT_USED,
++        &methods::OPTION_MAP_OR_NONE,
++        &methods::OPTION_MAP_UNWRAP_OR,
++        &methods::OPTION_MAP_UNWRAP_OR_ELSE,
++        &methods::OPTION_UNWRAP_USED,
++        &methods::OR_FUN_CALL,
++        &methods::RESULT_EXPECT_USED,
++        &methods::RESULT_MAP_OR_INTO_OPTION,
++        &methods::RESULT_MAP_UNWRAP_OR_ELSE,
++        &methods::RESULT_UNWRAP_USED,
++        &methods::SEARCH_IS_SOME,
++        &methods::SHOULD_IMPLEMENT_TRAIT,
++        &methods::SINGLE_CHAR_PATTERN,
++        &methods::SKIP_WHILE_NEXT,
++        &methods::STRING_EXTEND_CHARS,
++        &methods::SUSPICIOUS_MAP,
++        &methods::TEMPORARY_CSTRING_AS_PTR,
++        &methods::UNINIT_ASSUMED_INIT,
++        &methods::UNNECESSARY_FILTER_MAP,
++        &methods::UNNECESSARY_FOLD,
++        &methods::USELESS_ASREF,
++        &methods::WRONG_PUB_SELF_CONVENTION,
++        &methods::WRONG_SELF_CONVENTION,
++        &methods::ZST_OFFSET,
++        &minmax::MIN_MAX,
++        &misc::CMP_NAN,
++        &misc::CMP_OWNED,
++        &misc::FLOAT_CMP,
++        &misc::FLOAT_CMP_CONST,
++        &misc::MODULO_ONE,
++        &misc::SHORT_CIRCUIT_STATEMENT,
++        &misc::TOPLEVEL_REF_ARG,
++        &misc::USED_UNDERSCORE_BINDING,
++        &misc::ZERO_PTR,
++        &misc_early::BUILTIN_TYPE_SHADOW,
++        &misc_early::DOUBLE_NEG,
++        &misc_early::DUPLICATE_UNDERSCORE_ARGUMENT,
++        &misc_early::MIXED_CASE_HEX_LITERALS,
++        &misc_early::REDUNDANT_CLOSURE_CALL,
++        &misc_early::REDUNDANT_PATTERN,
++        &misc_early::UNNEEDED_FIELD_PATTERN,
++        &misc_early::UNNEEDED_WILDCARD_PATTERN,
++        &misc_early::UNSEPARATED_LITERAL_SUFFIX,
++        &misc_early::ZERO_PREFIXED_LITERAL,
++        &missing_const_for_fn::MISSING_CONST_FOR_FN,
++        &missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
++        &missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
++        &modulo_arithmetic::MODULO_ARITHMETIC,
++        &multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
++        &mut_key::MUTABLE_KEY_TYPE,
++        &mut_mut::MUT_MUT,
++        &mut_reference::UNNECESSARY_MUT_PASSED,
++        &mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
++        &mutex_atomic::MUTEX_ATOMIC,
++        &mutex_atomic::MUTEX_INTEGER,
++        &needless_bool::BOOL_COMPARISON,
++        &needless_bool::NEEDLESS_BOOL,
++        &needless_borrow::NEEDLESS_BORROW,
++        &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
++        &needless_continue::NEEDLESS_CONTINUE,
++        &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
++        &needless_update::NEEDLESS_UPDATE,
++        &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD,
++        &neg_multiply::NEG_MULTIPLY,
++        &new_without_default::NEW_WITHOUT_DEFAULT,
++        &no_effect::NO_EFFECT,
++        &no_effect::UNNECESSARY_OPERATION,
++        &non_copy_const::BORROW_INTERIOR_MUTABLE_CONST,
++        &non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST,
++        &non_expressive_names::JUST_UNDERSCORES_AND_DIGITS,
++        &non_expressive_names::MANY_SINGLE_CHAR_NAMES,
++        &non_expressive_names::SIMILAR_NAMES,
++        &open_options::NONSENSICAL_OPEN_OPTIONS,
++        &option_env_unwrap::OPTION_ENV_UNWRAP,
++        &overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
++        &panic_unimplemented::PANIC,
++        &panic_unimplemented::PANIC_PARAMS,
++        &panic_unimplemented::TODO,
++        &panic_unimplemented::UNIMPLEMENTED,
++        &panic_unimplemented::UNREACHABLE,
++        &partialeq_ne_impl::PARTIALEQ_NE_IMPL,
++        &path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
++        &precedence::PRECEDENCE,
++        &ptr::CMP_NULL,
++        &ptr::MUT_FROM_REF,
++        &ptr::PTR_ARG,
++        &ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
++        &question_mark::QUESTION_MARK,
++        &ranges::RANGE_MINUS_ONE,
++        &ranges::RANGE_PLUS_ONE,
++        &ranges::RANGE_ZIP_WITH_LEN,
++        &redundant_clone::REDUNDANT_CLONE,
++        &redundant_field_names::REDUNDANT_FIELD_NAMES,
++        &redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING,
++        &redundant_pub_crate::REDUNDANT_PUB_CRATE,
++        &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
++        &reference::DEREF_ADDROF,
++        &reference::REF_IN_DEREF,
++        &regex::INVALID_REGEX,
++        &regex::REGEX_MACRO,
++        &regex::TRIVIAL_REGEX,
++        &returns::LET_AND_RETURN,
++        &returns::NEEDLESS_RETURN,
++        &returns::UNUSED_UNIT,
++        &serde_api::SERDE_API_MISUSE,
++        &shadow::SHADOW_REUSE,
++        &shadow::SHADOW_SAME,
++        &shadow::SHADOW_UNRELATED,
++        &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
++        &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
++        &strings::STRING_ADD,
++        &strings::STRING_ADD_ASSIGN,
++        &strings::STRING_LIT_AS_BYTES,
++        &suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
++        &suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
++        &swap::ALMOST_SWAPPED,
++        &swap::MANUAL_SWAP,
++        &tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
++        &temporary_assignment::TEMPORARY_ASSIGNMENT,
++        &to_digit_is_some::TO_DIGIT_IS_SOME,
++        &trait_bounds::TYPE_REPETITION_IN_BOUNDS,
++        &transmute::CROSSPOINTER_TRANSMUTE,
++        &transmute::TRANSMUTE_BYTES_TO_STR,
++        &transmute::TRANSMUTE_FLOAT_TO_INT,
++        &transmute::TRANSMUTE_INT_TO_BOOL,
++        &transmute::TRANSMUTE_INT_TO_CHAR,
++        &transmute::TRANSMUTE_INT_TO_FLOAT,
++        &transmute::TRANSMUTE_PTR_TO_PTR,
++        &transmute::TRANSMUTE_PTR_TO_REF,
++        &transmute::UNSOUND_COLLECTION_TRANSMUTE,
++        &transmute::USELESS_TRANSMUTE,
++        &transmute::WRONG_TRANSMUTE,
++        &transmuting_null::TRANSMUTING_NULL,
++        &trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF,
++        &try_err::TRY_ERR,
++        &types::ABSURD_EXTREME_COMPARISONS,
++        &types::BORROWED_BOX,
++        &types::BOX_VEC,
++        &types::CAST_LOSSLESS,
++        &types::CAST_POSSIBLE_TRUNCATION,
++        &types::CAST_POSSIBLE_WRAP,
++        &types::CAST_PRECISION_LOSS,
++        &types::CAST_PTR_ALIGNMENT,
++        &types::CAST_REF_TO_MUT,
++        &types::CAST_SIGN_LOSS,
++        &types::CHAR_LIT_AS_U8,
++        &types::FN_TO_NUMERIC_CAST,
++        &types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
++        &types::IMPLICIT_HASHER,
++        &types::INVALID_UPCAST_COMPARISONS,
++        &types::LET_UNIT_VALUE,
++        &types::LINKEDLIST,
++        &types::OPTION_OPTION,
++        &types::REDUNDANT_ALLOCATION,
++        &types::TYPE_COMPLEXITY,
++        &types::UNIT_ARG,
++        &types::UNIT_CMP,
++        &types::UNNECESSARY_CAST,
++        &types::VEC_BOX,
++        &unicode::NON_ASCII_LITERAL,
++        &unicode::UNICODE_NOT_NFC,
++        &unicode::ZERO_WIDTH_SPACE,
++        &unnamed_address::FN_ADDRESS_COMPARISONS,
++        &unnamed_address::VTABLE_ADDRESS_COMPARISONS,
++        &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
++        &unused_io_amount::UNUSED_IO_AMOUNT,
++        &unused_self::UNUSED_SELF,
++        &unwrap::PANICKING_UNWRAP,
++        &unwrap::UNNECESSARY_UNWRAP,
++        &use_self::USE_SELF,
++        &utils::internal_lints::CLIPPY_LINTS_INTERNAL,
++        &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
++        &utils::internal_lints::COMPILER_LINT_FUNCTIONS,
++        &utils::internal_lints::DEFAULT_LINT,
++        &utils::internal_lints::LINT_WITHOUT_LINT_PASS,
++        &utils::internal_lints::OUTER_EXPN_EXPN_DATA,
++        &utils::internal_lints::PRODUCE_ICE,
++        &vec::USELESS_VEC,
++        &verbose_file_reads::VERBOSE_FILE_READS,
++        &wildcard_dependencies::WILDCARD_DEPENDENCIES,
++        &wildcard_imports::ENUM_GLOB_USE,
++        &wildcard_imports::WILDCARD_IMPORTS,
++        &write::PRINTLN_EMPTY_STRING,
++        &write::PRINT_LITERAL,
++        &write::PRINT_STDOUT,
++        &write::PRINT_WITH_NEWLINE,
++        &write::USE_DEBUG,
++        &write::WRITELN_EMPTY_STRING,
++        &write::WRITE_LITERAL,
++        &write::WRITE_WITH_NEWLINE,
++        &zero_div_zero::ZERO_DIVIDED_BY_ZERO,
++    ]);
++    // end register lints, do not remove this comment, it’s used in `update_lints`
++
++    store.register_late_pass(|| box await_holding_lock::AwaitHoldingLock);
++    store.register_late_pass(|| box serde_api::SerdeAPI);
++    store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
++    store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
++    store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass);
++    store.register_late_pass(|| box utils::inspector::DeepCodeInspector);
++    store.register_late_pass(|| box utils::author::Author);
++    let vec_box_size_threshold = conf.vec_box_size_threshold;
++    store.register_late_pass(move || box types::Types::new(vec_box_size_threshold));
++    store.register_late_pass(|| box booleans::NonminimalBool);
++    store.register_late_pass(|| box eq_op::EqOp);
++    store.register_late_pass(|| box enum_clike::UnportableVariant);
++    store.register_late_pass(|| box float_literal::FloatLiteral);
++    let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
++    store.register_late_pass(move || box bit_mask::BitMask::new(verbose_bit_mask_threshold));
++    store.register_late_pass(|| box ptr::Ptr);
++    store.register_late_pass(|| box needless_bool::NeedlessBool);
++    store.register_late_pass(|| box needless_bool::BoolComparison);
++    store.register_late_pass(|| box approx_const::ApproxConstant);
++    store.register_late_pass(|| box misc::MiscLints);
++    store.register_late_pass(|| box eta_reduction::EtaReduction);
++    store.register_late_pass(|| box identity_op::IdentityOp);
++    store.register_late_pass(|| box erasing_op::ErasingOp);
++    store.register_late_pass(|| box mut_mut::MutMut);
++    store.register_late_pass(|| box mut_reference::UnnecessaryMutPassed);
++    store.register_late_pass(|| box len_zero::LenZero);
++    store.register_late_pass(|| box attrs::Attributes);
++    store.register_late_pass(|| box block_in_if_condition::BlockInIfCondition);
++    store.register_late_pass(|| box unicode::Unicode);
++    store.register_late_pass(|| box strings::StringAdd);
++    store.register_late_pass(|| box implicit_return::ImplicitReturn);
++    store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub);
++    store.register_late_pass(|| box methods::Methods);
++    store.register_late_pass(|| box map_clone::MapClone);
++    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 loops::Loops);
++    store.register_late_pass(|| box main_recursion::MainRecursion::default());
++    store.register_late_pass(|| box lifetimes::Lifetimes);
++    store.register_late_pass(|| box entry::HashMapPass);
++    store.register_late_pass(|| box ranges::Ranges);
++    store.register_late_pass(|| box types::Casts);
++    let type_complexity_threshold = conf.type_complexity_threshold;
++    store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold));
++    store.register_late_pass(|| box matches::Matches::default());
++    store.register_late_pass(|| box minmax::MinMaxPass);
++    store.register_late_pass(|| box open_options::OpenOptions);
++    store.register_late_pass(|| box zero_div_zero::ZeroDiv);
++    store.register_late_pass(|| box mutex_atomic::Mutex);
++    store.register_late_pass(|| box needless_update::NeedlessUpdate);
++    store.register_late_pass(|| box needless_borrow::NeedlessBorrow::default());
++    store.register_late_pass(|| box needless_borrowed_ref::NeedlessBorrowedRef);
++    store.register_late_pass(|| box no_effect::NoEffect);
++    store.register_late_pass(|| box temporary_assignment::TemporaryAssignment);
++    store.register_late_pass(|| box transmute::Transmute);
++    let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
++    store.register_late_pass(move || box cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold));
++    let too_large_for_stack = conf.too_large_for_stack;
++    store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack});
++    store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented);
++    store.register_late_pass(|| box strings::StringLitAsBytes);
++    store.register_late_pass(|| box derive::Derive);
++    store.register_late_pass(|| box types::CharLitAsU8);
++    store.register_late_pass(|| box vec::UselessVec);
++    store.register_late_pass(|| box drop_bounds::DropBounds);
++    store.register_late_pass(|| box get_last_with_len::GetLastWithLen);
++    store.register_late_pass(|| box drop_forget_ref::DropForgetRef);
++    store.register_late_pass(|| box empty_enum::EmptyEnum);
++    store.register_late_pass(|| box types::AbsurdExtremeComparisons);
++    store.register_late_pass(|| box types::InvalidUpcastComparisons);
++    store.register_late_pass(|| box regex::Regex::default());
++    store.register_late_pass(|| box copies::CopyAndPaste);
++    store.register_late_pass(|| box copy_iterator::CopyIterator);
++    store.register_late_pass(|| box format::UselessFormat);
++    store.register_late_pass(|| box swap::Swap);
++    store.register_late_pass(|| box overflow_check_conditional::OverflowCheckConditional);
++    store.register_late_pass(|| box new_without_default::NewWithoutDefault::default());
++    let blacklisted_names = conf.blacklisted_names.iter().cloned().collect::<FxHashSet<_>>();
++    store.register_late_pass(move || box blacklisted_name::BlacklistedName::new(blacklisted_names.clone()));
++    let too_many_arguments_threshold1 = conf.too_many_arguments_threshold;
++    let too_many_lines_threshold2 = conf.too_many_lines_threshold;
++    store.register_late_pass(move || box functions::Functions::new(too_many_arguments_threshold1, too_many_lines_threshold2));
++    let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
++    store.register_late_pass(move || box doc::DocMarkdown::new(doc_valid_idents.clone()));
++    store.register_late_pass(|| box neg_multiply::NegMultiply);
++    store.register_late_pass(|| box mem_discriminant::MemDiscriminant);
++    store.register_late_pass(|| box mem_forget::MemForget);
++    store.register_late_pass(|| box mem_replace::MemReplace);
++    store.register_late_pass(|| box arithmetic::Arithmetic::default());
++    store.register_late_pass(|| box assign_ops::AssignOps);
++    store.register_late_pass(|| box let_if_seq::LetIfSeq);
++    store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence);
++    store.register_late_pass(|| box missing_doc::MissingDoc::new());
++    store.register_late_pass(|| box missing_inline::MissingInline);
++    store.register_late_pass(|| box if_let_some_result::OkIfLet);
++    store.register_late_pass(|| box redundant_pattern_matching::RedundantPatternMatching);
++    store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl);
++    store.register_late_pass(|| box unused_io_amount::UnusedIoAmount);
++    let enum_variant_size_threshold = conf.enum_variant_size_threshold;
++    store.register_late_pass(move || box large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold));
++    store.register_late_pass(|| box explicit_write::ExplicitWrite);
++    store.register_late_pass(|| box needless_pass_by_value::NeedlessPassByValue);
++    let trivially_copy_pass_by_ref = trivially_copy_pass_by_ref::TriviallyCopyPassByRef::new(
++        conf.trivial_copy_size_limit,
++        &sess.target,
++    );
++    store.register_late_pass(move || box trivially_copy_pass_by_ref);
++    store.register_late_pass(|| box try_err::TryErr);
++    store.register_late_pass(|| box use_self::UseSelf);
++    store.register_late_pass(|| box bytecount::ByteCount);
++    store.register_late_pass(|| box infinite_iter::InfiniteIter);
++    store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody);
++    store.register_late_pass(|| box identity_conversion::IdentityConversion::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_late_pass(|| box suspicious_trait_impl::SuspiciousImpl);
++    store.register_late_pass(|| box map_unit_fn::MapUnit);
++    store.register_late_pass(|| box inherent_impl::MultipleInherentImpl::default());
++    store.register_late_pass(|| box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd);
++    store.register_late_pass(|| box unwrap::Unwrap);
++    store.register_late_pass(|| box duration_subsec::DurationSubsec);
++    store.register_late_pass(|| box default_trait_access::DefaultTraitAccess);
++    store.register_late_pass(|| box indexing_slicing::IndexingSlicing);
++    store.register_late_pass(|| box non_copy_const::NonCopyConst);
++    store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast);
++    store.register_late_pass(|| box redundant_clone::RedundantClone);
++    store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit);
++    store.register_late_pass(|| box types::RefToMut);
++    store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants);
++    store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn);
++    store.register_late_pass(|| box transmuting_null::TransmutingNull);
++    store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite);
++    store.register_late_pass(|| box checked_conversions::CheckedConversions);
++    store.register_late_pass(|| box integer_division::IntegerDivision);
++    store.register_late_pass(|| box inherent_to_string::InherentToString);
++    store.register_late_pass(|| box trait_bounds::TraitBounds);
++    store.register_late_pass(|| box comparison_chain::ComparisonChain);
++    store.register_late_pass(|| box mut_key::MutableKeyType);
++    store.register_late_pass(|| box modulo_arithmetic::ModuloArithmetic);
++    store.register_early_pass(|| box reference::DerefAddrOf);
++    store.register_early_pass(|| box reference::RefInDeref);
++    store.register_early_pass(|| box double_parens::DoubleParens);
++    store.register_early_pass(|| box unsafe_removed_from_name::UnsafeNameRemoval);
++    store.register_early_pass(|| box if_not_else::IfNotElse);
++    store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse);
++    store.register_early_pass(|| box int_plus_one::IntPlusOne);
++    store.register_early_pass(|| box formatting::Formatting);
++    store.register_early_pass(|| box misc_early::MiscEarlyLints);
++    store.register_early_pass(|| box returns::Return);
++    store.register_early_pass(|| box collapsible_if::CollapsibleIf);
++    store.register_early_pass(|| box items_after_statements::ItemsAfterStatements);
++    store.register_early_pass(|| box precedence::Precedence);
++    store.register_early_pass(|| box needless_continue::NeedlessContinue);
++    store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes);
++    store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata);
++    store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions);
++    store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies);
++    store.register_early_pass(|| box literal_representation::LiteralDigitGrouping);
++    let literal_representation_threshold = conf.literal_representation_threshold;
++    store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold));
++    store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal);
++    let enum_variant_name_threshold = conf.enum_variant_name_threshold;
++    store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold));
++    store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments);
++    store.register_late_pass(|| box unused_self::UnusedSelf);
++    store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall);
++    store.register_late_pass(|| box exit::Exit);
++    store.register_late_pass(|| box to_digit_is_some::ToDigitIsSome);
++    let array_size_threshold = conf.array_size_threshold;
++    store.register_late_pass(move || box large_stack_arrays::LargeStackArrays::new(array_size_threshold));
++    store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold));
++    store.register_late_pass(move || box floating_point_arithmetic::FloatingPointArithmetic);
++    store.register_early_pass(|| box as_conversions::AsConversions);
++    store.register_early_pass(|| box utils::internal_lints::ProduceIce);
++    store.register_late_pass(|| box let_underscore::LetUnderscore);
++    store.register_late_pass(|| box atomic_ordering::AtomicOrdering);
++    store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports);
++    let max_fn_params_bools = conf.max_fn_params_bools;
++    let max_struct_bools = conf.max_struct_bools;
++    store.register_early_pass(move || box excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools));
++    store.register_early_pass(|| box option_env_unwrap::OptionEnvUnwrap);
++    store.register_late_pass(|| box wildcard_imports::WildcardImports);
++    store.register_early_pass(|| box macro_use::MacroUseImports);
++    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 future_not_send::FutureNotSend);
++    store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
++    store.register_late_pass(|| box if_let_mutex::IfLetMutex);
++    store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
++
++    store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
++        LintId::of(&arithmetic::FLOAT_ARITHMETIC),
++        LintId::of(&arithmetic::INTEGER_ARITHMETIC),
++        LintId::of(&as_conversions::AS_CONVERSIONS),
++        LintId::of(&dbg_macro::DBG_MACRO),
++        LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
++        LintId::of(&exit::EXIT),
++        LintId::of(&float_literal::LOSSY_FLOAT_LITERAL),
++        LintId::of(&implicit_return::IMPLICIT_RETURN),
++        LintId::of(&indexing_slicing::INDEXING_SLICING),
++        LintId::of(&inherent_impl::MULTIPLE_INHERENT_IMPL),
++        LintId::of(&integer_division::INTEGER_DIVISION),
++        LintId::of(&let_underscore::LET_UNDERSCORE_MUST_USE),
++        LintId::of(&literal_representation::DECIMAL_LITERAL_REPRESENTATION),
++        LintId::of(&matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
++        LintId::of(&matches::WILDCARD_ENUM_MATCH_ARM),
++        LintId::of(&mem_forget::MEM_FORGET),
++        LintId::of(&methods::CLONE_ON_REF_PTR),
++        LintId::of(&methods::FILETYPE_IS_FILE),
++        LintId::of(&methods::GET_UNWRAP),
++        LintId::of(&methods::OPTION_EXPECT_USED),
++        LintId::of(&methods::OPTION_UNWRAP_USED),
++        LintId::of(&methods::RESULT_EXPECT_USED),
++        LintId::of(&methods::RESULT_UNWRAP_USED),
++        LintId::of(&methods::WRONG_PUB_SELF_CONVENTION),
++        LintId::of(&misc::FLOAT_CMP_CONST),
++        LintId::of(&misc_early::UNNEEDED_FIELD_PATTERN),
++        LintId::of(&missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
++        LintId::of(&missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
++        LintId::of(&modulo_arithmetic::MODULO_ARITHMETIC),
++        LintId::of(&panic_unimplemented::PANIC),
++        LintId::of(&panic_unimplemented::TODO),
++        LintId::of(&panic_unimplemented::UNIMPLEMENTED),
++        LintId::of(&panic_unimplemented::UNREACHABLE),
++        LintId::of(&shadow::SHADOW_REUSE),
++        LintId::of(&shadow::SHADOW_SAME),
++        LintId::of(&strings::STRING_ADD),
++        LintId::of(&verbose_file_reads::VERBOSE_FILE_READS),
++        LintId::of(&write::PRINT_STDOUT),
++        LintId::of(&write::USE_DEBUG),
++    ]);
++
++    store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
++        LintId::of(&attrs::INLINE_ALWAYS),
++        LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK),
++        LintId::of(&checked_conversions::CHECKED_CONVERSIONS),
++        LintId::of(&copies::MATCH_SAME_ARMS),
++        LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
++        LintId::of(&copy_iterator::COPY_ITERATOR),
++        LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS),
++        LintId::of(&dereference::EXPLICIT_DEREF_METHODS),
++        LintId::of(&derive::EXPL_IMPL_CLONE_ON_COPY),
++        LintId::of(&derive::UNSAFE_DERIVE_DESERIALIZE),
++        LintId::of(&doc::DOC_MARKDOWN),
++        LintId::of(&doc::MISSING_ERRORS_DOC),
++        LintId::of(&empty_enum::EMPTY_ENUM),
++        LintId::of(&enum_variants::MODULE_NAME_REPETITIONS),
++        LintId::of(&enum_variants::PUB_ENUM_VARIANT_NAMES),
++        LintId::of(&eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),
++        LintId::of(&excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS),
++        LintId::of(&excessive_bools::STRUCT_EXCESSIVE_BOOLS),
++        LintId::of(&functions::MUST_USE_CANDIDATE),
++        LintId::of(&functions::TOO_MANY_LINES),
++        LintId::of(&if_not_else::IF_NOT_ELSE),
++        LintId::of(&implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
++        LintId::of(&infinite_iter::MAYBE_INFINITE_ITER),
++        LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS),
++        LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS),
++        LintId::of(&literal_representation::LARGE_DIGIT_GROUPS),
++        LintId::of(&literal_representation::UNREADABLE_LITERAL),
++        LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP),
++        LintId::of(&loops::EXPLICIT_ITER_LOOP),
++        LintId::of(&macro_use::MACRO_USE_IMPORTS),
++        LintId::of(&matches::MATCH_BOOL),
++        LintId::of(&matches::SINGLE_MATCH_ELSE),
++        LintId::of(&methods::FILTER_MAP),
++        LintId::of(&methods::FILTER_MAP_NEXT),
++        LintId::of(&methods::FIND_MAP),
++        LintId::of(&methods::INEFFICIENT_TO_STRING),
++        LintId::of(&methods::MAP_FLATTEN),
++        LintId::of(&methods::OPTION_MAP_UNWRAP_OR),
++        LintId::of(&methods::OPTION_MAP_UNWRAP_OR_ELSE),
++        LintId::of(&methods::RESULT_MAP_UNWRAP_OR_ELSE),
++        LintId::of(&misc::USED_UNDERSCORE_BINDING),
++        LintId::of(&misc_early::UNSEPARATED_LITERAL_SUFFIX),
++        LintId::of(&mut_mut::MUT_MUT),
++        LintId::of(&needless_continue::NEEDLESS_CONTINUE),
++        LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
++        LintId::of(&non_expressive_names::SIMILAR_NAMES),
++        LintId::of(&ranges::RANGE_PLUS_ONE),
++        LintId::of(&shadow::SHADOW_UNRELATED),
++        LintId::of(&strings::STRING_ADD_ASSIGN),
++        LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS),
++        LintId::of(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF),
++        LintId::of(&types::CAST_LOSSLESS),
++        LintId::of(&types::CAST_POSSIBLE_TRUNCATION),
++        LintId::of(&types::CAST_POSSIBLE_WRAP),
++        LintId::of(&types::CAST_PRECISION_LOSS),
++        LintId::of(&types::CAST_SIGN_LOSS),
++        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(&unused_self::UNUSED_SELF),
++        LintId::of(&wildcard_imports::ENUM_GLOB_USE),
++        LintId::of(&wildcard_imports::WILDCARD_IMPORTS),
++    ]);
++
++    store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![
++        LintId::of(&utils::internal_lints::CLIPPY_LINTS_INTERNAL),
++        LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
++        LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS),
++        LintId::of(&utils::internal_lints::DEFAULT_LINT),
++        LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS),
++        LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA),
++        LintId::of(&utils::internal_lints::PRODUCE_ICE),
++    ]);
++
++    store.register_group(true, "clippy::all", Some("clippy"), vec![
++        LintId::of(&approx_const::APPROX_CONSTANT),
++        LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
++        LintId::of(&assign_ops::ASSIGN_OP_PATTERN),
++        LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP),
++        LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING),
++        LintId::of(&attrs::DEPRECATED_CFG_ATTR),
++        LintId::of(&attrs::DEPRECATED_SEMVER),
++        LintId::of(&attrs::MISMATCHED_TARGET_OS),
++        LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
++        LintId::of(&attrs::USELESS_ATTRIBUTE),
++        LintId::of(&bit_mask::BAD_BIT_MASK),
++        LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK),
++        LintId::of(&bit_mask::VERBOSE_BIT_MASK),
++        LintId::of(&blacklisted_name::BLACKLISTED_NAME),
++        LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR),
++        LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT),
++        LintId::of(&booleans::LOGIC_BUG),
++        LintId::of(&booleans::NONMINIMAL_BOOL),
++        LintId::of(&bytecount::NAIVE_BYTECOUNT),
++        LintId::of(&collapsible_if::COLLAPSIBLE_IF),
++        LintId::of(&comparison_chain::COMPARISON_CHAIN),
++        LintId::of(&copies::IFS_SAME_COND),
++        LintId::of(&copies::IF_SAME_THEN_ELSE),
++        LintId::of(&derive::DERIVE_HASH_XOR_EQ),
++        LintId::of(&doc::MISSING_SAFETY_DOC),
++        LintId::of(&doc::NEEDLESS_DOCTEST_MAIN),
++        LintId::of(&double_comparison::DOUBLE_COMPARISONS),
++        LintId::of(&double_parens::DOUBLE_PARENS),
++        LintId::of(&drop_bounds::DROP_BOUNDS),
++        LintId::of(&drop_forget_ref::DROP_COPY),
++        LintId::of(&drop_forget_ref::DROP_REF),
++        LintId::of(&drop_forget_ref::FORGET_COPY),
++        LintId::of(&drop_forget_ref::FORGET_REF),
++        LintId::of(&duration_subsec::DURATION_SUBSEC),
++        LintId::of(&entry::MAP_ENTRY),
++        LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
++        LintId::of(&enum_variants::ENUM_VARIANT_NAMES),
++        LintId::of(&enum_variants::MODULE_INCEPTION),
++        LintId::of(&eq_op::EQ_OP),
++        LintId::of(&eq_op::OP_REF),
++        LintId::of(&erasing_op::ERASING_OP),
++        LintId::of(&escape::BOXED_LOCAL),
++        LintId::of(&eta_reduction::REDUNDANT_CLOSURE),
++        LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION),
++        LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE),
++        LintId::of(&explicit_write::EXPLICIT_WRITE),
++        LintId::of(&float_literal::EXCESSIVE_PRECISION),
++        LintId::of(&format::USELESS_FORMAT),
++        LintId::of(&formatting::POSSIBLE_MISSING_COMMA),
++        LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
++        LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),
++        LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
++        LintId::of(&functions::DOUBLE_MUST_USE),
++        LintId::of(&functions::MUST_USE_UNIT),
++        LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF),
++        LintId::of(&functions::TOO_MANY_ARGUMENTS),
++        LintId::of(&get_last_with_len::GET_LAST_WITH_LEN),
++        LintId::of(&identity_conversion::IDENTITY_CONVERSION),
++        LintId::of(&identity_op::IDENTITY_OP),
++        LintId::of(&if_let_mutex::IF_LET_MUTEX),
++        LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
++        LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING),
++        LintId::of(&infinite_iter::INFINITE_ITER),
++        LintId::of(&inherent_to_string::INHERENT_TO_STRING),
++        LintId::of(&inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
++        LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
++        LintId::of(&int_plus_one::INT_PLUS_ONE),
++        LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS),
++        LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT),
++        LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
++        LintId::of(&len_zero::LEN_ZERO),
++        LintId::of(&let_if_seq::USELESS_LET_IF_SEQ),
++        LintId::of(&let_underscore::LET_UNDERSCORE_LOCK),
++        LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES),
++        LintId::of(&lifetimes::NEEDLESS_LIFETIMES),
++        LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
++        LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES),
++        LintId::of(&loops::EMPTY_LOOP),
++        LintId::of(&loops::EXPLICIT_COUNTER_LOOP),
++        LintId::of(&loops::FOR_KV_MAP),
++        LintId::of(&loops::FOR_LOOP_OVER_OPTION),
++        LintId::of(&loops::FOR_LOOP_OVER_RESULT),
++        LintId::of(&loops::ITER_NEXT_LOOP),
++        LintId::of(&loops::MANUAL_MEMCPY),
++        LintId::of(&loops::MUT_RANGE_BOUND),
++        LintId::of(&loops::NEEDLESS_COLLECT),
++        LintId::of(&loops::NEEDLESS_RANGE_LOOP),
++        LintId::of(&loops::NEVER_LOOP),
++        LintId::of(&loops::REVERSE_RANGE_LOOP),
++        LintId::of(&loops::WHILE_IMMUTABLE_CONDITION),
++        LintId::of(&loops::WHILE_LET_LOOP),
++        LintId::of(&loops::WHILE_LET_ON_ITERATOR),
++        LintId::of(&main_recursion::MAIN_RECURSION),
++        LintId::of(&map_clone::MAP_CLONE),
++        LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
++        LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
++        LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
++        LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
++        LintId::of(&matches::MATCH_AS_REF),
++        LintId::of(&matches::MATCH_OVERLAPPING_ARM),
++        LintId::of(&matches::MATCH_REF_PATS),
++        LintId::of(&matches::MATCH_SINGLE_BINDING),
++        LintId::of(&matches::MATCH_WILD_ERR_ARM),
++        LintId::of(&matches::SINGLE_MATCH),
++        LintId::of(&matches::WILDCARD_IN_OR_PATTERNS),
++        LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM),
++        LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
++        LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT),
++        LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT),
++        LintId::of(&methods::CHARS_LAST_CMP),
++        LintId::of(&methods::CHARS_NEXT_CMP),
++        LintId::of(&methods::CLONE_DOUBLE_REF),
++        LintId::of(&methods::CLONE_ON_COPY),
++        LintId::of(&methods::EXPECT_FUN_CALL),
++        LintId::of(&methods::FILTER_NEXT),
++        LintId::of(&methods::FLAT_MAP_IDENTITY),
++        LintId::of(&methods::INTO_ITER_ON_REF),
++        LintId::of(&methods::ITERATOR_STEP_BY_ZERO),
++        LintId::of(&methods::ITER_CLONED_COLLECT),
++        LintId::of(&methods::ITER_NTH),
++        LintId::of(&methods::ITER_NTH_ZERO),
++        LintId::of(&methods::ITER_SKIP_NEXT),
++        LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
++        LintId::of(&methods::NEW_RET_NO_SELF),
++        LintId::of(&methods::OK_EXPECT),
++        LintId::of(&methods::OPTION_AND_THEN_SOME),
++        LintId::of(&methods::OPTION_AS_REF_DEREF),
++        LintId::of(&methods::OPTION_MAP_OR_NONE),
++        LintId::of(&methods::OR_FUN_CALL),
++        LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION),
++        LintId::of(&methods::SEARCH_IS_SOME),
++        LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT),
++        LintId::of(&methods::SINGLE_CHAR_PATTERN),
++        LintId::of(&methods::SKIP_WHILE_NEXT),
++        LintId::of(&methods::STRING_EXTEND_CHARS),
++        LintId::of(&methods::SUSPICIOUS_MAP),
++        LintId::of(&methods::TEMPORARY_CSTRING_AS_PTR),
++        LintId::of(&methods::UNINIT_ASSUMED_INIT),
++        LintId::of(&methods::UNNECESSARY_FILTER_MAP),
++        LintId::of(&methods::UNNECESSARY_FOLD),
++        LintId::of(&methods::USELESS_ASREF),
++        LintId::of(&methods::WRONG_SELF_CONVENTION),
++        LintId::of(&methods::ZST_OFFSET),
++        LintId::of(&minmax::MIN_MAX),
++        LintId::of(&misc::CMP_NAN),
++        LintId::of(&misc::CMP_OWNED),
++        LintId::of(&misc::FLOAT_CMP),
++        LintId::of(&misc::MODULO_ONE),
++        LintId::of(&misc::SHORT_CIRCUIT_STATEMENT),
++        LintId::of(&misc::TOPLEVEL_REF_ARG),
++        LintId::of(&misc::ZERO_PTR),
++        LintId::of(&misc_early::BUILTIN_TYPE_SHADOW),
++        LintId::of(&misc_early::DOUBLE_NEG),
++        LintId::of(&misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
++        LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS),
++        LintId::of(&misc_early::REDUNDANT_CLOSURE_CALL),
++        LintId::of(&misc_early::REDUNDANT_PATTERN),
++        LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN),
++        LintId::of(&misc_early::ZERO_PREFIXED_LITERAL),
++        LintId::of(&mut_key::MUTABLE_KEY_TYPE),
++        LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED),
++        LintId::of(&mutex_atomic::MUTEX_ATOMIC),
++        LintId::of(&needless_bool::BOOL_COMPARISON),
++        LintId::of(&needless_bool::NEEDLESS_BOOL),
++        LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
++        LintId::of(&needless_update::NEEDLESS_UPDATE),
++        LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
++        LintId::of(&neg_multiply::NEG_MULTIPLY),
++        LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT),
++        LintId::of(&no_effect::NO_EFFECT),
++        LintId::of(&no_effect::UNNECESSARY_OPERATION),
++        LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
++        LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
++        LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
++        LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES),
++        LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS),
++        LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP),
++        LintId::of(&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
++        LintId::of(&panic_unimplemented::PANIC_PARAMS),
++        LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL),
++        LintId::of(&precedence::PRECEDENCE),
++        LintId::of(&ptr::CMP_NULL),
++        LintId::of(&ptr::MUT_FROM_REF),
++        LintId::of(&ptr::PTR_ARG),
++        LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
++        LintId::of(&question_mark::QUESTION_MARK),
++        LintId::of(&ranges::RANGE_MINUS_ONE),
++        LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
++        LintId::of(&redundant_clone::REDUNDANT_CLONE),
++        LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
++        LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING),
++        LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
++        LintId::of(&reference::DEREF_ADDROF),
++        LintId::of(&reference::REF_IN_DEREF),
++        LintId::of(&regex::INVALID_REGEX),
++        LintId::of(&regex::REGEX_MACRO),
++        LintId::of(&regex::TRIVIAL_REGEX),
++        LintId::of(&returns::LET_AND_RETURN),
++        LintId::of(&returns::NEEDLESS_RETURN),
++        LintId::of(&returns::UNUSED_UNIT),
++        LintId::of(&serde_api::SERDE_API_MISUSE),
++        LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
++        LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
++        LintId::of(&strings::STRING_LIT_AS_BYTES),
++        LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
++        LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
++        LintId::of(&swap::ALMOST_SWAPPED),
++        LintId::of(&swap::MANUAL_SWAP),
++        LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
++        LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT),
++        LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME),
++        LintId::of(&transmute::CROSSPOINTER_TRANSMUTE),
++        LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR),
++        LintId::of(&transmute::TRANSMUTE_FLOAT_TO_INT),
++        LintId::of(&transmute::TRANSMUTE_INT_TO_BOOL),
++        LintId::of(&transmute::TRANSMUTE_INT_TO_CHAR),
++        LintId::of(&transmute::TRANSMUTE_INT_TO_FLOAT),
++        LintId::of(&transmute::TRANSMUTE_PTR_TO_PTR),
++        LintId::of(&transmute::TRANSMUTE_PTR_TO_REF),
++        LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE),
++        LintId::of(&transmute::WRONG_TRANSMUTE),
++        LintId::of(&transmuting_null::TRANSMUTING_NULL),
++        LintId::of(&try_err::TRY_ERR),
++        LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
++        LintId::of(&types::BORROWED_BOX),
++        LintId::of(&types::BOX_VEC),
++        LintId::of(&types::CAST_PTR_ALIGNMENT),
++        LintId::of(&types::CAST_REF_TO_MUT),
++        LintId::of(&types::CHAR_LIT_AS_U8),
++        LintId::of(&types::FN_TO_NUMERIC_CAST),
++        LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
++        LintId::of(&types::REDUNDANT_ALLOCATION),
++        LintId::of(&types::TYPE_COMPLEXITY),
++        LintId::of(&types::UNIT_ARG),
++        LintId::of(&types::UNIT_CMP),
++        LintId::of(&types::UNNECESSARY_CAST),
++        LintId::of(&types::VEC_BOX),
++        LintId::of(&unicode::ZERO_WIDTH_SPACE),
++        LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
++        LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
++        LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
++        LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT),
++        LintId::of(&unwrap::PANICKING_UNWRAP),
++        LintId::of(&unwrap::UNNECESSARY_UNWRAP),
++        LintId::of(&vec::USELESS_VEC),
++        LintId::of(&write::PRINTLN_EMPTY_STRING),
++        LintId::of(&write::PRINT_LITERAL),
++        LintId::of(&write::PRINT_WITH_NEWLINE),
++        LintId::of(&write::WRITELN_EMPTY_STRING),
++        LintId::of(&write::WRITE_LITERAL),
++        LintId::of(&write::WRITE_WITH_NEWLINE),
++        LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO),
++    ]);
++
++    store.register_group(true, "clippy::style", Some("clippy_style"), vec![
++        LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
++        LintId::of(&assign_ops::ASSIGN_OP_PATTERN),
++        LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
++        LintId::of(&bit_mask::VERBOSE_BIT_MASK),
++        LintId::of(&blacklisted_name::BLACKLISTED_NAME),
++        LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR),
++        LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT),
++        LintId::of(&collapsible_if::COLLAPSIBLE_IF),
++        LintId::of(&comparison_chain::COMPARISON_CHAIN),
++        LintId::of(&doc::MISSING_SAFETY_DOC),
++        LintId::of(&doc::NEEDLESS_DOCTEST_MAIN),
++        LintId::of(&enum_variants::ENUM_VARIANT_NAMES),
++        LintId::of(&enum_variants::MODULE_INCEPTION),
++        LintId::of(&eq_op::OP_REF),
++        LintId::of(&eta_reduction::REDUNDANT_CLOSURE),
++        LintId::of(&float_literal::EXCESSIVE_PRECISION),
++        LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
++        LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),
++        LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
++        LintId::of(&functions::DOUBLE_MUST_USE),
++        LintId::of(&functions::MUST_USE_UNIT),
++        LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
++        LintId::of(&inherent_to_string::INHERENT_TO_STRING),
++        LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
++        LintId::of(&len_zero::LEN_ZERO),
++        LintId::of(&let_if_seq::USELESS_LET_IF_SEQ),
++        LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
++        LintId::of(&loops::EMPTY_LOOP),
++        LintId::of(&loops::FOR_KV_MAP),
++        LintId::of(&loops::NEEDLESS_RANGE_LOOP),
++        LintId::of(&loops::WHILE_LET_ON_ITERATOR),
++        LintId::of(&main_recursion::MAIN_RECURSION),
++        LintId::of(&map_clone::MAP_CLONE),
++        LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
++        LintId::of(&matches::MATCH_OVERLAPPING_ARM),
++        LintId::of(&matches::MATCH_REF_PATS),
++        LintId::of(&matches::MATCH_WILD_ERR_ARM),
++        LintId::of(&matches::SINGLE_MATCH),
++        LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
++        LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT),
++        LintId::of(&methods::CHARS_LAST_CMP),
++        LintId::of(&methods::CHARS_NEXT_CMP),
++        LintId::of(&methods::INTO_ITER_ON_REF),
++        LintId::of(&methods::ITER_CLONED_COLLECT),
++        LintId::of(&methods::ITER_NTH_ZERO),
++        LintId::of(&methods::ITER_SKIP_NEXT),
++        LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
++        LintId::of(&methods::NEW_RET_NO_SELF),
++        LintId::of(&methods::OK_EXPECT),
++        LintId::of(&methods::OPTION_MAP_OR_NONE),
++        LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION),
++        LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT),
++        LintId::of(&methods::STRING_EXTEND_CHARS),
++        LintId::of(&methods::UNNECESSARY_FOLD),
++        LintId::of(&methods::WRONG_SELF_CONVENTION),
++        LintId::of(&misc::TOPLEVEL_REF_ARG),
++        LintId::of(&misc::ZERO_PTR),
++        LintId::of(&misc_early::BUILTIN_TYPE_SHADOW),
++        LintId::of(&misc_early::DOUBLE_NEG),
++        LintId::of(&misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
++        LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS),
++        LintId::of(&misc_early::REDUNDANT_PATTERN),
++        LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED),
++        LintId::of(&neg_multiply::NEG_MULTIPLY),
++        LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT),
++        LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
++        LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES),
++        LintId::of(&panic_unimplemented::PANIC_PARAMS),
++        LintId::of(&ptr::CMP_NULL),
++        LintId::of(&ptr::PTR_ARG),
++        LintId::of(&question_mark::QUESTION_MARK),
++        LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
++        LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING),
++        LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
++        LintId::of(&regex::REGEX_MACRO),
++        LintId::of(&regex::TRIVIAL_REGEX),
++        LintId::of(&returns::LET_AND_RETURN),
++        LintId::of(&returns::NEEDLESS_RETURN),
++        LintId::of(&returns::UNUSED_UNIT),
++        LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
++        LintId::of(&strings::STRING_LIT_AS_BYTES),
++        LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
++        LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME),
++        LintId::of(&try_err::TRY_ERR),
++        LintId::of(&types::FN_TO_NUMERIC_CAST),
++        LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
++        LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
++        LintId::of(&write::PRINTLN_EMPTY_STRING),
++        LintId::of(&write::PRINT_LITERAL),
++        LintId::of(&write::PRINT_WITH_NEWLINE),
++        LintId::of(&write::WRITELN_EMPTY_STRING),
++        LintId::of(&write::WRITE_LITERAL),
++        LintId::of(&write::WRITE_WITH_NEWLINE),
++    ]);
++
++    store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![
++        LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP),
++        LintId::of(&attrs::DEPRECATED_CFG_ATTR),
++        LintId::of(&booleans::NONMINIMAL_BOOL),
++        LintId::of(&double_comparison::DOUBLE_COMPARISONS),
++        LintId::of(&double_parens::DOUBLE_PARENS),
++        LintId::of(&duration_subsec::DURATION_SUBSEC),
++        LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION),
++        LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE),
++        LintId::of(&explicit_write::EXPLICIT_WRITE),
++        LintId::of(&format::USELESS_FORMAT),
++        LintId::of(&functions::TOO_MANY_ARGUMENTS),
++        LintId::of(&get_last_with_len::GET_LAST_WITH_LEN),
++        LintId::of(&identity_conversion::IDENTITY_CONVERSION),
++        LintId::of(&identity_op::IDENTITY_OP),
++        LintId::of(&int_plus_one::INT_PLUS_ONE),
++        LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES),
++        LintId::of(&lifetimes::NEEDLESS_LIFETIMES),
++        LintId::of(&loops::EXPLICIT_COUNTER_LOOP),
++        LintId::of(&loops::MUT_RANGE_BOUND),
++        LintId::of(&loops::WHILE_LET_LOOP),
++        LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
++        LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
++        LintId::of(&matches::MATCH_AS_REF),
++        LintId::of(&matches::MATCH_SINGLE_BINDING),
++        LintId::of(&matches::WILDCARD_IN_OR_PATTERNS),
++        LintId::of(&methods::CLONE_ON_COPY),
++        LintId::of(&methods::FILTER_NEXT),
++        LintId::of(&methods::FLAT_MAP_IDENTITY),
++        LintId::of(&methods::OPTION_AND_THEN_SOME),
++        LintId::of(&methods::OPTION_AS_REF_DEREF),
++        LintId::of(&methods::SEARCH_IS_SOME),
++        LintId::of(&methods::SKIP_WHILE_NEXT),
++        LintId::of(&methods::SUSPICIOUS_MAP),
++        LintId::of(&methods::UNNECESSARY_FILTER_MAP),
++        LintId::of(&methods::USELESS_ASREF),
++        LintId::of(&misc::SHORT_CIRCUIT_STATEMENT),
++        LintId::of(&misc_early::REDUNDANT_CLOSURE_CALL),
++        LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN),
++        LintId::of(&misc_early::ZERO_PREFIXED_LITERAL),
++        LintId::of(&needless_bool::BOOL_COMPARISON),
++        LintId::of(&needless_bool::NEEDLESS_BOOL),
++        LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
++        LintId::of(&needless_update::NEEDLESS_UPDATE),
++        LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
++        LintId::of(&no_effect::NO_EFFECT),
++        LintId::of(&no_effect::UNNECESSARY_OPERATION),
++        LintId::of(&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
++        LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL),
++        LintId::of(&precedence::PRECEDENCE),
++        LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
++        LintId::of(&ranges::RANGE_MINUS_ONE),
++        LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
++        LintId::of(&reference::DEREF_ADDROF),
++        LintId::of(&reference::REF_IN_DEREF),
++        LintId::of(&swap::MANUAL_SWAP),
++        LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT),
++        LintId::of(&transmute::CROSSPOINTER_TRANSMUTE),
++        LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR),
++        LintId::of(&transmute::TRANSMUTE_FLOAT_TO_INT),
++        LintId::of(&transmute::TRANSMUTE_INT_TO_BOOL),
++        LintId::of(&transmute::TRANSMUTE_INT_TO_CHAR),
++        LintId::of(&transmute::TRANSMUTE_INT_TO_FLOAT),
++        LintId::of(&transmute::TRANSMUTE_PTR_TO_PTR),
++        LintId::of(&transmute::TRANSMUTE_PTR_TO_REF),
++        LintId::of(&types::BORROWED_BOX),
++        LintId::of(&types::CHAR_LIT_AS_U8),
++        LintId::of(&types::TYPE_COMPLEXITY),
++        LintId::of(&types::UNIT_ARG),
++        LintId::of(&types::UNNECESSARY_CAST),
++        LintId::of(&types::VEC_BOX),
++        LintId::of(&unwrap::UNNECESSARY_UNWRAP),
++        LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO),
++    ]);
++
++    store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![
++        LintId::of(&approx_const::APPROX_CONSTANT),
++        LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING),
++        LintId::of(&attrs::DEPRECATED_SEMVER),
++        LintId::of(&attrs::MISMATCHED_TARGET_OS),
++        LintId::of(&attrs::USELESS_ATTRIBUTE),
++        LintId::of(&bit_mask::BAD_BIT_MASK),
++        LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK),
++        LintId::of(&booleans::LOGIC_BUG),
++        LintId::of(&copies::IFS_SAME_COND),
++        LintId::of(&copies::IF_SAME_THEN_ELSE),
++        LintId::of(&derive::DERIVE_HASH_XOR_EQ),
++        LintId::of(&drop_bounds::DROP_BOUNDS),
++        LintId::of(&drop_forget_ref::DROP_COPY),
++        LintId::of(&drop_forget_ref::DROP_REF),
++        LintId::of(&drop_forget_ref::FORGET_COPY),
++        LintId::of(&drop_forget_ref::FORGET_REF),
++        LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
++        LintId::of(&eq_op::EQ_OP),
++        LintId::of(&erasing_op::ERASING_OP),
++        LintId::of(&formatting::POSSIBLE_MISSING_COMMA),
++        LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF),
++        LintId::of(&if_let_mutex::IF_LET_MUTEX),
++        LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING),
++        LintId::of(&infinite_iter::INFINITE_ITER),
++        LintId::of(&inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
++        LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
++        LintId::of(&let_underscore::LET_UNDERSCORE_LOCK),
++        LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES),
++        LintId::of(&loops::FOR_LOOP_OVER_OPTION),
++        LintId::of(&loops::FOR_LOOP_OVER_RESULT),
++        LintId::of(&loops::ITER_NEXT_LOOP),
++        LintId::of(&loops::NEVER_LOOP),
++        LintId::of(&loops::REVERSE_RANGE_LOOP),
++        LintId::of(&loops::WHILE_IMMUTABLE_CONDITION),
++        LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
++        LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM),
++        LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT),
++        LintId::of(&methods::CLONE_DOUBLE_REF),
++        LintId::of(&methods::ITERATOR_STEP_BY_ZERO),
++        LintId::of(&methods::TEMPORARY_CSTRING_AS_PTR),
++        LintId::of(&methods::UNINIT_ASSUMED_INIT),
++        LintId::of(&methods::ZST_OFFSET),
++        LintId::of(&minmax::MIN_MAX),
++        LintId::of(&misc::CMP_NAN),
++        LintId::of(&misc::FLOAT_CMP),
++        LintId::of(&misc::MODULO_ONE),
++        LintId::of(&mut_key::MUTABLE_KEY_TYPE),
++        LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
++        LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
++        LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS),
++        LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP),
++        LintId::of(&ptr::MUT_FROM_REF),
++        LintId::of(&regex::INVALID_REGEX),
++        LintId::of(&serde_api::SERDE_API_MISUSE),
++        LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
++        LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
++        LintId::of(&swap::ALMOST_SWAPPED),
++        LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE),
++        LintId::of(&transmute::WRONG_TRANSMUTE),
++        LintId::of(&transmuting_null::TRANSMUTING_NULL),
++        LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
++        LintId::of(&types::CAST_PTR_ALIGNMENT),
++        LintId::of(&types::CAST_REF_TO_MUT),
++        LintId::of(&types::UNIT_CMP),
++        LintId::of(&unicode::ZERO_WIDTH_SPACE),
++        LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
++        LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
++        LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT),
++        LintId::of(&unwrap::PANICKING_UNWRAP),
++    ]);
++
++    store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
++        LintId::of(&bytecount::NAIVE_BYTECOUNT),
++        LintId::of(&entry::MAP_ENTRY),
++        LintId::of(&escape::BOXED_LOCAL),
++        LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS),
++        LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT),
++        LintId::of(&loops::MANUAL_MEMCPY),
++        LintId::of(&loops::NEEDLESS_COLLECT),
++        LintId::of(&methods::EXPECT_FUN_CALL),
++        LintId::of(&methods::ITER_NTH),
++        LintId::of(&methods::OR_FUN_CALL),
++        LintId::of(&methods::SINGLE_CHAR_PATTERN),
++        LintId::of(&misc::CMP_OWNED),
++        LintId::of(&mutex_atomic::MUTEX_ATOMIC),
++        LintId::of(&redundant_clone::REDUNDANT_CLONE),
++        LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
++        LintId::of(&types::BOX_VEC),
++        LintId::of(&types::REDUNDANT_ALLOCATION),
++        LintId::of(&vec::USELESS_VEC),
++    ]);
++
++    store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![
++        LintId::of(&cargo_common_metadata::CARGO_COMMON_METADATA),
++        LintId::of(&multiple_crate_versions::MULTIPLE_CRATE_VERSIONS),
++        LintId::of(&wildcard_dependencies::WILDCARD_DEPENDENCIES),
++    ]);
++
++    store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
++        LintId::of(&attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
++        LintId::of(&cognitive_complexity::COGNITIVE_COMPLEXITY),
++        LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM),
++        LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS),
++        LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS),
++        LintId::of(&future_not_send::FUTURE_NOT_SEND),
++        LintId::of(&missing_const_for_fn::MISSING_CONST_FOR_FN),
++        LintId::of(&mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
++        LintId::of(&mutex_atomic::MUTEX_INTEGER),
++        LintId::of(&needless_borrow::NEEDLESS_BORROW),
++        LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
++        LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE),
++        LintId::of(&transmute::USELESS_TRANSMUTE),
++        LintId::of(&use_self::USE_SELF),
++    ]);
++}
++
++#[rustfmt::skip]
++fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) {
++    store.register_removed(
++        "should_assert_eq",
++        "`assert!()` will be more flexible with RFC 2011",
++    );
++    store.register_removed(
++        "extend_from_slice",
++        "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice",
++    );
++    store.register_removed(
++        "range_step_by_zero",
++        "`iterator.step_by(0)` panics nowadays",
++    );
++    store.register_removed(
++        "unstable_as_slice",
++        "`Vec::as_slice` has been stabilized in 1.7",
++    );
++    store.register_removed(
++        "unstable_as_mut_slice",
++        "`Vec::as_mut_slice` has been stabilized in 1.7",
++    );
++    store.register_removed(
++        "str_to_string",
++        "using `str::to_string` is common even today and specialization will likely happen soon",
++    );
++    store.register_removed(
++        "string_to_string",
++        "using `string::to_string` is common even today and specialization will likely happen soon",
++    );
++    store.register_removed(
++        "misaligned_transmute",
++        "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
++    );
++    store.register_removed(
++        "assign_ops",
++        "using compound assignment operators (e.g., `+=`) is harmless",
++    );
++    store.register_removed(
++        "if_let_redundant_pattern_matching",
++        "this lint has been changed to redundant_pattern_matching",
++    );
++    store.register_removed(
++        "unsafe_vector_initialization",
++        "the replacement suggested by this lint had substantially different behavior",
++    );
++}
++
++/// Register renamed lints.
++///
++/// Used in `./src/driver.rs`.
++pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
++    ls.register_renamed("clippy::stutter", "clippy::module_name_repetitions");
++    ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default");
++    ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity");
++    ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes");
++}
++
++// only exists to let the dogfood integration test works.
++// Don't run clippy as an executable directly
++#[allow(dead_code)]
++fn main() {
++    panic!("Please use the cargo-clippy executable");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d80ad47ab2468d5f6c9f4f0d3a41ee33ee479861
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,526 @@@
++use rustc_data_structures::fx::{FxHashMap, FxHashSet};
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::intravisit::{
++    walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_ty, NestedVisitorMap, Visitor,
++};
++use rustc_hir::FnRetTy::Return;
++use rustc_hir::{
++    BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item,
++    ItemKind, Lifetime, LifetimeName, ParamName, QPath, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty,
++    TyKind, WhereClause, WherePredicate,
++};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++use rustc_span::symbol::kw;
++
++use crate::reexport::Name;
++use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for lifetime annotations which can be removed by
++    /// relying on lifetime elision.
++    ///
++    /// **Why is this bad?** The additional lifetimes make the code look more
++    /// complicated, while there is nothing out of the ordinary going on. Removing
++    /// them leads to more readable code.
++    ///
++    /// **Known problems:** Potential false negatives: we bail out if the function
++    /// has a `where` clause where lifetimes are mentioned.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// // Bad: unnecessary lifetime annotations
++    /// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {
++    ///     x
++    /// }
++    ///
++    /// // Good
++    /// fn elided(x: &u8, y: u8) -> &u8 {
++    ///     x
++    /// }
++    /// ```
++    pub NEEDLESS_LIFETIMES,
++    complexity,
++    "using explicit lifetimes for references in function arguments when elision rules \
++     would allow omitting them"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for lifetimes in generics that are never used
++    /// anywhere else.
++    ///
++    /// **Why is this bad?** The additional lifetimes make the code look more
++    /// complicated, while there is nothing out of the ordinary going on. Removing
++    /// them leads to more readable code.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// // Bad: unnecessary lifetimes
++    /// fn unused_lifetime<'a>(x: u8) {
++    ///     // ..
++    /// }
++    ///
++    /// // Good
++    /// fn no_lifetime(x: u8) {
++    ///     // ...
++    /// }
++    /// ```
++    pub EXTRA_UNUSED_LIFETIMES,
++    complexity,
++    "unused lifetimes in function definitions"
++}
++
++declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Lifetimes {
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++        if let ItemKind::Fn(ref sig, ref generics, id) = item.kind {
++            check_fn_inner(cx, &sig.decl, Some(id), generics, item.span, true);
++        }
++    }
++
++    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem<'_>) {
++        if let ImplItemKind::Fn(ref sig, id) = item.kind {
++            let report_extra_lifetimes = trait_ref_of_method(cx, item.hir_id).is_none();
++            check_fn_inner(
++                cx,
++                &sig.decl,
++                Some(id),
++                &item.generics,
++                item.span,
++                report_extra_lifetimes,
++            );
++        }
++    }
++
++    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) {
++        if let TraitItemKind::Fn(ref sig, ref body) = item.kind {
++            let body = match *body {
++                TraitFn::Required(_) => None,
++                TraitFn::Provided(id) => Some(id),
++            };
++            check_fn_inner(cx, &sig.decl, body, &item.generics, item.span, true);
++        }
++    }
++}
++
++/// The lifetime of a &-reference.
++#[derive(PartialEq, Eq, Hash, Debug)]
++enum RefLt {
++    Unnamed,
++    Static,
++    Named(Name),
++}
++
++fn check_fn_inner<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    decl: &'tcx FnDecl<'_>,
++    body: Option<BodyId>,
++    generics: &'tcx Generics<'_>,
++    span: Span,
++    report_extra_lifetimes: bool,
++) {
++    if in_macro(span) || has_where_lifetimes(cx, &generics.where_clause) {
++        return;
++    }
++
++    let mut bounds_lts = Vec::new();
++    let types = generics.params.iter().filter(|param| match param.kind {
++        GenericParamKind::Type { .. } => true,
++        _ => false,
++    });
++    for typ in types {
++        for bound in typ.bounds {
++            let mut visitor = RefVisitor::new(cx);
++            walk_param_bound(&mut visitor, bound);
++            if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) {
++                return;
++            }
++            if let GenericBound::Trait(ref trait_ref, _) = *bound {
++                let params = &trait_ref
++                    .trait_ref
++                    .path
++                    .segments
++                    .last()
++                    .expect("a path must have at least one segment")
++                    .args;
++                if let Some(ref params) = *params {
++                    let lifetimes = params.args.iter().filter_map(|arg| match arg {
++                        GenericArg::Lifetime(lt) => Some(lt),
++                        _ => None,
++                    });
++                    for bound in lifetimes {
++                        if bound.name != LifetimeName::Static && !bound.is_elided() {
++                            return;
++                        }
++                        bounds_lts.push(bound);
++                    }
++                }
++            }
++        }
++    }
++    if could_use_elision(cx, decl, body, &generics.params, bounds_lts) {
++        span_lint(
++            cx,
++            NEEDLESS_LIFETIMES,
++            span.with_hi(decl.output.span().hi()),
++            "explicit lifetimes given in parameter types where they could be elided \
++             (or replaced with `'_` if needed by type declaration)",
++        );
++    }
++    if report_extra_lifetimes {
++        self::report_extra_lifetimes(cx, decl, generics);
++    }
++}
++
++fn could_use_elision<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    func: &'tcx FnDecl<'_>,
++    body: Option<BodyId>,
++    named_generics: &'tcx [GenericParam<'_>],
++    bounds_lts: Vec<&'tcx Lifetime>,
++) -> bool {
++    // There are two scenarios where elision works:
++    // * no output references, all input references have different LT
++    // * output references, exactly one input reference with same LT
++    // All lifetimes must be unnamed, 'static or defined without bounds on the
++    // level of the current item.
++
++    // check named LTs
++    let allowed_lts = allowed_lts_from(named_generics);
++
++    // these will collect all the lifetimes for references in arg/return types
++    let mut input_visitor = RefVisitor::new(cx);
++    let mut output_visitor = RefVisitor::new(cx);
++
++    // extract lifetimes in input argument types
++    for arg in func.inputs {
++        input_visitor.visit_ty(arg);
++    }
++    // extract lifetimes in output type
++    if let Return(ref ty) = func.output {
++        output_visitor.visit_ty(ty);
++    }
++
++    let input_lts = match input_visitor.into_vec() {
++        Some(lts) => lts_from_bounds(lts, bounds_lts.into_iter()),
++        None => return false,
++    };
++    let output_lts = match output_visitor.into_vec() {
++        Some(val) => val,
++        None => return false,
++    };
++
++    if let Some(body_id) = body {
++        let mut checker = BodyLifetimeChecker {
++            lifetimes_used_in_body: false,
++        };
++        checker.visit_expr(&cx.tcx.hir().body(body_id).value);
++        if checker.lifetimes_used_in_body {
++            return false;
++        }
++    }
++
++    // check for lifetimes from higher scopes
++    for lt in input_lts.iter().chain(output_lts.iter()) {
++        if !allowed_lts.contains(lt) {
++            return false;
++        }
++    }
++
++    // no input lifetimes? easy case!
++    if input_lts.is_empty() {
++        false
++    } else if output_lts.is_empty() {
++        // no output lifetimes, check distinctness of input lifetimes
++
++        // only unnamed and static, ok
++        let unnamed_and_static = input_lts.iter().all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static);
++        if unnamed_and_static {
++            return false;
++        }
++        // we have no output reference, so we only need all distinct lifetimes
++        input_lts.len() == unique_lifetimes(&input_lts)
++    } else {
++        // we have output references, so we need one input reference,
++        // and all output lifetimes must be the same
++        if unique_lifetimes(&output_lts) > 1 {
++            return false;
++        }
++        if input_lts.len() == 1 {
++            match (&input_lts[0], &output_lts[0]) {
++                (&RefLt::Named(n1), &RefLt::Named(n2)) if n1 == n2 => true,
++                (&RefLt::Named(_), &RefLt::Unnamed) => true,
++                _ => false, /* already elided, different named lifetimes
++                             * or something static going on */
++            }
++        } else {
++            false
++        }
++    }
++}
++
++fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
++    let mut allowed_lts = FxHashSet::default();
++    for par in named_generics.iter() {
++        if let GenericParamKind::Lifetime { .. } = par.kind {
++            if par.bounds.is_empty() {
++                allowed_lts.insert(RefLt::Named(par.name.ident().name));
++            }
++        }
++    }
++    allowed_lts.insert(RefLt::Unnamed);
++    allowed_lts.insert(RefLt::Static);
++    allowed_lts
++}
++
++fn lts_from_bounds<'a, T: Iterator<Item = &'a Lifetime>>(mut vec: Vec<RefLt>, bounds_lts: T) -> Vec<RefLt> {
++    for lt in bounds_lts {
++        if lt.name != LifetimeName::Static {
++            vec.push(RefLt::Named(lt.name.ident().name));
++        }
++    }
++
++    vec
++}
++
++/// Number of unique lifetimes in the given vector.
++#[must_use]
++fn unique_lifetimes(lts: &[RefLt]) -> usize {
++    lts.iter().collect::<FxHashSet<_>>().len()
++}
++
++/// A visitor usable for `rustc_front::visit::walk_ty()`.
++struct RefVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    lts: Vec<RefLt>,
++    abort: bool,
++}
++
++impl<'v, 't> RefVisitor<'v, 't> {
++    fn new(cx: &'v LateContext<'v, 't>) -> Self {
++        Self {
++            cx,
++            lts: Vec::new(),
++            abort: false,
++        }
++    }
++
++    fn record(&mut self, lifetime: &Option<Lifetime>) {
++        if let Some(ref lt) = *lifetime {
++            if lt.name == LifetimeName::Static {
++                self.lts.push(RefLt::Static);
++            } else if let LifetimeName::Param(ParamName::Fresh(_)) = lt.name {
++                // Fresh lifetimes generated should be ignored.
++            } else if lt.is_elided() {
++                self.lts.push(RefLt::Unnamed);
++            } else {
++                self.lts.push(RefLt::Named(lt.name.ident().name));
++            }
++        } else {
++            self.lts.push(RefLt::Unnamed);
++        }
++    }
++
++    fn into_vec(self) -> Option<Vec<RefLt>> {
++        if self.abort {
++            None
++        } else {
++            Some(self.lts)
++        }
++    }
++
++    fn collect_anonymous_lifetimes(&mut self, qpath: &QPath<'_>, ty: &Ty<'_>) {
++        if let Some(ref last_path_segment) = last_path_segment(qpath).args {
++            if !last_path_segment.parenthesized
++                && !last_path_segment.args.iter().any(|arg| match arg {
++                    GenericArg::Lifetime(_) => true,
++                    _ => false,
++                })
++            {
++                let hir_id = ty.hir_id;
++                match self.cx.tables.qpath_res(qpath, hir_id) {
++                    Res::Def(DefKind::TyAlias | DefKind::Struct, def_id) => {
++                        let generics = self.cx.tcx.generics_of(def_id);
++                        for _ in generics.params.as_slice() {
++                            self.record(&None);
++                        }
++                    },
++                    Res::Def(DefKind::Trait, def_id) => {
++                        let trait_def = self.cx.tcx.trait_def(def_id);
++                        for _ in &self.cx.tcx.generics_of(trait_def.def_id).params {
++                            self.record(&None);
++                        }
++                    },
++                    _ => (),
++                }
++            }
++        }
++    }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    // for lifetimes as parameters of generics
++    fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
++        self.record(&Some(*lifetime));
++    }
++
++    fn visit_ty(&mut self, ty: &'tcx Ty<'_>) {
++        match ty.kind {
++            TyKind::Rptr(ref lt, _) if lt.is_elided() => {
++                self.record(&None);
++            },
++            TyKind::Path(ref path) => {
++                self.collect_anonymous_lifetimes(path, ty);
++            },
++            TyKind::Def(item, _) => {
++                let map = self.cx.tcx.hir();
++                if let ItemKind::OpaqueTy(ref exist_ty) = map.expect_item(item.id).kind {
++                    for bound in exist_ty.bounds {
++                        if let GenericBound::Outlives(_) = *bound {
++                            self.record(&None);
++                        }
++                    }
++                } else {
++                    unreachable!()
++                }
++                walk_ty(self, ty);
++            },
++            TyKind::TraitObject(bounds, ref lt) => {
++                if !lt.is_elided() {
++                    self.abort = true;
++                }
++                for bound in bounds {
++                    self.visit_poly_trait_ref(bound, TraitBoundModifier::None);
++                }
++                return;
++            },
++            _ => (),
++        }
++        walk_ty(self, ty);
++    }
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++/// Are any lifetimes mentioned in the `where` clause? If so, we don't try to
++/// reason about elision.
++fn has_where_lifetimes<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, where_clause: &'tcx WhereClause<'_>) -> bool {
++    for predicate in where_clause.predicates {
++        match *predicate {
++            WherePredicate::RegionPredicate(..) => return true,
++            WherePredicate::BoundPredicate(ref pred) => {
++                // a predicate like F: Trait or F: for<'a> Trait<'a>
++                let mut visitor = RefVisitor::new(cx);
++                // walk the type F, it may not contain LT refs
++                walk_ty(&mut visitor, &pred.bounded_ty);
++                if !visitor.lts.is_empty() {
++                    return true;
++                }
++                // if the bounds define new lifetimes, they are fine to occur
++                let allowed_lts = allowed_lts_from(&pred.bound_generic_params);
++                // now walk the bounds
++                for bound in pred.bounds.iter() {
++                    walk_param_bound(&mut visitor, bound);
++                }
++                // and check that all lifetimes are allowed
++                match visitor.into_vec() {
++                    None => return false,
++                    Some(lts) => {
++                        for lt in lts {
++                            if !allowed_lts.contains(&lt) {
++                                return true;
++                            }
++                        }
++                    },
++                }
++            },
++            WherePredicate::EqPredicate(ref pred) => {
++                let mut visitor = RefVisitor::new(cx);
++                walk_ty(&mut visitor, &pred.lhs_ty);
++                walk_ty(&mut visitor, &pred.rhs_ty);
++                if !visitor.lts.is_empty() {
++                    return true;
++                }
++            },
++        }
++    }
++    false
++}
++
++struct LifetimeChecker {
++    map: FxHashMap<Name, Span>,
++}
++
++impl<'tcx> Visitor<'tcx> for LifetimeChecker {
++    type Map = Map<'tcx>;
++
++    // for lifetimes as parameters of generics
++    fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
++        self.map.remove(&lifetime.name.ident().name);
++    }
++
++    fn visit_generic_param(&mut self, param: &'tcx GenericParam<'_>) {
++        // don't actually visit `<'a>` or `<'a: 'b>`
++        // we've already visited the `'a` declarations and
++        // don't want to spuriously remove them
++        // `'b` in `'a: 'b` is useless unless used elsewhere in
++        // a non-lifetime bound
++        if let GenericParamKind::Type { .. } = param.kind {
++            walk_generic_param(self, param)
++        }
++    }
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++fn report_extra_lifetimes<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) {
++    let hs = generics
++        .params
++        .iter()
++        .filter_map(|par| match par.kind {
++            GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)),
++            _ => None,
++        })
++        .collect();
++    let mut checker = LifetimeChecker { map: hs };
++
++    walk_generics(&mut checker, generics);
++    walk_fn_decl(&mut checker, func);
++
++    for &v in checker.map.values() {
++        span_lint(
++            cx,
++            EXTRA_UNUSED_LIFETIMES,
++            v,
++            "this lifetime isn't used in the function definition",
++        );
++    }
++}
++
++struct BodyLifetimeChecker {
++    lifetimes_used_in_body: bool,
++}
++
++impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker {
++    type Map = Map<'tcx>;
++
++    // for lifetimes as parameters of generics
++    fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
++        if lifetime.name.ident().name != kw::Invalid && lifetime.name.ident().name != kw::StaticLifetime {
++            self.lifetimes_used_in_body = true;
++        }
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ec7c4531ed7169c770ddb2ce0d17dae648d9ab60
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,430 @@@
++//! Lints concerned with the grouping of digits with underscores in integral or
++//! floating-point literal expressions.
++
++use crate::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};
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
++
++declare_clippy_lint! {
++    /// **What it does:** Warns if a long integral or floating-point constant does
++    /// not contain underscores.
++    ///
++    /// **Why is this bad?** Reading long numbers is difficult without separators.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// let x: u64 = 61864918973511;
++    /// ```
++    pub UNREADABLE_LITERAL,
++    pedantic,
++    "long integer literal without underscores"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Warns for mistyped suffix in literals
++    ///
++    /// **Why is this bad?** This is most probably a typo
++    ///
++    /// **Known problems:**
++    /// - Recommends a signed suffix, even though the number might be too big and an unsigned
++    ///   suffix is required
++    /// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// 2_32;
++    /// ```
++    pub MISTYPED_LITERAL_SUFFIXES,
++    correctness,
++    "mistyped literal suffix"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Warns if an integral or floating-point constant is
++    /// grouped inconsistently with underscores.
++    ///
++    /// **Why is this bad?** Readers may incorrectly interpret inconsistently
++    /// grouped digits.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// let x: u64 = 618_64_9189_73_511;
++    /// ```
++    pub INCONSISTENT_DIGIT_GROUPING,
++    style,
++    "integer literals with digits grouped inconsistently"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Warns if the digits of an integral or floating-point
++    /// constant are grouped into groups that
++    /// are too large.
++    ///
++    /// **Why is this bad?** Negatively impacts readability.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// let x: u64 = 6186491_8973511;
++    /// ```
++    pub LARGE_DIGIT_GROUPS,
++    pedantic,
++    "grouping digits into groups that are too large"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Warns if there is a better representation for a numeric literal.
++    ///
++    /// **Why is this bad?** Especially for big powers of 2 a hexadecimal representation is more
++    /// readable than a decimal representation.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// `255` => `0xFF`
++    /// `65_535` => `0xFFFF`
++    /// `4_042_322_160` => `0xF0F0_F0F0`
++    pub DECIMAL_LITERAL_REPRESENTATION,
++    restriction,
++    "using decimal representation when hexadecimal would be better"
++}
++
++enum WarningType {
++    UnreadableLiteral,
++    InconsistentDigitGrouping,
++    LargeDigitGroups,
++    DecimalRepresentation,
++    MistypedLiteralSuffix,
++}
++
++impl WarningType {
++    fn display(&self, suggested_format: String, cx: &EarlyContext<'_>, span: rustc_span::Span) {
++        match self {
++            Self::MistypedLiteralSuffix => span_lint_and_sugg(
++                cx,
++                MISTYPED_LITERAL_SUFFIXES,
++                span,
++                "mistyped literal suffix",
++                "did you mean to write",
++                suggested_format,
++                Applicability::MaybeIncorrect,
++            ),
++            Self::UnreadableLiteral => span_lint_and_sugg(
++                cx,
++                UNREADABLE_LITERAL,
++                span,
++                "long literal lacking separators",
++                "consider",
++                suggested_format,
++                Applicability::MachineApplicable,
++            ),
++            Self::LargeDigitGroups => span_lint_and_sugg(
++                cx,
++                LARGE_DIGIT_GROUPS,
++                span,
++                "digit groups should be smaller",
++                "consider",
++                suggested_format,
++                Applicability::MachineApplicable,
++            ),
++            Self::InconsistentDigitGrouping => span_lint_and_sugg(
++                cx,
++                INCONSISTENT_DIGIT_GROUPING,
++                span,
++                "digits grouped inconsistently by underscores",
++                "consider",
++                suggested_format,
++                Applicability::MachineApplicable,
++            ),
++            Self::DecimalRepresentation => span_lint_and_sugg(
++                cx,
++                DECIMAL_LITERAL_REPRESENTATION,
++                span,
++                "integer literal has a better hexadecimal representation",
++                "consider",
++                suggested_format,
++                Applicability::MachineApplicable,
++            ),
++        };
++    }
++}
++
++declare_lint_pass!(LiteralDigitGrouping => [
++    UNREADABLE_LITERAL,
++    INCONSISTENT_DIGIT_GROUPING,
++    LARGE_DIGIT_GROUPS,
++    MISTYPED_LITERAL_SUFFIXES,
++]);
++
++impl EarlyLintPass for LiteralDigitGrouping {
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++        if in_external_macro(cx.sess(), expr.span) {
++            return;
++        }
++
++        if let ExprKind::Lit(ref lit) = expr.kind {
++            Self::check_lit(cx, lit)
++        }
++    }
++}
++
++// Length of each UUID hyphenated group in hex digits.
++const UUID_GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12];
++
++impl LiteralDigitGrouping {
++    fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
++        if_chain! {
++            if let Some(src) = snippet_opt(cx, lit.span);
++            if let Some(mut num_lit) = NumericLiteral::from_lit(&src, &lit);
++            then {
++                if !Self::check_for_mistyped_suffix(cx, lit.span, &mut num_lit) {
++                    return;
++                }
++
++                if Self::is_literal_uuid_formatted(&mut num_lit) {
++                    return;
++                }
++
++                let result = (|| {
++
++                    let integral_group_size = Self::get_group_size(num_lit.integer.split('_'))?;
++                    if let Some(fraction) = num_lit.fraction {
++                        let fractional_group_size = Self::get_group_size(fraction.rsplit('_'))?;
++
++                        let consistent = Self::parts_consistent(integral_group_size,
++                                                                fractional_group_size,
++                                                                num_lit.integer.len(),
++                                                                fraction.len());
++                        if !consistent {
++                            return Err(WarningType::InconsistentDigitGrouping);
++                        };
++                    }
++                    Ok(())
++                })();
++
++
++                if let Err(warning_type) = result {
++                    let should_warn = match warning_type {
++                        | WarningType::UnreadableLiteral
++                        | WarningType::InconsistentDigitGrouping
++                        | WarningType::LargeDigitGroups => {
++                            !in_macro(lit.span)
++                        }
++                        WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => {
++                            true
++                        }
++                    };
++                    if should_warn {
++                        warning_type.display(num_lit.format(), cx, lit.span)
++                    }
++                }
++            }
++        }
++    }
++
++    // Returns `false` if the check fails
++    fn check_for_mistyped_suffix(
++        cx: &EarlyContext<'_>,
++        span: rustc_span::Span,
++        num_lit: &mut NumericLiteral<'_>,
++    ) -> bool {
++        if num_lit.suffix.is_some() {
++            return true;
++        }
++
++        let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent {
++            (exponent, &["32", "64"][..], 'f')
++        } else if let Some(fraction) = &mut num_lit.fraction {
++            (fraction, &["32", "64"][..], 'f')
++        } else {
++            (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i')
++        };
++
++        let mut split = part.rsplit('_');
++        let last_group = split.next().expect("At least one group");
++        if split.next().is_some() && mistyped_suffixes.contains(&last_group) {
++            *part = &part[..part.len() - last_group.len()];
++            let mut sugg = num_lit.format();
++            sugg.push('_');
++            sugg.push(missing_char);
++            sugg.push_str(last_group);
++            WarningType::MistypedLiteralSuffix.display(sugg, cx, span);
++            false
++        } else {
++            true
++        }
++    }
++
++    /// Checks whether the numeric literal matches the formatting of a UUID.
++    ///
++    /// Returns `true` if the radix is hexadecimal, and the groups match the
++    /// UUID format of 8-4-4-4-12.
++    fn is_literal_uuid_formatted(num_lit: &mut NumericLiteral<'_>) -> bool {
++        if num_lit.radix != Radix::Hexadecimal {
++            return false;
++        }
++
++        // UUIDs should not have a fraction
++        if num_lit.fraction.is_some() {
++            return false;
++        }
++
++        let group_sizes: Vec<usize> = num_lit.integer.split('_').map(str::len).collect();
++        if UUID_GROUP_LENS.len() == group_sizes.len() {
++            UUID_GROUP_LENS.iter().zip(&group_sizes).all(|(&a, &b)| a == b)
++        } else {
++            false
++        }
++    }
++
++    /// Given the sizes of the digit groups of both integral and fractional
++    /// parts, and the length
++    /// of both parts, determine if the digits have been grouped consistently.
++    #[must_use]
++    fn parts_consistent(
++        int_group_size: Option<usize>,
++        frac_group_size: Option<usize>,
++        int_size: usize,
++        frac_size: usize,
++    ) -> bool {
++        match (int_group_size, frac_group_size) {
++            // No groups on either side of decimal point - trivially consistent.
++            (None, None) => true,
++            // Integral part has grouped digits, fractional part does not.
++            (Some(int_group_size), None) => frac_size <= int_group_size,
++            // Fractional part has grouped digits, integral part does not.
++            (None, Some(frac_group_size)) => int_size <= frac_group_size,
++            // Both parts have grouped digits. Groups should be the same size.
++            (Some(int_group_size), Some(frac_group_size)) => int_group_size == frac_group_size,
++        }
++    }
++
++    /// Returns the size of the digit groups (or None if ungrouped) if successful,
++    /// otherwise returns a `WarningType` for linting.
++    fn get_group_size<'a>(groups: impl Iterator<Item = &'a str>) -> Result<Option<usize>, WarningType> {
++        let mut groups = groups.map(str::len);
++
++        let first = groups.next().expect("At least one group");
++
++        if let Some(second) = groups.next() {
++            if !groups.all(|x| x == second) || first > second {
++                Err(WarningType::InconsistentDigitGrouping)
++            } else if second > 4 {
++                Err(WarningType::LargeDigitGroups)
++            } else {
++                Ok(Some(second))
++            }
++        } else if first > 5 {
++            Err(WarningType::UnreadableLiteral)
++        } else {
++            Ok(None)
++        }
++    }
++}
++
++#[allow(clippy::module_name_repetitions)]
++#[derive(Copy, Clone)]
++pub struct DecimalLiteralRepresentation {
++    threshold: u64,
++}
++
++impl_lint_pass!(DecimalLiteralRepresentation => [DECIMAL_LITERAL_REPRESENTATION]);
++
++impl EarlyLintPass for DecimalLiteralRepresentation {
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++        if in_external_macro(cx.sess(), expr.span) {
++            return;
++        }
++
++        if let ExprKind::Lit(ref lit) = expr.kind {
++            self.check_lit(cx, lit)
++        }
++    }
++}
++
++impl DecimalLiteralRepresentation {
++    #[must_use]
++    pub fn new(threshold: u64) -> Self {
++        Self { threshold }
++    }
++    fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
++        // Lint integral literals.
++        if_chain! {
++            if let LitKind::Int(val, _) = lit.kind;
++            if let Some(src) = snippet_opt(cx, lit.span);
++            if let Some(num_lit) = NumericLiteral::from_lit(&src, &lit);
++            if num_lit.radix == Radix::Decimal;
++            if val >= u128::from(self.threshold);
++            then {
++                let hex = format!("{:#X}", val);
++                let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false);
++                let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| {
++                    warning_type.display(num_lit.format(), cx, lit.span)
++                });
++            }
++        }
++    }
++
++    fn do_lint(digits: &str) -> Result<(), WarningType> {
++        if digits.len() == 1 {
++            // Lint for 1 digit literals, if someone really sets the threshold that low
++            if digits == "1"
++                || digits == "2"
++                || digits == "4"
++                || digits == "8"
++                || digits == "3"
++                || digits == "7"
++                || digits == "F"
++            {
++                return Err(WarningType::DecimalRepresentation);
++            }
++        } else if digits.len() < 4 {
++            // Lint for Literals with a hex-representation of 2 or 3 digits
++            let f = &digits[0..1]; // first digit
++            let s = &digits[1..]; // suffix
++
++            // Powers of 2
++            if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0'))
++                // Powers of 2 minus 1
++                || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F'))
++            {
++                return Err(WarningType::DecimalRepresentation);
++            }
++        } else {
++            // Lint for Literals with a hex-representation of 4 digits or more
++            let f = &digits[0..1]; // first digit
++            let m = &digits[1..digits.len() - 1]; // middle digits, except last
++            let s = &digits[1..]; // suffix
++
++            // Powers of 2 with a margin of +15/-16
++            if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0'))
++                || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F'))
++                // Lint for representations with only 0s and Fs, while allowing 7 as the first
++                // digit
++                || ((f.eq("7") || f.eq("F")) && s.chars().all(|c| c == '0' || c == 'F'))
++            {
++                return Err(WarningType::DecimalRepresentation);
++            }
++        }
++
++        Ok(())
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f7c7fd82d833b6c71a93c7b3b97fc854d93f9af7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2550 @@@
++use crate::consts::{constant, Constant};
++use crate::reexport::Name;
++use crate::utils::paths;
++use crate::utils::usage::{is_unused, mutated_variables};
++use crate::utils::{
++    get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
++    is_integer_const, is_no_std_crate, is_refutable, last_path_segment, match_trait_method, match_type, match_var,
++    multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help,
++    span_lint_and_sugg, span_lint_and_then, SpanlessEq,
++};
++use crate::utils::{is_type_diagnostic_item, qpath_res, same_tys, sext, sugg};
++use if_chain::if_chain;
++use itertools::Itertools;
++use rustc_ast::ast;
++use rustc_data_structures::fx::{FxHashMap, FxHashSet};
++use rustc_errors::Applicability;
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::intravisit::{walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
++use rustc_hir::{
++    def_id, BinOpKind, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, GenericArg, HirId, LoopSource,
++    MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind,
++};
++use rustc_infer::infer::TyCtxtInferExt;
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::middle::region;
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++use rustc_span::BytePos;
++use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase};
++use std::iter::{once, Iterator};
++use std::mem;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for for-loops that manually copy items between
++    /// slices that could be optimized by having a memcpy.
++    ///
++    /// **Why is this bad?** It is not as fast as a memcpy.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let src = vec![1];
++    /// # let mut dst = vec![0; 65];
++    /// for i in 0..src.len() {
++    ///     dst[i + 64] = src[i];
++    /// }
++    /// ```
++    /// Could be written as:
++    /// ```rust
++    /// # let src = vec![1];
++    /// # let mut dst = vec![0; 65];
++    /// dst[64..(src.len() + 64)].clone_from_slice(&src[..]);
++    /// ```
++    pub MANUAL_MEMCPY,
++    perf,
++    "manually copying items between slices"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for looping over the range of `0..len` of some
++    /// collection just to get the values by index.
++    ///
++    /// **Why is this bad?** Just iterating the collection itself makes the intent
++    /// more clear and is probably faster.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let vec = vec!['a', 'b', 'c'];
++    /// for i in 0..vec.len() {
++    ///     println!("{}", vec[i]);
++    /// }
++    /// ```
++    /// Could be written as:
++    /// ```rust
++    /// let vec = vec!['a', 'b', 'c'];
++    /// for i in vec {
++    ///     println!("{}", i);
++    /// }
++    /// ```
++    pub NEEDLESS_RANGE_LOOP,
++    style,
++    "for-looping over a range of indices where an iterator over items would do"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for loops on `x.iter()` where `&x` will do, and
++    /// suggests the latter.
++    ///
++    /// **Why is this bad?** Readability.
++    ///
++    /// **Known problems:** False negatives. We currently only warn on some known
++    /// types.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// // with `y` a `Vec` or slice:
++    /// # let y = vec![1];
++    /// for x in y.iter() {
++    ///     // ..
++    /// }
++    /// ```
++    /// can be rewritten to
++    /// ```rust
++    /// # let y = vec![1];
++    /// for x in &y {
++    ///     // ..
++    /// }
++    /// ```
++    pub EXPLICIT_ITER_LOOP,
++    pedantic,
++    "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for loops on `y.into_iter()` where `y` will do, and
++    /// suggests the latter.
++    ///
++    /// **Why is this bad?** Readability.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let y = vec![1];
++    /// // with `y` a `Vec` or slice:
++    /// for x in y.into_iter() {
++    ///     // ..
++    /// }
++    /// ```
++    /// can be rewritten to
++    /// ```rust
++    /// # let y = vec![1];
++    /// for x in y {
++    ///     // ..
++    /// }
++    /// ```
++    pub EXPLICIT_INTO_ITER_LOOP,
++    pedantic,
++    "for-looping over `_.into_iter()` when `_` would do"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for loops on `x.next()`.
++    ///
++    /// **Why is this bad?** `next()` returns either `Some(value)` if there was a
++    /// value, or `None` otherwise. The insidious thing is that `Option<_>`
++    /// implements `IntoIterator`, so that possibly one value will be iterated,
++    /// leading to some hard to find bugs. No one will want to write such code
++    /// [except to win an Underhanded Rust
++    /// Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// for x in y.next() {
++    ///     ..
++    /// }
++    /// ```
++    pub ITER_NEXT_LOOP,
++    correctness,
++    "for-looping over `_.next()` which is probably not intended"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `for` loops over `Option` values.
++    ///
++    /// **Why is this bad?** Readability. This is more clearly expressed as an `if
++    /// let`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// for x in option {
++    ///     ..
++    /// }
++    /// ```
++    ///
++    /// This should be
++    /// ```ignore
++    /// if let Some(x) = option {
++    ///     ..
++    /// }
++    /// ```
++    pub FOR_LOOP_OVER_OPTION,
++    correctness,
++    "for-looping over an `Option`, which is more clearly expressed as an `if let`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `for` loops over `Result` values.
++    ///
++    /// **Why is this bad?** Readability. This is more clearly expressed as an `if
++    /// let`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// for x in result {
++    ///     ..
++    /// }
++    /// ```
++    ///
++    /// This should be
++    /// ```ignore
++    /// if let Ok(x) = result {
++    ///     ..
++    /// }
++    /// ```
++    pub FOR_LOOP_OVER_RESULT,
++    correctness,
++    "for-looping over a `Result`, which is more clearly expressed as an `if let`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Detects `loop + match` combinations that are easier
++    /// written as a `while let` loop.
++    ///
++    /// **Why is this bad?** The `while let` loop is usually shorter and more
++    /// readable.
++    ///
++    /// **Known problems:** Sometimes the wrong binding is displayed (#383).
++    ///
++    /// **Example:**
++    /// ```rust,no_run
++    /// # let y = Some(1);
++    /// loop {
++    ///     let x = match y {
++    ///         Some(x) => x,
++    ///         None => break,
++    ///     };
++    ///     // .. do something with x
++    /// }
++    /// // is easier written as
++    /// while let Some(x) = y {
++    ///     // .. do something with x
++    /// };
++    /// ```
++    pub WHILE_LET_LOOP,
++    complexity,
++    "`loop { if let { ... } else break }`, which can be written as a `while let` loop"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for functions collecting an iterator when collect
++    /// is not needed.
++    ///
++    /// **Why is this bad?** `collect` causes the allocation of a new data structure,
++    /// when this allocation may not be needed.
++    ///
++    /// **Known problems:**
++    /// None
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let iterator = vec![1].into_iter();
++    /// let len = iterator.clone().collect::<Vec<_>>().len();
++    /// // should be
++    /// let len = iterator.count();
++    /// ```
++    pub NEEDLESS_COLLECT,
++    perf,
++    "collecting an iterator when collect is not needed"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for loops over ranges `x..y` where both `x` and `y`
++    /// are constant and `x` is greater or equal to `y`, unless the range is
++    /// reversed or has a negative `.step_by(_)`.
++    ///
++    /// **Why is it bad?** Such loops will either be skipped or loop until
++    /// wrap-around (in debug code, this may `panic!()`). Both options are probably
++    /// not intended.
++    ///
++    /// **Known problems:** The lint cannot catch loops over dynamically defined
++    /// ranges. Doing this would require simulating all possible inputs and code
++    /// paths through the program, which would be complex and error-prone.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// for x in 5..10 - 5 {
++    ///     ..
++    /// } // oops, stray `-`
++    /// ```
++    pub REVERSE_RANGE_LOOP,
++    correctness,
++    "iteration over an empty range, such as `10..0` or `5..5`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks `for` loops over slices with an explicit counter
++    /// and suggests the use of `.enumerate()`.
++    ///
++    /// **Why is it bad?** Using `.enumerate()` makes the intent more clear,
++    /// declutters the code and may be faster in some instances.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let v = vec![1];
++    /// # fn bar(bar: usize, baz: usize) {}
++    /// let mut i = 0;
++    /// for item in &v {
++    ///     bar(i, *item);
++    ///     i += 1;
++    /// }
++    /// ```
++    /// Could be written as
++    /// ```rust
++    /// # let v = vec![1];
++    /// # fn bar(bar: usize, baz: usize) {}
++    /// for (i, item) in v.iter().enumerate() { bar(i, *item); }
++    /// ```
++    pub EXPLICIT_COUNTER_LOOP,
++    complexity,
++    "for-looping with an explicit counter when `_.enumerate()` would do"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for empty `loop` expressions.
++    ///
++    /// **Why is this bad?** Those busy loops burn CPU cycles without doing
++    /// anything. Think of the environment and either block on something or at least
++    /// make the thread sleep for some microseconds.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```no_run
++    /// loop {}
++    /// ```
++    pub EMPTY_LOOP,
++    style,
++    "empty `loop {}`, which should block or sleep"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `while let` expressions on iterators.
++    ///
++    /// **Why is this bad?** Readability. A simple `for` loop is shorter and conveys
++    /// the intent better.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// while let Some(val) = iter() {
++    ///     ..
++    /// }
++    /// ```
++    pub WHILE_LET_ON_ITERATOR,
++    style,
++    "using a while-let loop instead of a for loop on an iterator"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for iterating a map (`HashMap` or `BTreeMap`) and
++    /// ignoring either the keys or values.
++    ///
++    /// **Why is this bad?** Readability. There are `keys` and `values` methods that
++    /// can be used to express that don't need the values or keys.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// for (k, _) in &map {
++    ///     ..
++    /// }
++    /// ```
++    ///
++    /// could be replaced by
++    ///
++    /// ```ignore
++    /// for k in map.keys() {
++    ///     ..
++    /// }
++    /// ```
++    pub FOR_KV_MAP,
++    style,
++    "looping on a map using `iter` when `keys` or `values` would do"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for loops that will always `break`, `return` or
++    /// `continue` an outer loop.
++    ///
++    /// **Why is this bad?** This loop never loops, all it does is obfuscating the
++    /// code.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// ```rust
++    /// loop {
++    ///     ..;
++    ///     break;
++    /// }
++    /// ```
++    pub NEVER_LOOP,
++    correctness,
++    "any loop that will always `break` or `return`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for loops which have a range bound that is a mutable variable
++    ///
++    /// **Why is this bad?** One might think that modifying the mutable variable changes the loop bounds
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let mut foo = 42;
++    /// for i in 0..foo {
++    ///     foo -= 1;
++    ///     println!("{}", i); // prints numbers from 0 to 42, not 0 to 21
++    /// }
++    /// ```
++    pub MUT_RANGE_BOUND,
++    complexity,
++    "for loop over a range where one of the bounds is a mutable variable"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks whether variables used within while loop condition
++    /// can be (and are) mutated in the body.
++    ///
++    /// **Why is this bad?** If the condition is unchanged, entering the body of the loop
++    /// will lead to an infinite loop.
++    ///
++    /// **Known problems:** If the `while`-loop is in a closure, the check for mutation of the
++    /// condition variables in the body can cause false negatives. For example when only `Upvar` `a` is
++    /// in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let i = 0;
++    /// while i > 10 {
++    ///     println!("let me loop forever!");
++    /// }
++    /// ```
++    pub WHILE_IMMUTABLE_CONDITION,
++    correctness,
++    "variables used within while expression are not mutated in the body"
++}
++
++declare_lint_pass!(Loops => [
++    MANUAL_MEMCPY,
++    NEEDLESS_RANGE_LOOP,
++    EXPLICIT_ITER_LOOP,
++    EXPLICIT_INTO_ITER_LOOP,
++    ITER_NEXT_LOOP,
++    FOR_LOOP_OVER_RESULT,
++    FOR_LOOP_OVER_OPTION,
++    WHILE_LET_LOOP,
++    NEEDLESS_COLLECT,
++    REVERSE_RANGE_LOOP,
++    EXPLICIT_COUNTER_LOOP,
++    EMPTY_LOOP,
++    WHILE_LET_ON_ITERATOR,
++    FOR_KV_MAP,
++    NEVER_LOOP,
++    MUT_RANGE_BOUND,
++    WHILE_IMMUTABLE_CONDITION,
++]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Loops {
++    #[allow(clippy::too_many_lines)]
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if let Some((pat, arg, body)) = higher::for_loop(expr) {
++            // we don't want to check expanded macros
++            // this check is not at the top of the function
++            // since higher::for_loop expressions are marked as expansions
++            if body.span.from_expansion() {
++                return;
++            }
++            check_for_loop(cx, pat, arg, body, expr);
++        }
++
++        // we don't want to check expanded macros
++        if expr.span.from_expansion() {
++            return;
++        }
++
++        // check for never_loop
++        if let ExprKind::Loop(ref block, _, _) = expr.kind {
++            match never_loop_block(block, expr.hir_id) {
++                NeverLoopResult::AlwaysBreak => span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops"),
++                NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (),
++            }
++        }
++
++        // check for `loop { if let {} else break }` that could be `while let`
++        // (also matches an explicit "match" instead of "if let")
++        // (even if the "match" or "if let" is used for declaration)
++        if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind {
++            // also check for empty `loop {}` statements
++            if block.stmts.is_empty() && block.expr.is_none() && !is_no_std_crate(cx.tcx.hir().krate()) {
++                span_lint(
++                    cx,
++                    EMPTY_LOOP,
++                    expr.span,
++                    "empty `loop {}` detected. You may want to either use `panic!()` or add \
++                     `std::thread::sleep(..);` to the loop body.",
++                );
++            }
++
++            // extract the expression from the first statement (if any) in a block
++            let inner_stmt_expr = extract_expr_from_first_stmt(block);
++            // or extract the first expression (if any) from the block
++            if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(block)) {
++                if let ExprKind::Match(ref matchexpr, ref arms, ref source) = inner.kind {
++                    // ensure "if let" compatible match structure
++                    match *source {
++                        MatchSource::Normal | MatchSource::IfLetDesugar { .. } => {
++                            if arms.len() == 2
++                                && arms[0].guard.is_none()
++                                && arms[1].guard.is_none()
++                                && is_simple_break_expr(&arms[1].body)
++                            {
++                                if in_external_macro(cx.sess(), expr.span) {
++                                    return;
++                                }
++
++                                // NOTE: we used to build a body here instead of using
++                                // ellipsis, this was removed because:
++                                // 1) it was ugly with big bodies;
++                                // 2) it was not indented properly;
++                                // 3) it wasn’t very smart (see #675).
++                                let mut applicability = Applicability::HasPlaceholders;
++                                span_lint_and_sugg(
++                                    cx,
++                                    WHILE_LET_LOOP,
++                                    expr.span,
++                                    "this loop could be written as a `while let` loop",
++                                    "try",
++                                    format!(
++                                        "while let {} = {} {{ .. }}",
++                                        snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability),
++                                        snippet_with_applicability(cx, matchexpr.span, "..", &mut applicability),
++                                    ),
++                                    applicability,
++                                );
++                            }
++                        },
++                        _ => (),
++                    }
++                }
++            }
++        }
++        if let ExprKind::Match(ref match_expr, ref arms, MatchSource::WhileLetDesugar) = expr.kind {
++            let pat = &arms[0].pat.kind;
++            if let (
++                &PatKind::TupleStruct(ref qpath, ref pat_args, _),
++                &ExprKind::MethodCall(ref method_path, _, ref method_args),
++            ) = (pat, &match_expr.kind)
++            {
++                let iter_expr = &method_args[0];
++
++                // 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 implements_trait(cx, cx.tables.expr_ty(iter_expr), iter_def_id, &[]);
++                    then {
++                        return;
++                    }
++                }
++
++                let lhs_constructor = last_path_segment(qpath);
++                if method_path.ident.name == sym!(next)
++                    && match_trait_method(cx, match_expr, &paths::ITERATOR)
++                    && lhs_constructor.ident.name == sym!(Some)
++                    && (pat_args.is_empty()
++                        || !is_refutable(cx, &pat_args[0])
++                            && !is_used_inside(cx, iter_expr, &arms[0].body)
++                            && !is_iterator_used_after_while_let(cx, iter_expr)
++                            && !is_nested(cx, expr, &method_args[0]))
++                {
++                    let mut applicability = Applicability::MachineApplicable;
++                    let iterator = snippet_with_applicability(cx, method_args[0].span, "_", &mut applicability);
++                    let loop_var = if pat_args.is_empty() {
++                        "_".to_string()
++                    } else {
++                        snippet_with_applicability(cx, pat_args[0].span, "_", &mut applicability).into_owned()
++                    };
++                    span_lint_and_sugg(
++                        cx,
++                        WHILE_LET_ON_ITERATOR,
++                        expr.span.with_hi(match_expr.span.hi()),
++                        "this loop could be written as a `for` loop",
++                        "try",
++                        format!("for {} in {}", loop_var, iterator),
++                        applicability,
++                    );
++                }
++            }
++        }
++
++        if let Some((cond, body)) = higher::while_loop(&expr) {
++            check_infinite_loop(cx, cond, body);
++        }
++
++        check_needless_collect(expr, cx);
++    }
++}
++
++enum NeverLoopResult {
++    // A break/return always get triggered but not necessarily for the main loop.
++    AlwaysBreak,
++    // A continue may occur for the main loop.
++    MayContinueMainLoop,
++    Otherwise,
++}
++
++#[must_use]
++fn absorb_break(arg: &NeverLoopResult) -> NeverLoopResult {
++    match *arg {
++        NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise,
++        NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop,
++    }
++}
++
++// Combine two results for parts that are called in order.
++#[must_use]
++fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult {
++    match first {
++        NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop => first,
++        NeverLoopResult::Otherwise => second,
++    }
++}
++
++// Combine two results where both parts are called but not necessarily in order.
++#[must_use]
++fn combine_both(left: NeverLoopResult, right: NeverLoopResult) -> NeverLoopResult {
++    match (left, right) {
++        (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
++            NeverLoopResult::MayContinueMainLoop
++        },
++        (NeverLoopResult::AlwaysBreak, _) | (_, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
++        (NeverLoopResult::Otherwise, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
++    }
++}
++
++// Combine two results where only one of the part may have been executed.
++#[must_use]
++fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult {
++    match (b1, b2) {
++        (NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
++        (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
++            NeverLoopResult::MayContinueMainLoop
++        },
++        (NeverLoopResult::Otherwise, _) | (_, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
++    }
++}
++
++fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult {
++    let stmts = block.stmts.iter().map(stmt_to_expr);
++    let expr = once(block.expr.as_deref());
++    let mut iter = stmts.chain(expr).filter_map(|e| e);
++    never_loop_expr_seq(&mut iter, main_loop_id)
++}
++
++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,
++    }
++}
++
++fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
++    match expr.kind {
++        ExprKind::Box(ref e)
++        | ExprKind::Unary(_, ref e)
++        | ExprKind::Cast(ref e, _)
++        | ExprKind::Type(ref e, _)
++        | ExprKind::Field(ref e, _)
++        | ExprKind::AddrOf(_, _, ref e)
++        | ExprKind::Struct(_, _, Some(ref e))
++        | ExprKind::Repeat(ref e, _)
++        | ExprKind::DropTemps(ref e) => never_loop_expr(e, main_loop_id),
++        ExprKind::Array(ref es) | ExprKind::MethodCall(_, _, ref es) | ExprKind::Tup(ref es) => {
++            never_loop_expr_all(&mut es.iter(), main_loop_id)
++        },
++        ExprKind::Call(ref e, ref es) => never_loop_expr_all(&mut once(&**e).chain(es.iter()), main_loop_id),
++        ExprKind::Binary(_, ref e1, ref e2)
++        | ExprKind::Assign(ref e1, ref e2, _)
++        | ExprKind::AssignOp(_, ref e1, ref e2)
++        | ExprKind::Index(ref e1, ref e2) => never_loop_expr_all(&mut [&**e1, &**e2].iter().cloned(), main_loop_id),
++        ExprKind::Loop(ref b, _, _) => {
++            // Break can come from the inner loop so remove them.
++            absorb_break(&never_loop_block(b, main_loop_id))
++        },
++        ExprKind::Match(ref e, ref arms, _) => {
++            let e = never_loop_expr(e, main_loop_id);
++            if arms.is_empty() {
++                e
++            } else {
++                let arms = never_loop_expr_branch(&mut arms.iter().map(|a| &*a.body), main_loop_id);
++                combine_seq(e, arms)
++            }
++        },
++        ExprKind::Block(ref b, _) => never_loop_block(b, main_loop_id),
++        ExprKind::Continue(d) => {
++            let id = d
++                .target_id
++                .expect("target ID can only be missing in the presence of compilation errors");
++            if id == main_loop_id {
++                NeverLoopResult::MayContinueMainLoop
++            } else {
++                NeverLoopResult::AlwaysBreak
++            }
++        },
++        ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => {
++            if let Some(ref e) = *e {
++                combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
++            } else {
++                NeverLoopResult::AlwaysBreak
++            }
++        },
++        ExprKind::Struct(_, _, None)
++        | ExprKind::Yield(_, _)
++        | ExprKind::Closure(_, _, _, _, _)
++        | ExprKind::LlvmInlineAsm(_)
++        | ExprKind::Path(_)
++        | ExprKind::Lit(_)
++        | ExprKind::Err => NeverLoopResult::Otherwise,
++    }
++}
++
++fn never_loop_expr_seq<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult {
++    es.map(|e| never_loop_expr(e, main_loop_id))
++        .fold(NeverLoopResult::Otherwise, combine_seq)
++}
++
++fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult {
++    es.map(|e| never_loop_expr(e, main_loop_id))
++        .fold(NeverLoopResult::Otherwise, combine_both)
++}
++
++fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(e: &mut T, main_loop_id: HirId) -> NeverLoopResult {
++    e.map(|e| never_loop_expr(e, main_loop_id))
++        .fold(NeverLoopResult::AlwaysBreak, combine_branches)
++}
++
++fn check_for_loop<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    pat: &'tcx Pat<'_>,
++    arg: &'tcx Expr<'_>,
++    body: &'tcx Expr<'_>,
++    expr: &'tcx Expr<'_>,
++) {
++    check_for_loop_range(cx, pat, arg, body, expr);
++    check_for_loop_reverse_range(cx, arg, expr);
++    check_for_loop_arg(cx, pat, arg, expr);
++    check_for_loop_explicit_counter(cx, pat, arg, body, expr);
++    check_for_loop_over_map_kv(cx, pat, arg, body, expr);
++    check_for_mut_range_bound(cx, arg, body);
++    detect_manual_memcpy(cx, pat, arg, body, expr);
++}
++
++fn same_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> bool {
++    if_chain! {
++        if let ExprKind::Path(ref qpath) = expr.kind;
++        if let QPath::Resolved(None, ref path) = *qpath;
++        if path.segments.len() == 1;
++        if let Res::Local(local_id) = qpath_res(cx, qpath, expr.hir_id);
++        // our variable!
++        if local_id == var;
++        then {
++            return true;
++        }
++    }
++
++    false
++}
++
++struct Offset {
++    value: String,
++    negate: bool,
++}
++
++impl Offset {
++    fn negative(s: String) -> Self {
++        Self { value: s, negate: true }
++    }
++
++    fn positive(s: String) -> Self {
++        Self {
++            value: s,
++            negate: false,
++        }
++    }
++}
++
++struct FixedOffsetVar {
++    var_name: String,
++    offset: Offset,
++}
++
++fn is_slice_like<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'_>) -> bool {
++    let is_slice = match ty.kind {
++        ty::Ref(_, subty, _) => is_slice_like(cx, subty),
++        ty::Slice(..) | ty::Array(..) => true,
++        _ => false,
++    };
++
++    is_slice || is_type_diagnostic_item(cx, ty, sym!(vec_type)) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type))
++}
++
++fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>, var: HirId) -> Option<FixedOffsetVar> {
++    fn extract_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &Expr<'_>, var: HirId) -> Option<String> {
++        match e.kind {
++            ExprKind::Lit(ref l) => match l.node {
++                ast::LitKind::Int(x, _ty) => Some(x.to_string()),
++                _ => None,
++            },
++            ExprKind::Path(..) if !same_var(cx, e, var) => Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())),
++            _ => None,
++        }
++    }
++
++    if let ExprKind::Index(ref seqexpr, ref idx) = expr.kind {
++        let ty = cx.tables.expr_ty(seqexpr);
++        if !is_slice_like(cx, ty) {
++            return None;
++        }
++
++        let offset = match idx.kind {
++            ExprKind::Binary(op, ref lhs, ref rhs) => match op.node {
++                BinOpKind::Add => {
++                    let offset_opt = if same_var(cx, lhs, var) {
++                        extract_offset(cx, rhs, var)
++                    } else if same_var(cx, rhs, var) {
++                        extract_offset(cx, lhs, var)
++                    } else {
++                        None
++                    };
++
++                    offset_opt.map(Offset::positive)
++                },
++                BinOpKind::Sub if same_var(cx, lhs, var) => extract_offset(cx, rhs, var).map(Offset::negative),
++                _ => None,
++            },
++            ExprKind::Path(..) => {
++                if same_var(cx, idx, var) {
++                    Some(Offset::positive("0".into()))
++                } else {
++                    None
++                }
++            },
++            _ => None,
++        };
++
++        offset.map(|o| FixedOffsetVar {
++            var_name: snippet_opt(cx, seqexpr.span).unwrap_or_else(|| "???".into()),
++            offset: o,
++        })
++    } else {
++        None
++    }
++}
++
++fn fetch_cloned_fixed_offset_var<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &Expr<'_>,
++    var: HirId,
++) -> Option<FixedOffsetVar> {
++    if_chain! {
++        if let ExprKind::MethodCall(ref method, _, ref args) = expr.kind;
++        if method.ident.name == sym!(clone);
++        if args.len() == 1;
++        if let Some(arg) = args.get(0);
++        then {
++            return get_fixed_offset_var(cx, arg, var);
++        }
++    }
++
++    get_fixed_offset_var(cx, expr, var)
++}
++
++fn get_indexed_assignments<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    body: &Expr<'_>,
++    var: HirId,
++) -> Vec<(FixedOffsetVar, FixedOffsetVar)> {
++    fn get_assignment<'a, 'tcx>(
++        cx: &LateContext<'a, 'tcx>,
++        e: &Expr<'_>,
++        var: HirId,
++    ) -> Option<(FixedOffsetVar, FixedOffsetVar)> {
++        if let ExprKind::Assign(ref lhs, ref rhs, _) = e.kind {
++            match (
++                get_fixed_offset_var(cx, lhs, var),
++                fetch_cloned_fixed_offset_var(cx, rhs, var),
++            ) {
++                (Some(offset_left), Some(offset_right)) => {
++                    // Source and destination must be different
++                    if offset_left.var_name == offset_right.var_name {
++                        None
++                    } else {
++                        Some((offset_left, offset_right))
++                    }
++                },
++                _ => None,
++            }
++        } else {
++            None
++        }
++    }
++
++    if let ExprKind::Block(ref b, _) = body.kind {
++        let Block {
++            ref stmts, ref expr, ..
++        } = **b;
++
++        stmts
++            .iter()
++            .map(|stmt| match stmt.kind {
++                StmtKind::Local(..) | StmtKind::Item(..) => None,
++                StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => Some(get_assignment(cx, e, var)),
++            })
++            .chain(expr.as_ref().into_iter().map(|e| Some(get_assignment(cx, &*e, var))))
++            .filter_map(|op| op)
++            .collect::<Option<Vec<_>>>()
++            .unwrap_or_default()
++    } else {
++        get_assignment(cx, body, var).into_iter().collect()
++    }
++}
++
++/// Checks for for loops that sequentially copy items from one slice-like
++/// object to another.
++fn detect_manual_memcpy<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    pat: &'tcx Pat<'_>,
++    arg: &'tcx Expr<'_>,
++    body: &'tcx Expr<'_>,
++    expr: &'tcx Expr<'_>,
++) {
++    if let Some(higher::Range {
++        start: Some(start),
++        ref end,
++        limits,
++    }) = higher::range(cx, arg)
++    {
++        // the var must be a single name
++        if let PatKind::Binding(_, canonical_id, _, _) = pat.kind {
++            let print_sum = |arg1: &Offset, arg2: &Offset| -> String {
++                match (&arg1.value[..], arg1.negate, &arg2.value[..], arg2.negate) {
++                    ("0", _, "0", _) => "".into(),
++                    ("0", _, x, false) | (x, false, "0", false) => x.into(),
++                    ("0", _, x, true) | (x, false, "0", true) => format!("-{}", x),
++                    (x, false, y, false) => format!("({} + {})", x, y),
++                    (x, false, y, true) => {
++                        if x == y {
++                            "0".into()
++                        } else {
++                            format!("({} - {})", x, y)
++                        }
++                    },
++                    (x, true, y, false) => {
++                        if x == y {
++                            "0".into()
++                        } else {
++                            format!("({} - {})", y, x)
++                        }
++                    },
++                    (x, true, y, true) => format!("-({} + {})", x, y),
++                }
++            };
++
++            let print_limit = |end: &Option<&Expr<'_>>, offset: Offset, var_name: &str| {
++                if let Some(end) = *end {
++                    if_chain! {
++                        if let ExprKind::MethodCall(ref method, _, ref len_args) = end.kind;
++                        if method.ident.name == sym!(len);
++                        if len_args.len() == 1;
++                        if let Some(arg) = len_args.get(0);
++                        if snippet(cx, arg.span, "??") == var_name;
++                        then {
++                            return if offset.negate {
++                                format!("({} - {})", snippet(cx, end.span, "<src>.len()"), offset.value)
++                            } else {
++                                String::new()
++                            };
++                        }
++                    }
++
++                    let end_str = match limits {
++                        ast::RangeLimits::Closed => {
++                            let end = sugg::Sugg::hir(cx, end, "<count>");
++                            format!("{}", end + sugg::ONE)
++                        },
++                        ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")),
++                    };
++
++                    print_sum(&Offset::positive(end_str), &offset)
++                } else {
++                    "..".into()
++                }
++            };
++
++            // The only statements in the for loops can be indexed assignments from
++            // indexed retrievals.
++            let manual_copies = get_indexed_assignments(cx, body, canonical_id);
++
++            let big_sugg = manual_copies
++                .into_iter()
++                .map(|(dst_var, src_var)| {
++                    let start_str = Offset::positive(snippet(cx, start.span, "").to_string());
++                    let dst_offset = print_sum(&start_str, &dst_var.offset);
++                    let dst_limit = print_limit(end, dst_var.offset, &dst_var.var_name);
++                    let src_offset = print_sum(&start_str, &src_var.offset);
++                    let src_limit = print_limit(end, src_var.offset, &src_var.var_name);
++                    let dst = if dst_offset == "" && dst_limit == "" {
++                        dst_var.var_name
++                    } else {
++                        format!("{}[{}..{}]", dst_var.var_name, dst_offset, dst_limit)
++                    };
++
++                    format!(
++                        "{}.clone_from_slice(&{}[{}..{}])",
++                        dst, src_var.var_name, src_offset, src_limit
++                    )
++                })
++                .join("\n    ");
++
++            if !big_sugg.is_empty() {
++                span_lint_and_sugg(
++                    cx,
++                    MANUAL_MEMCPY,
++                    expr.span,
++                    "it looks like you're manually copying between slices",
++                    "try replacing the loop by",
++                    big_sugg,
++                    Applicability::Unspecified,
++                );
++            }
++        }
++    }
++}
++
++/// Checks for looping over a range and then indexing a sequence with it.
++/// The iteratee must be a range literal.
++#[allow(clippy::too_many_lines)]
++fn check_for_loop_range<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    pat: &'tcx Pat<'_>,
++    arg: &'tcx Expr<'_>,
++    body: &'tcx Expr<'_>,
++    expr: &'tcx Expr<'_>,
++) {
++    if let Some(higher::Range {
++        start: Some(start),
++        ref end,
++        limits,
++    }) = higher::range(cx, arg)
++    {
++        // the var must be a single name
++        if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind {
++            let mut visitor = VarVisitor {
++                cx,
++                var: canonical_id,
++                indexed_mut: FxHashSet::default(),
++                indexed_indirectly: FxHashMap::default(),
++                indexed_directly: FxHashMap::default(),
++                referenced: FxHashSet::default(),
++                nonindex: false,
++                prefer_mutable: false,
++            };
++            walk_expr(&mut visitor, body);
++
++            // linting condition: we only indexed one variable, and indexed it directly
++            if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 {
++                let (indexed, (indexed_extent, indexed_ty)) = visitor
++                    .indexed_directly
++                    .into_iter()
++                    .next()
++                    .expect("already checked that we have exactly 1 element");
++
++                // ensure that the indexed variable was declared before the loop, see #601
++                if let Some(indexed_extent) = indexed_extent {
++                    let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
++                    let parent_def_id = cx.tcx.hir().local_def_id(parent_id);
++                    let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id);
++                    let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id);
++                    if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) {
++                        return;
++                    }
++                }
++
++                // don't lint if the container that is indexed does not have .iter() method
++                let has_iter = has_iter_method(cx, indexed_ty);
++                if has_iter.is_none() {
++                    return;
++                }
++
++                // don't lint if the container that is indexed into is also used without
++                // indexing
++                if visitor.referenced.contains(&indexed) {
++                    return;
++                }
++
++                let starts_at_zero = is_integer_const(cx, start, 0);
++
++                let skip = if starts_at_zero {
++                    String::new()
++                } else {
++                    format!(".skip({})", snippet(cx, start.span, ".."))
++                };
++
++                let mut end_is_start_plus_val = false;
++
++                let take = if let Some(end) = *end {
++                    let mut take_expr = end;
++
++                    if let ExprKind::Binary(ref op, ref left, ref right) = end.kind {
++                        if let BinOpKind::Add = op.node {
++                            let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left);
++                            let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right);
++
++                            if start_equal_left {
++                                take_expr = right;
++                            } else if start_equal_right {
++                                take_expr = left;
++                            }
++
++                            end_is_start_plus_val = start_equal_left | start_equal_right;
++                        }
++                    }
++
++                    if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) {
++                        String::new()
++                    } else {
++                        match limits {
++                            ast::RangeLimits::Closed => {
++                                let take_expr = sugg::Sugg::hir(cx, take_expr, "<count>");
++                                format!(".take({})", take_expr + sugg::ONE)
++                            },
++                            ast::RangeLimits::HalfOpen => format!(".take({})", snippet(cx, take_expr.span, "..")),
++                        }
++                    }
++                } else {
++                    String::new()
++                };
++
++                let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) {
++                    ("mut ", "iter_mut")
++                } else {
++                    ("", "iter")
++                };
++
++                let take_is_empty = take.is_empty();
++                let mut method_1 = take;
++                let mut method_2 = skip;
++
++                if end_is_start_plus_val {
++                    mem::swap(&mut method_1, &mut method_2);
++                }
++
++                if visitor.nonindex {
++                    span_lint_and_then(
++                        cx,
++                        NEEDLESS_RANGE_LOOP,
++                        expr.span,
++                        &format!("the loop variable `{}` is used to index `{}`", ident.name, indexed),
++                        |diag| {
++                            multispan_sugg(
++                                diag,
++                                "consider using an iterator".to_string(),
++                                vec![
++                                    (pat.span, format!("({}, <item>)", ident.name)),
++                                    (
++                                        arg.span,
++                                        format!("{}.{}().enumerate(){}{}", indexed, method, method_1, method_2),
++                                    ),
++                                ],
++                            );
++                        },
++                    );
++                } else {
++                    let repl = if starts_at_zero && take_is_empty {
++                        format!("&{}{}", ref_mut, indexed)
++                    } else {
++                        format!("{}.{}(){}{}", indexed, method, method_1, method_2)
++                    };
++
++                    span_lint_and_then(
++                        cx,
++                        NEEDLESS_RANGE_LOOP,
++                        expr.span,
++                        &format!(
++                            "the loop variable `{}` is only used to index `{}`.",
++                            ident.name, indexed
++                        ),
++                        |diag| {
++                            multispan_sugg(
++                                diag,
++                                "consider using an iterator".to_string(),
++                                vec![(pat.span, "<item>".to_string()), (arg.span, repl)],
++                            );
++                        },
++                    );
++                }
++            }
++        }
++    }
++}
++
++fn is_len_call(expr: &Expr<'_>, var: Name) -> bool {
++    if_chain! {
++        if let ExprKind::MethodCall(ref method, _, ref len_args) = expr.kind;
++        if len_args.len() == 1;
++        if method.ident.name == sym!(len);
++        if let ExprKind::Path(QPath::Resolved(_, ref path)) = len_args[0].kind;
++        if path.segments.len() == 1;
++        if path.segments[0].ident.name == var;
++        then {
++            return true;
++        }
++    }
++
++    false
++}
++
++fn is_end_eq_array_len<'tcx>(
++    cx: &LateContext<'_, 'tcx>,
++    end: &Expr<'_>,
++    limits: ast::RangeLimits,
++    indexed_ty: Ty<'tcx>,
++) -> bool {
++    if_chain! {
++        if let ExprKind::Lit(ref lit) = end.kind;
++        if let ast::LitKind::Int(end_int, _) = lit.node;
++        if let ty::Array(_, arr_len_const) = indexed_ty.kind;
++        if let Some(arr_len) = arr_len_const.try_eval_usize(cx.tcx, cx.param_env);
++        then {
++            return match limits {
++                ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(),
++                ast::RangeLimits::HalfOpen => end_int >= arr_len.into(),
++            };
++        }
++    }
++
++    false
++}
++
++fn check_for_loop_reverse_range<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arg: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) {
++    // if this for loop is iterating over a two-sided range...
++    if let Some(higher::Range {
++        start: Some(start),
++        end: Some(end),
++        limits,
++    }) = higher::range(cx, arg)
++    {
++        // ...and both sides are compile-time constant integers...
++        if let Some((start_idx, _)) = constant(cx, cx.tables, start) {
++            if let Some((end_idx, _)) = constant(cx, cx.tables, end) {
++                // ...and the start index is greater than the end index,
++                // this loop will never run. This is often confusing for developers
++                // who think that this will iterate from the larger value to the
++                // smaller value.
++                let ty = cx.tables.expr_ty(start);
++                let (sup, eq) = match (start_idx, end_idx) {
++                    (Constant::Int(start_idx), Constant::Int(end_idx)) => (
++                        match ty.kind {
++                            ty::Int(ity) => sext(cx.tcx, start_idx, ity) > sext(cx.tcx, end_idx, ity),
++                            ty::Uint(_) => start_idx > end_idx,
++                            _ => false,
++                        },
++                        start_idx == end_idx,
++                    ),
++                    _ => (false, false),
++                };
++
++                if sup {
++                    let start_snippet = snippet(cx, start.span, "_");
++                    let end_snippet = snippet(cx, end.span, "_");
++                    let dots = if limits == ast::RangeLimits::Closed {
++                        "..="
++                    } else {
++                        ".."
++                    };
++
++                    span_lint_and_then(
++                        cx,
++                        REVERSE_RANGE_LOOP,
++                        expr.span,
++                        "this range is empty so this for loop will never run",
++                        |diag| {
++                            diag.span_suggestion(
++                                arg.span,
++                                "consider using the following if you are attempting to iterate over this \
++                                 range in reverse",
++                                format!(
++                                    "({end}{dots}{start}).rev()",
++                                    end = end_snippet,
++                                    dots = dots,
++                                    start = start_snippet
++                                ),
++                                Applicability::MaybeIncorrect,
++                            );
++                        },
++                    );
++                } else if eq && limits != ast::RangeLimits::Closed {
++                    // if they are equal, it's also problematic - this loop
++                    // will never run.
++                    span_lint(
++                        cx,
++                        REVERSE_RANGE_LOOP,
++                        expr.span,
++                        "this range is empty so this for loop will never run",
++                    );
++                }
++            }
++        }
++    }
++}
++
++fn lint_iter_method(cx: &LateContext<'_, '_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) {
++    let mut applicability = Applicability::MachineApplicable;
++    let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
++    let muta = if method_name == "iter_mut" { "mut " } else { "" };
++    span_lint_and_sugg(
++        cx,
++        EXPLICIT_ITER_LOOP,
++        arg.span,
++        "it is more concise to loop over references to containers instead of using explicit \
++         iteration methods",
++        "to write this more concisely, try",
++        format!("&{}{}", muta, object),
++        applicability,
++    )
++}
++
++fn check_for_loop_arg(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) {
++    let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used
++    if let ExprKind::MethodCall(ref method, _, ref args) = arg.kind {
++        // just the receiver, no arguments
++        if args.len() == 1 {
++            let method_name = &*method.ident.as_str();
++            // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
++            if method_name == "iter" || method_name == "iter_mut" {
++                if is_ref_iterable_type(cx, &args[0]) {
++                    lint_iter_method(cx, args, arg, method_name);
++                }
++            } else if method_name == "into_iter" && match_trait_method(cx, arg, &paths::INTO_ITERATOR) {
++                let receiver_ty = cx.tables.expr_ty(&args[0]);
++                let receiver_ty_adjusted = cx.tables.expr_ty_adjusted(&args[0]);
++                if same_tys(cx, receiver_ty, receiver_ty_adjusted) {
++                    let mut applicability = Applicability::MachineApplicable;
++                    let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
++                    span_lint_and_sugg(
++                        cx,
++                        EXPLICIT_INTO_ITER_LOOP,
++                        arg.span,
++                        "it is more concise to loop over containers instead of using explicit \
++                         iteration methods",
++                        "to write this more concisely, try",
++                        object.to_string(),
++                        applicability,
++                    );
++                } else {
++                    let ref_receiver_ty = cx.tcx.mk_ref(
++                        cx.tcx.lifetimes.re_erased,
++                        ty::TypeAndMut {
++                            ty: receiver_ty,
++                            mutbl: Mutability::Not,
++                        },
++                    );
++                    if same_tys(cx, receiver_ty_adjusted, ref_receiver_ty) {
++                        lint_iter_method(cx, args, arg, method_name)
++                    }
++                }
++            } else if method_name == "next" && match_trait_method(cx, arg, &paths::ITERATOR) {
++                span_lint(
++                    cx,
++                    ITER_NEXT_LOOP,
++                    expr.span,
++                    "you are iterating over `Iterator::next()` which is an Option; this will compile but is \
++                     probably not what you want",
++                );
++                next_loop_linted = true;
++            }
++        }
++    }
++    if !next_loop_linted {
++        check_arg_type(cx, pat, arg);
++    }
++}
++
++/// Checks for `for` loops over `Option`s and `Result`s.
++fn check_arg_type(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>) {
++    let ty = cx.tables.expr_ty(arg);
++    if is_type_diagnostic_item(cx, ty, sym!(option_type)) {
++        span_lint_and_help(
++            cx,
++            FOR_LOOP_OVER_OPTION,
++            arg.span,
++            &format!(
++                "for loop over `{0}`, which is an `Option`. This is more readably written as an \
++                 `if let` statement.",
++                snippet(cx, arg.span, "_")
++            ),
++            None,
++            &format!(
++                "consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`",
++                snippet(cx, pat.span, "_"),
++                snippet(cx, arg.span, "_")
++            ),
++        );
++    } else if is_type_diagnostic_item(cx, ty, sym!(result_type)) {
++        span_lint_and_help(
++            cx,
++            FOR_LOOP_OVER_RESULT,
++            arg.span,
++            &format!(
++                "for loop over `{0}`, which is a `Result`. This is more readably written as an \
++                 `if let` statement.",
++                snippet(cx, arg.span, "_")
++            ),
++            None,
++            &format!(
++                "consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`",
++                snippet(cx, pat.span, "_"),
++                snippet(cx, arg.span, "_")
++            ),
++        );
++    }
++}
++
++fn check_for_loop_explicit_counter<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    pat: &'tcx Pat<'_>,
++    arg: &'tcx Expr<'_>,
++    body: &'tcx Expr<'_>,
++    expr: &'tcx Expr<'_>,
++) {
++    // Look for variables that are incremented once per loop iteration.
++    let mut visitor = IncrementVisitor {
++        cx,
++        states: FxHashMap::default(),
++        depth: 0,
++        done: false,
++    };
++    walk_expr(&mut visitor, body);
++
++    // For each candidate, check the parent block to see if
++    // it's initialized to zero at the start of the loop.
++    if let Some(block) = get_enclosing_block(&cx, expr.hir_id) {
++        for (id, _) in visitor.states.iter().filter(|&(_, v)| *v == VarState::IncrOnce) {
++            let mut visitor2 = InitializeVisitor {
++                cx,
++                end_expr: expr,
++                var_id: *id,
++                state: VarState::IncrOnce,
++                name: None,
++                depth: 0,
++                past_loop: false,
++            };
++            walk_block(&mut visitor2, block);
++
++            if visitor2.state == VarState::Warn {
++                if let Some(name) = visitor2.name {
++                    let mut applicability = Applicability::MachineApplicable;
++
++                    // for some reason this is the only way to get the `Span`
++                    // of the entire `for` loop
++                    let for_span = if let ExprKind::Match(_, arms, _) = &expr.kind {
++                        arms[0].body.span
++                    } else {
++                        unreachable!()
++                    };
++
++                    span_lint_and_sugg(
++                        cx,
++                        EXPLICIT_COUNTER_LOOP,
++                        for_span.with_hi(arg.span.hi()),
++                        &format!("the variable `{}` is used as a loop counter.", name),
++                        "consider using",
++                        format!(
++                            "for ({}, {}) in {}.enumerate()",
++                            name,
++                            snippet_with_applicability(cx, pat.span, "item", &mut applicability),
++                            make_iterator_snippet(cx, arg, &mut applicability),
++                        ),
++                        applicability,
++                    );
++                }
++            }
++        }
++    }
++}
++
++/// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the
++/// actual `Iterator` that the loop uses.
++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| implements_trait(cx, cx.tables.expr_ty(arg), id, &[]));
++    if impls_iterator {
++        format!(
++            "{}",
++            sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par()
++        )
++    } else {
++        // (&x).into_iter() ==> x.iter()
++        // (&mut x).into_iter() ==> x.iter_mut()
++        match &arg.kind {
++            ExprKind::AddrOf(BorrowKind::Ref, mutability, arg_inner)
++                if has_iter_method(cx, cx.tables.expr_ty(&arg_inner)).is_some() =>
++            {
++                let meth_name = match mutability {
++                    Mutability::Mut => "iter_mut",
++                    Mutability::Not => "iter",
++                };
++                format!(
++                    "{}.{}()",
++                    sugg::Sugg::hir_with_applicability(cx, &arg_inner, "_", applic_ref).maybe_par(),
++                    meth_name,
++                )
++            }
++            _ => format!(
++                "{}.into_iter()",
++                sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par()
++            ),
++        }
++    }
++}
++
++/// Checks for the `FOR_KV_MAP` lint.
++fn check_for_loop_over_map_kv<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    pat: &'tcx Pat<'_>,
++    arg: &'tcx Expr<'_>,
++    body: &'tcx Expr<'_>,
++    expr: &'tcx Expr<'_>,
++) {
++    let pat_span = pat.span;
++
++    if let PatKind::Tuple(ref pat, _) = pat.kind {
++        if pat.len() == 2 {
++            let arg_span = arg.span;
++            let (new_pat_span, kind, ty, mutbl) = match cx.tables.expr_ty(arg).kind {
++                ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) {
++                    (key, _) if pat_is_wild(key, body) => (pat[1].span, "value", ty, mutbl),
++                    (_, value) if pat_is_wild(value, body) => (pat[0].span, "key", ty, Mutability::Not),
++                    _ => return,
++                },
++                _ => return,
++            };
++            let mutbl = match mutbl {
++                Mutability::Not => "",
++                Mutability::Mut => "_mut",
++            };
++            let arg = match arg.kind {
++                ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) => &**expr,
++                _ => arg,
++            };
++
++            if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || match_type(cx, ty, &paths::BTREEMAP) {
++                span_lint_and_then(
++                    cx,
++                    FOR_KV_MAP,
++                    expr.span,
++                    &format!("you seem to want to iterate on a map's {}s", kind),
++                    |diag| {
++                        let map = sugg::Sugg::hir(cx, arg, "map");
++                        multispan_sugg(
++                            diag,
++                            "use the corresponding method".into(),
++                            vec![
++                                (pat_span, snippet(cx, new_pat_span, kind).into_owned()),
++                                (arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl)),
++                            ],
++                        );
++                    },
++                );
++            }
++        }
++    }
++}
++
++struct MutatePairDelegate {
++    hir_id_low: Option<HirId>,
++    hir_id_high: Option<HirId>,
++    span_low: Option<Span>,
++    span_high: Option<Span>,
++}
++
++impl<'tcx> Delegate<'tcx> for MutatePairDelegate {
++    fn consume(&mut self, _: &Place<'tcx>, _: ConsumeMode) {}
++
++    fn borrow(&mut self, cmt: &Place<'tcx>, bk: ty::BorrowKind) {
++        if let ty::BorrowKind::MutBorrow = bk {
++            if let PlaceBase::Local(id) = cmt.base {
++                if Some(id) == self.hir_id_low {
++                    self.span_low = Some(cmt.span)
++                }
++                if Some(id) == self.hir_id_high {
++                    self.span_high = Some(cmt.span)
++                }
++            }
++        }
++    }
++
++    fn mutate(&mut self, cmt: &Place<'tcx>) {
++        if let PlaceBase::Local(id) = cmt.base {
++            if Some(id) == self.hir_id_low {
++                self.span_low = Some(cmt.span)
++            }
++            if Some(id) == self.hir_id_high {
++                self.span_high = Some(cmt.span)
++            }
++        }
++    }
++}
++
++impl<'tcx> MutatePairDelegate {
++    fn mutation_span(&self) -> (Option<Span>, Option<Span>) {
++        (self.span_low, self.span_high)
++    }
++}
++
++fn check_for_mut_range_bound(cx: &LateContext<'_, '_>, arg: &Expr<'_>, body: &Expr<'_>) {
++    if let Some(higher::Range {
++        start: Some(start),
++        end: Some(end),
++        ..
++    }) = higher::range(cx, arg)
++    {
++        let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)];
++        if mut_ids[0].is_some() || mut_ids[1].is_some() {
++            let (span_low, span_high) = check_for_mutation(cx, body, &mut_ids);
++            mut_warn_with_span(cx, span_low);
++            mut_warn_with_span(cx, span_high);
++        }
++    }
++}
++
++fn mut_warn_with_span(cx: &LateContext<'_, '_>, span: Option<Span>) {
++    if let Some(sp) = span {
++        span_lint(
++            cx,
++            MUT_RANGE_BOUND,
++            sp,
++            "attempt to mutate range bound within loop; note that the range of the loop is unchanged",
++        );
++    }
++}
++
++fn check_for_mutability(cx: &LateContext<'_, '_>, bound: &Expr<'_>) -> Option<HirId> {
++    if_chain! {
++        if let ExprKind::Path(ref qpath) = bound.kind;
++        if let QPath::Resolved(None, _) = *qpath;
++        then {
++            let res = qpath_res(cx, qpath, bound.hir_id);
++            if let Res::Local(hir_id) = res {
++                let node_str = cx.tcx.hir().get(hir_id);
++                if_chain! {
++                    if let Node::Binding(pat) = node_str;
++                    if let PatKind::Binding(bind_ann, ..) = pat.kind;
++                    if let BindingAnnotation::Mutable = bind_ann;
++                    then {
++                        return Some(hir_id);
++                    }
++                }
++            }
++        }
++    }
++    None
++}
++
++fn check_for_mutation(
++    cx: &LateContext<'_, '_>,
++    body: &Expr<'_>,
++    bound_ids: &[Option<HirId>],
++) -> (Option<Span>, Option<Span>) {
++    let mut delegate = MutatePairDelegate {
++        hir_id_low: bound_ids[0],
++        hir_id_high: bound_ids[1],
++        span_low: None,
++        span_high: None,
++    };
++    let def_id = body.hir_id.owner.to_def_id();
++    cx.tcx.infer_ctxt().enter(|infcx| {
++        ExprUseVisitor::new(&mut delegate, &infcx, def_id.expect_local(), cx.param_env, cx.tables).walk_expr(body);
++    });
++    delegate.mutation_span()
++}
++
++/// Returns `true` if the pattern is a `PatWild` or an ident prefixed with `_`.
++fn pat_is_wild<'tcx>(pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
++    match *pat {
++        PatKind::Wild => true,
++        PatKind::Binding(.., ident, None) if ident.as_str().starts_with('_') => is_unused(&ident, body),
++        _ => false,
++    }
++}
++
++struct LocalUsedVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    local: HirId,
++    used: bool,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for LocalUsedVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        if same_var(self.cx, expr, self.local) {
++            self.used = true;
++        } else {
++            walk_expr(self, expr);
++        }
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++struct VarVisitor<'a, 'tcx> {
++    /// context reference
++    cx: &'a LateContext<'a, 'tcx>,
++    /// var name to look for as index
++    var: HirId,
++    /// indexed variables that are used mutably
++    indexed_mut: FxHashSet<Name>,
++    /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global
++    indexed_indirectly: FxHashMap<Name, Option<region::Scope>>,
++    /// subset of `indexed` of vars that are indexed directly: `v[i]`
++    /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
++    indexed_directly: FxHashMap<Name, (Option<region::Scope>, Ty<'tcx>)>,
++    /// Any names that are used outside an index operation.
++    /// Used to detect things like `&mut vec` used together with `vec[i]`
++    referenced: FxHashSet<Name>,
++    /// has the loop variable been used in expressions other than the index of
++    /// an index op?
++    nonindex: bool,
++    /// Whether we are inside the `$` in `&mut $` or `$ = foo` or `$.bar`, where bar
++    /// takes `&mut self`
++    prefer_mutable: bool,
++}
++
++impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
++    fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
++        if_chain! {
++            // the indexed container is referenced by a name
++            if let ExprKind::Path(ref seqpath) = seqexpr.kind;
++            if let QPath::Resolved(None, ref seqvar) = *seqpath;
++            if seqvar.segments.len() == 1;
++            then {
++                let index_used_directly = same_var(self.cx, idx, self.var);
++                let indexed_indirectly = {
++                    let mut used_visitor = LocalUsedVisitor {
++                        cx: self.cx,
++                        local: self.var,
++                        used: false,
++                    };
++                    walk_expr(&mut used_visitor, idx);
++                    used_visitor.used
++                };
++
++                if indexed_indirectly || index_used_directly {
++                    if self.prefer_mutable {
++                        self.indexed_mut.insert(seqvar.segments[0].ident.name);
++                    }
++                    let res = qpath_res(self.cx, seqpath, seqexpr.hir_id);
++                    match res {
++                        Res::Local(hir_id) => {
++                            let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id);
++                            let parent_def_id = self.cx.tcx.hir().local_def_id(parent_id);
++                            let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id);
++                            if indexed_indirectly {
++                                self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
++                            }
++                            if index_used_directly {
++                                self.indexed_directly.insert(
++                                    seqvar.segments[0].ident.name,
++                                    (Some(extent), self.cx.tables.node_type(seqexpr.hir_id)),
++                                );
++                            }
++                            return false;  // no need to walk further *on the variable*
++                        }
++                        Res::Def(DefKind::Static | DefKind::Const, ..) => {
++                            if indexed_indirectly {
++                                self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
++                            }
++                            if index_used_directly {
++                                self.indexed_directly.insert(
++                                    seqvar.segments[0].ident.name,
++                                    (None, self.cx.tables.node_type(seqexpr.hir_id)),
++                                );
++                            }
++                            return false;  // no need to walk further *on the variable*
++                        }
++                        _ => (),
++                    }
++                }
++            }
++        }
++        true
++    }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            // a range index op
++            if let ExprKind::MethodCall(ref meth, _, ref args) = expr.kind;
++            if (meth.ident.name == sym!(index) && match_trait_method(self.cx, expr, &paths::INDEX))
++                || (meth.ident.name == sym!(index_mut) && match_trait_method(self.cx, expr, &paths::INDEX_MUT));
++            if !self.check(&args[1], &args[0], expr);
++            then { return }
++        }
++
++        if_chain! {
++            // an index op
++            if let ExprKind::Index(ref seqexpr, ref idx) = expr.kind;
++            if !self.check(idx, seqexpr, expr);
++            then { return }
++        }
++
++        if_chain! {
++            // directly using a variable
++            if let ExprKind::Path(ref qpath) = expr.kind;
++            if let QPath::Resolved(None, ref path) = *qpath;
++            if path.segments.len() == 1;
++            then {
++                if let Res::Local(local_id) = qpath_res(self.cx, qpath, expr.hir_id) {
++                    if local_id == self.var {
++                        self.nonindex = true;
++                    } else {
++                        // not the correct variable, but still a variable
++                        self.referenced.insert(path.segments[0].ident.name);
++                    }
++                }
++            }
++        }
++
++        let old = self.prefer_mutable;
++        match expr.kind {
++            ExprKind::AssignOp(_, ref lhs, ref rhs) | ExprKind::Assign(ref lhs, ref rhs, _) => {
++                self.prefer_mutable = true;
++                self.visit_expr(lhs);
++                self.prefer_mutable = false;
++                self.visit_expr(rhs);
++            },
++            ExprKind::AddrOf(BorrowKind::Ref, mutbl, ref expr) => {
++                if mutbl == Mutability::Mut {
++                    self.prefer_mutable = true;
++                }
++                self.visit_expr(expr);
++            },
++            ExprKind::Call(ref f, args) => {
++                self.visit_expr(f);
++                for expr in args {
++                    let ty = self.cx.tables.expr_ty_adjusted(expr);
++                    self.prefer_mutable = false;
++                    if let ty::Ref(_, _, mutbl) = ty.kind {
++                        if mutbl == Mutability::Mut {
++                            self.prefer_mutable = true;
++                        }
++                    }
++                    self.visit_expr(expr);
++                }
++            },
++            ExprKind::MethodCall(_, _, args) => {
++                let def_id = self.cx.tables.type_dependent_def_id(expr.hir_id).unwrap();
++                for (ty, expr) in self.cx.tcx.fn_sig(def_id).inputs().skip_binder().iter().zip(args) {
++                    self.prefer_mutable = false;
++                    if let ty::Ref(_, _, mutbl) = ty.kind {
++                        if mutbl == Mutability::Mut {
++                            self.prefer_mutable = true;
++                        }
++                    }
++                    self.visit_expr(expr);
++                }
++            },
++            ExprKind::Closure(_, _, body_id, ..) => {
++                let body = self.cx.tcx.hir().body(body_id);
++                self.visit_expr(&body.value);
++            },
++            _ => walk_expr(self, expr),
++        }
++        self.prefer_mutable = old;
++    }
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++fn is_used_inside<'a, 'tcx>(cx: &'a LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, container: &'tcx Expr<'_>) -> bool {
++    let def_id = match var_def_id(cx, expr) {
++        Some(id) => id,
++        None => return false,
++    };
++    if let Some(used_mutably) = mutated_variables(container, cx) {
++        if used_mutably.contains(&def_id) {
++            return true;
++        }
++    }
++    false
++}
++
++fn is_iterator_used_after_while_let<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, iter_expr: &'tcx Expr<'_>) -> bool {
++    let def_id = match var_def_id(cx, iter_expr) {
++        Some(id) => id,
++        None => return false,
++    };
++    let mut visitor = VarUsedAfterLoopVisitor {
++        cx,
++        def_id,
++        iter_expr_id: iter_expr.hir_id,
++        past_while_let: false,
++        var_used_after_while_let: false,
++    };
++    if let Some(enclosing_block) = get_enclosing_block(cx, def_id) {
++        walk_block(&mut visitor, enclosing_block);
++    }
++    visitor.var_used_after_while_let
++}
++
++struct VarUsedAfterLoopVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    def_id: HirId,
++    iter_expr_id: HirId,
++    past_while_let: bool,
++    var_used_after_while_let: bool,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for VarUsedAfterLoopVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        if self.past_while_let {
++            if Some(self.def_id) == var_def_id(self.cx, expr) {
++                self.var_used_after_while_let = true;
++            }
++        } else if self.iter_expr_id == expr.hir_id {
++            self.past_while_let = true;
++        }
++        walk_expr(self, expr);
++    }
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++/// Returns `true` if the type of expr is one that provides `IntoIterator` impls
++/// for `&T` and `&mut T`, such as `Vec`.
++#[rustfmt::skip]
++fn is_ref_iterable_type(cx: &LateContext<'_, '_>, e: &Expr<'_>) -> bool {
++    // no walk_ptrs_ty: calling iter() on a reference can make sense because it
++    // will allow further borrows afterwards
++    let ty = cx.tables.expr_ty(e);
++    is_iterable_array(ty, cx) ||
++    is_type_diagnostic_item(cx, ty, sym!(vec_type)) ||
++    match_type(cx, ty, &paths::LINKED_LIST) ||
++    is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) ||
++    is_type_diagnostic_item(cx, ty, sym!(hashset_type)) ||
++    is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
++    match_type(cx, ty, &paths::BINARY_HEAP) ||
++    match_type(cx, ty, &paths::BTREEMAP) ||
++    match_type(cx, ty, &paths::BTREESET)
++}
++
++fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'_, 'tcx>) -> bool {
++    // IntoIterator is currently only implemented for array sizes <= 32 in rustc
++    match ty.kind {
++        ty::Array(_, n) => {
++            if let Some(val) = n.try_eval_usize(cx.tcx, cx.param_env) {
++                (0..=32).contains(&val)
++            } else {
++                false
++            }
++        },
++        _ => false,
++    }
++}
++
++/// If a block begins with a statement (possibly a `let` binding) and has an
++/// expression, return it.
++fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
++    if block.stmts.is_empty() {
++        return None;
++    }
++    if let StmtKind::Local(ref local) = block.stmts[0].kind {
++        if let Some(expr) = local.init {
++            Some(expr)
++        } else {
++            None
++        }
++    } else {
++        None
++    }
++}
++
++/// If a block begins with an expression (with or without semicolon), return it.
++fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
++    match block.expr {
++        Some(ref expr) if block.stmts.is_empty() => Some(expr),
++        None if !block.stmts.is_empty() => match block.stmts[0].kind {
++            StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => Some(expr),
++            StmtKind::Local(..) | StmtKind::Item(..) => None,
++        },
++        _ => None,
++    }
++}
++
++/// Returns `true` if expr contains a single break expr without destination label
++/// and
++/// passed expression. The expression may be within a block.
++fn is_simple_break_expr(expr: &Expr<'_>) -> bool {
++    match expr.kind {
++        ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true,
++        ExprKind::Block(ref b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)),
++        _ => false,
++    }
++}
++
++// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
++// incremented exactly once in the loop body, and initialized to zero
++// at the start of the loop.
++#[derive(Debug, PartialEq)]
++enum VarState {
++    Initial,  // Not examined yet
++    IncrOnce, // Incremented exactly once, may be a loop counter
++    Declared, // Declared but not (yet) initialized to zero
++    Warn,
++    DontWarn,
++}
++
++/// Scan a for loop for variables that are incremented exactly once.
++struct IncrementVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,      // context reference
++    states: FxHashMap<HirId, VarState>, // incremented variables
++    depth: u32,                         // depth of conditional expressions
++    done: bool,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        if self.done {
++            return;
++        }
++
++        // If node is a variable
++        if let Some(def_id) = var_def_id(self.cx, expr) {
++            if let Some(parent) = get_parent_expr(self.cx, expr) {
++                let state = self.states.entry(def_id).or_insert(VarState::Initial);
++
++                match parent.kind {
++                    ExprKind::AssignOp(op, ref lhs, ref rhs) => {
++                        if lhs.hir_id == expr.hir_id {
++                            if op.node == BinOpKind::Add && is_integer_const(self.cx, rhs, 1) {
++                                *state = match *state {
++                                    VarState::Initial if self.depth == 0 => VarState::IncrOnce,
++                                    _ => VarState::DontWarn,
++                                };
++                            } else {
++                                // Assigned some other value
++                                *state = VarState::DontWarn;
++                            }
++                        }
++                    },
++                    ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => *state = VarState::DontWarn,
++                    ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => {
++                        *state = VarState::DontWarn
++                    },
++                    _ => (),
++                }
++            }
++        } else if is_loop(expr) || is_conditional(expr) {
++            self.depth += 1;
++            walk_expr(self, expr);
++            self.depth -= 1;
++            return;
++        } else if let ExprKind::Continue(_) = expr.kind {
++            self.done = true;
++            return;
++        }
++        walk_expr(self, expr);
++    }
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++/// Checks whether a variable is initialized to zero at the start of a loop.
++struct InitializeVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>, // context reference
++    end_expr: &'tcx Expr<'tcx>,    // the for loop. Stop scanning here.
++    var_id: HirId,
++    state: VarState,
++    name: Option<Name>,
++    depth: u32, // depth of conditional expressions
++    past_loop: bool,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
++        // Look for declarations of the variable
++        if let StmtKind::Local(ref local) = stmt.kind {
++            if local.pat.hir_id == self.var_id {
++                if let PatKind::Binding(.., ident, _) = local.pat.kind {
++                    self.name = Some(ident.name);
++
++                    self.state = if let Some(ref init) = local.init {
++                        if is_integer_const(&self.cx, init, 0) {
++                            VarState::Warn
++                        } else {
++                            VarState::Declared
++                        }
++                    } else {
++                        VarState::Declared
++                    }
++                }
++            }
++        }
++        walk_stmt(self, stmt);
++    }
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        if self.state == VarState::DontWarn {
++            return;
++        }
++        if SpanlessEq::new(self.cx).eq_expr(&expr, self.end_expr) {
++            self.past_loop = true;
++            return;
++        }
++        // No need to visit expressions before the variable is
++        // declared
++        if self.state == VarState::IncrOnce {
++            return;
++        }
++
++        // If node is the desired variable, see how it's used
++        if var_def_id(self.cx, expr) == Some(self.var_id) {
++            if let Some(parent) = get_parent_expr(self.cx, expr) {
++                match parent.kind {
++                    ExprKind::AssignOp(_, ref lhs, _) if lhs.hir_id == expr.hir_id => {
++                        self.state = VarState::DontWarn;
++                    },
++                    ExprKind::Assign(ref lhs, ref rhs, _) if lhs.hir_id == expr.hir_id => {
++                        self.state = if is_integer_const(&self.cx, rhs, 0) && self.depth == 0 {
++                            VarState::Warn
++                        } else {
++                            VarState::DontWarn
++                        }
++                    },
++                    ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => {
++                        self.state = VarState::DontWarn
++                    },
++                    _ => (),
++                }
++            }
++
++            if self.past_loop {
++                self.state = VarState::DontWarn;
++                return;
++            }
++        } else if !self.past_loop && is_loop(expr) {
++            self.state = VarState::DontWarn;
++            return;
++        } else if is_conditional(expr) {
++            self.depth += 1;
++            walk_expr(self, expr);
++            self.depth -= 1;
++            return;
++        }
++        walk_expr(self, expr);
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
++    }
++}
++
++fn var_def_id(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option<HirId> {
++    if let ExprKind::Path(ref qpath) = expr.kind {
++        let path_res = qpath_res(cx, qpath, expr.hir_id);
++        if let Res::Local(hir_id) = path_res {
++            return Some(hir_id);
++        }
++    }
++    None
++}
++
++fn is_loop(expr: &Expr<'_>) -> bool {
++    match expr.kind {
++        ExprKind::Loop(..) => true,
++        _ => false,
++    }
++}
++
++fn is_conditional(expr: &Expr<'_>) -> bool {
++    match expr.kind {
++        ExprKind::Match(..) => true,
++        _ => false,
++    }
++}
++
++fn is_nested(cx: &LateContext<'_, '_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool {
++    if_chain! {
++        if let Some(loop_block) = get_enclosing_block(cx, match_expr.hir_id);
++        let parent_node = cx.tcx.hir().get_parent_node(loop_block.hir_id);
++        if let Some(Node::Expr(loop_expr)) = cx.tcx.hir().find(parent_node);
++        then {
++            return is_loop_nested(cx, loop_expr, iter_expr)
++        }
++    }
++    false
++}
++
++fn is_loop_nested(cx: &LateContext<'_, '_>, loop_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool {
++    let mut id = loop_expr.hir_id;
++    let iter_name = if let Some(name) = path_name(iter_expr) {
++        name
++    } else {
++        return true;
++    };
++    loop {
++        let parent = cx.tcx.hir().get_parent_node(id);
++        if parent == id {
++            return false;
++        }
++        match cx.tcx.hir().find(parent) {
++            Some(Node::Expr(expr)) => {
++                if let ExprKind::Loop(..) = expr.kind {
++                    return true;
++                };
++            },
++            Some(Node::Block(block)) => {
++                let mut block_visitor = LoopNestVisitor {
++                    hir_id: id,
++                    iterator: iter_name,
++                    nesting: Unknown,
++                };
++                walk_block(&mut block_visitor, block);
++                if block_visitor.nesting == RuledOut {
++                    return false;
++                }
++            },
++            Some(Node::Stmt(_)) => (),
++            _ => {
++                return false;
++            },
++        }
++        id = parent;
++    }
++}
++
++#[derive(PartialEq, Eq)]
++enum Nesting {
++    Unknown,     // no nesting detected yet
++    RuledOut,    // the iterator is initialized or assigned within scope
++    LookFurther, // no nesting detected, no further walk required
++}
++
++use self::Nesting::{LookFurther, RuledOut, Unknown};
++
++struct LoopNestVisitor {
++    hir_id: HirId,
++    iterator: Name,
++    nesting: Nesting,
++}
++
++impl<'tcx> Visitor<'tcx> for LoopNestVisitor {
++    type Map = Map<'tcx>;
++
++    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
++        if stmt.hir_id == self.hir_id {
++            self.nesting = LookFurther;
++        } else if self.nesting == Unknown {
++            walk_stmt(self, stmt);
++        }
++    }
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        if self.nesting != Unknown {
++            return;
++        }
++        if expr.hir_id == self.hir_id {
++            self.nesting = LookFurther;
++            return;
++        }
++        match expr.kind {
++            ExprKind::Assign(ref path, _, _) | ExprKind::AssignOp(_, ref path, _) => {
++                if match_var(path, self.iterator) {
++                    self.nesting = RuledOut;
++                }
++            },
++            _ => walk_expr(self, expr),
++        }
++    }
++
++    fn visit_pat(&mut self, pat: &'tcx Pat<'_>) {
++        if self.nesting != Unknown {
++            return;
++        }
++        if let PatKind::Binding(.., span_name, _) = pat.kind {
++            if self.iterator == span_name.name {
++                self.nesting = RuledOut;
++                return;
++            }
++        }
++        walk_pat(self, pat)
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++fn path_name(e: &Expr<'_>) -> Option<Name> {
++    if let ExprKind::Path(QPath::Resolved(_, ref path)) = e.kind {
++        let segments = &path.segments;
++        if segments.len() == 1 {
++            return Some(segments[0].ident.name);
++        }
++    };
++    None
++}
++
++fn check_infinite_loop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) {
++    if constant(cx, cx.tables, cond).is_some() {
++        // A pure constant condition (e.g., `while false`) is not linted.
++        return;
++    }
++
++    let mut var_visitor = VarCollectorVisitor {
++        cx,
++        ids: FxHashSet::default(),
++        def_ids: FxHashMap::default(),
++        skip: false,
++    };
++    var_visitor.visit_expr(cond);
++    if var_visitor.skip {
++        return;
++    }
++    let used_in_condition = &var_visitor.ids;
++    let no_cond_variable_mutated = if let Some(used_mutably) = mutated_variables(expr, cx) {
++        used_in_condition.is_disjoint(&used_mutably)
++    } else {
++        return;
++    };
++    let mutable_static_in_cond = var_visitor.def_ids.iter().any(|(_, v)| *v);
++
++    let mut has_break_or_return_visitor = HasBreakOrReturnVisitor {
++        has_break_or_return: false,
++    };
++    has_break_or_return_visitor.visit_expr(expr);
++    let has_break_or_return = has_break_or_return_visitor.has_break_or_return;
++
++    if no_cond_variable_mutated && !mutable_static_in_cond {
++        span_lint_and_then(
++            cx,
++            WHILE_IMMUTABLE_CONDITION,
++            cond.span,
++            "variables in the condition are not mutated in the loop body",
++            |diag| {
++                diag.note("this may lead to an infinite or to a never running loop");
++
++                if has_break_or_return {
++                    diag.note("this loop contains `return`s or `break`s");
++                    diag.help("rewrite it as `if cond { loop { } }`");
++                }
++            },
++        );
++    }
++}
++
++struct HasBreakOrReturnVisitor {
++    has_break_or_return: bool,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for HasBreakOrReturnVisitor {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        if self.has_break_or_return {
++            return;
++        }
++
++        match expr.kind {
++            ExprKind::Ret(_) | ExprKind::Break(_, _) => {
++                self.has_break_or_return = true;
++                return;
++            },
++            _ => {},
++        }
++
++        walk_expr(self, expr);
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++/// Collects the set of variables in an expression
++/// Stops analysis if a function call is found
++/// Note: In some cases such as `self`, there are no mutable annotation,
++/// All variables definition IDs are collected
++struct VarCollectorVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    ids: FxHashSet<HirId>,
++    def_ids: FxHashMap<def_id::DefId, bool>,
++    skip: bool,
++}
++
++impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> {
++    fn insert_def_id(&mut self, ex: &'tcx Expr<'_>) {
++        if_chain! {
++            if let ExprKind::Path(ref qpath) = ex.kind;
++            if let QPath::Resolved(None, _) = *qpath;
++            let res = qpath_res(self.cx, qpath, ex.hir_id);
++            then {
++                match res {
++                    Res::Local(hir_id) => {
++                        self.ids.insert(hir_id);
++                    },
++                    Res::Def(DefKind::Static, def_id) => {
++                        let mutable = self.cx.tcx.is_mutable_static(def_id);
++                        self.def_ids.insert(def_id, mutable);
++                    },
++                    _ => {},
++                }
++            }
++        }
++    }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
++        match ex.kind {
++            ExprKind::Path(_) => self.insert_def_id(ex),
++            // If there is any function/method call… we just stop analysis
++            ExprKind::Call(..) | ExprKind::MethodCall(..) => self.skip = true,
++
++            _ => walk_expr(self, ex),
++        }
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
++
++fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'a, 'tcx>) {
++    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 let Some(ref generic_args) = chain_method.args;
++        if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
++        then {
++            let ty = cx.tables.node_type(ty.hir_id);
++            if is_type_diagnostic_item(cx, ty, sym!(vec_type)) ||
++                is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
++                match_type(cx, ty, &paths::BTREEMAP) ||
++                is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) {
++                if method.ident.name == sym!(len) {
++                    let span = shorten_needless_collect_span(expr);
++                    span_lint_and_sugg(
++                        cx,
++                        NEEDLESS_COLLECT,
++                        span,
++                        NEEDLESS_COLLECT_MSG,
++                        "replace with",
++                        ".count()".to_string(),
++                        Applicability::MachineApplicable,
++                    );
++                }
++                if method.ident.name == sym!(is_empty) {
++                    let span = shorten_needless_collect_span(expr);
++                    span_lint_and_sugg(
++                        cx,
++                        NEEDLESS_COLLECT,
++                        span,
++                        NEEDLESS_COLLECT_MSG,
++                        "replace with",
++                        ".next().is_none()".to_string(),
++                        Applicability::MachineApplicable,
++                    );
++                }
++                if method.ident.name == sym!(contains) {
++                    let contains_arg = snippet(cx, args[1].span, "??");
++                    let span = shorten_needless_collect_span(expr);
++                    span_lint_and_then(
++                        cx,
++                        NEEDLESS_COLLECT,
++                        span,
++                        NEEDLESS_COLLECT_MSG,
++                        |diag| {
++                            let (arg, pred) = if contains_arg.starts_with('&') {
++                                ("x", &contains_arg[1..])
++                            } else {
++                                ("&x", &*contains_arg)
++                            };
++                            diag.span_suggestion(
++                                span,
++                                "replace with",
++                                format!(
++                                    ".any(|{}| x == {})",
++                                    arg, pred
++                                ),
++                                Applicability::MachineApplicable,
++                            );
++                        }
++                    );
++                }
++            }
++        }
++    }
++}
++
++fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span {
++    if_chain! {
++        if let ExprKind::MethodCall(_, _, ref args) = expr.kind;
++        if let ExprKind::MethodCall(_, ref span, _) = args[0].kind;
++        then {
++            return expr.span.with_lo(span.lo() - BytePos(1));
++        }
++    }
++    unreachable!()
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b1345d0b751ae3b22acf860b2a591fcf547e1f9a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++use crate::utils::{snippet, span_lint_and_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 rustc_span::edition::Edition;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `#[macro_use] use...`.
++    ///
++    /// **Why is this bad?** Since the Rust 2018 edition you can import
++    /// macro's directly, this is considered idiomatic.
++    ///
++    /// **Known problems:** This lint does not generate an auto-applicable suggestion.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// #[macro_use]
++    /// use lazy_static;
++    /// ```
++    pub MACRO_USE_IMPORTS,
++    pedantic,
++    "#[macro_use] is no longer needed"
++}
++
++declare_lint_pass!(MacroUseImports => [MACRO_USE_IMPORTS]);
++
++impl EarlyLintPass for MacroUseImports {
++    fn check_item(&mut self, ecx: &EarlyContext<'_>, item: &ast::Item) {
++        if_chain! {
++            if ecx.sess.opts.edition == Edition::Edition2018;
++            if let ast::ItemKind::Use(use_tree) = &item.kind;
++            if let Some(mac_attr) = item
++                .attrs
++                .iter()
++                .find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string()));
++            then {
++                let msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition";
++                let help = format!("use {}::<macro name>", snippet(ecx, use_tree.span, "_"));
++                span_lint_and_sugg(
++                    ecx,
++                    MACRO_USE_IMPORTS,
++                    mac_attr.span,
++                    msg,
++                    "remove the attribute and import the macro directly, try",
++                    help,
++                    Applicability::HasPlaceholders,
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8a0e47a3d31c552bd0026c366991ba82ac8ed4b4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++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.
++    ///
++    /// **Why is this bad?** Apart from special setups (which we could detect following attributes like #![no_std]),
++    /// recursing into main() seems like an unintuitive antipattern we should be able to detect.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```no_run
++    /// fn main() {
++    ///     main();
++    /// }
++    /// ```
++    pub MAIN_RECURSION,
++    style,
++    "recursion using the entrypoint"
++}
++
++#[derive(Default)]
++pub struct MainRecursion {
++    has_no_std_attr: bool,
++}
++
++impl_lint_pass!(MainRecursion => [MAIN_RECURSION]);
++
++impl LateLintPass<'_, '_> for MainRecursion {
++    fn check_crate(&mut self, _: &LateContext<'_, '_>, krate: &Crate<'_>) {
++        self.has_no_std_attr = is_no_std_crate(krate);
++    }
++
++    fn check_expr_post(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++        if self.has_no_std_attr {
++            return;
++        }
++
++        if_chain! {
++            if let ExprKind::Call(func, _) = &expr.kind;
++            if let ExprKind::Path(path) = &func.kind;
++            if let QPath::Resolved(_, path) = &path;
++            if let Some(def_id) = path.res.opt_def_id();
++            if is_entrypoint_fn(cx, def_id);
++            then {
++                span_lint_and_help(
++                    cx,
++                    MAIN_RECURSION,
++                    func.span,
++                    &format!("recursing into entrypoint `{}`", snippet(cx, func.span, "main")),
++                    None,
++                    "consider using another function for this recursion"
++                )
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0b346393ac3892cede0117ea618d13ab9f803d65
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,150 @@@
++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 if_chain::if_chain;
++use rustc_ast::ast::Ident;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::mir::Mutability;
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests
++    /// `iterator.cloned()` instead
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// let x = vec![42, 43];
++    /// let y = x.iter();
++    /// let z = y.map(|i| *i);
++    /// ```
++    ///
++    /// The correct use would be:
++    ///
++    /// ```rust
++    /// let x = vec![42, 43];
++    /// let y = x.iter();
++    /// let z = y.cloned();
++    /// ```
++    pub MAP_CLONE,
++    style,
++    "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
++}
++
++declare_lint_pass!(MapClone => [MAP_CLONE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MapClone {
++    fn check_expr(&mut self, cx: &LateContext<'_, '_>, e: &hir::Expr<'_>) {
++        if e.span.from_expansion() {
++            return;
++        }
++
++        if_chain! {
++            if let hir::ExprKind::MethodCall(ref method, _, ref args) = e.kind;
++            if args.len() == 2;
++            if method.ident.as_str() == "map";
++            let ty = cx.tables.expr_ty(&args[0]);
++            if is_type_diagnostic_item(cx, ty, sym!(option_type)) || match_trait_method(cx, e, &paths::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);
++            then {
++                match closure_body.params[0].pat.kind {
++                    hir::PatKind::Ref(ref inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
++                        hir::BindingAnnotation::Unannotated, .., name, None
++                    ) = inner.kind {
++                        if ident_eq(name, closure_expr) {
++                            lint(cx, e.span, args[0].span, true);
++                        }
++                    },
++                    hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
++                        match closure_expr.kind {
++                            hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner) => {
++                                if ident_eq(name, inner) {
++                                    if let ty::Ref(.., Mutability::Not) = cx.tables.expr_ty(inner).kind {
++                                        lint(cx, e.span, args[0].span, true);
++                                    }
++                                }
++                            },
++                            hir::ExprKind::MethodCall(ref method, _, ref obj) => {
++                                if ident_eq(name, &obj[0]) && method.ident.as_str() == "clone"
++                                    && match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) {
++
++                                    let obj_ty = cx.tables.expr_ty(&obj[0]);
++                                    if let ty::Ref(_, ty, _) = obj_ty.kind {
++                                        let copy = is_copy(cx, ty);
++                                        lint(cx, e.span, args[0].span, copy);
++                                    } else {
++                                        lint_needless_cloning(cx, e.span, args[0].span);
++                                    }
++                                }
++                            },
++                            _ => {},
++                        }
++                    },
++                    _ => {},
++                }
++            }
++        }
++    }
++}
++
++fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
++    if let hir::ExprKind::Path(hir::QPath::Resolved(None, ref path)) = path.kind {
++        path.segments.len() == 1 && path.segments[0].ident == name
++    } else {
++        false
++    }
++}
++
++fn lint_needless_cloning(cx: &LateContext<'_, '_>, root: Span, receiver: Span) {
++    span_lint_and_sugg(
++        cx,
++        MAP_CLONE,
++        root.trim_start(receiver).unwrap(),
++        "You are needlessly cloning iterator elements",
++        "Remove the `map` call",
++        String::new(),
++        Applicability::MachineApplicable,
++    )
++}
++
++fn lint(cx: &LateContext<'_, '_>, replace: Span, root: Span, copied: bool) {
++    let mut applicability = Applicability::MachineApplicable;
++    if copied {
++        span_lint_and_sugg(
++            cx,
++            MAP_CLONE,
++            replace,
++            "You are using an explicit closure for copying elements",
++            "Consider calling the dedicated `copied` method",
++            format!(
++                "{}.copied()",
++                snippet_with_applicability(cx, root, "..", &mut applicability)
++            ),
++            applicability,
++        )
++    } else {
++        span_lint_and_sugg(
++            cx,
++            MAP_CLONE,
++            replace,
++            "You are using an explicit closure for cloning elements",
++            "Consider calling the dedicated `cloned` method",
++            format!(
++                "{}.cloned()",
++                snippet_with_applicability(cx, root, "..", &mut applicability)
++            ),
++            applicability,
++        )
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fecd91c7814dcc8eb8fa3c3f2c58c7bb4ca0cb49
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,273 @@@
++use crate::utils::{is_type_diagnostic_item, iter_input_pats, method_chain_args, snippet, span_lint_and_then};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `option.map(f)` where f is a function
++    /// or closure that returns the unit type.
++    ///
++    /// **Why is this bad?** Readability, this can be written more clearly with
++    /// an if let statement
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// # fn do_stuff() -> Option<String> { Some(String::new()) }
++    /// # fn log_err_msg(foo: String) -> Option<String> { Some(foo) }
++    /// # fn format_msg(foo: String) -> String { String::new() }
++    /// let x: Option<String> = do_stuff();
++    /// x.map(log_err_msg);
++    /// # let x: Option<String> = do_stuff();
++    /// x.map(|msg| log_err_msg(format_msg(msg)));
++    /// ```
++    ///
++    /// The correct use would be:
++    ///
++    /// ```rust
++    /// # fn do_stuff() -> Option<String> { Some(String::new()) }
++    /// # fn log_err_msg(foo: String) -> Option<String> { Some(foo) }
++    /// # fn format_msg(foo: String) -> String { String::new() }
++    /// let x: Option<String> = do_stuff();
++    /// if let Some(msg) = x {
++    ///     log_err_msg(msg);
++    /// }
++    ///
++    /// # let x: Option<String> = do_stuff();
++    /// if let Some(msg) = x {
++    ///     log_err_msg(format_msg(msg));
++    /// }
++    /// ```
++    pub OPTION_MAP_UNIT_FN,
++    complexity,
++    "using `option.map(f)`, where `f` is a function or closure that returns `()`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `result.map(f)` where f is a function
++    /// or closure that returns the unit type.
++    ///
++    /// **Why is this bad?** Readability, this can be written more clearly with
++    /// an if let statement
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// # fn do_stuff() -> Result<String, String> { Ok(String::new()) }
++    /// # fn log_err_msg(foo: String) -> Result<String, String> { Ok(foo) }
++    /// # fn format_msg(foo: String) -> String { String::new() }
++    /// let x: Result<String, String> = do_stuff();
++    /// x.map(log_err_msg);
++    /// # let x: Result<String, String> = do_stuff();
++    /// x.map(|msg| log_err_msg(format_msg(msg)));
++    /// ```
++    ///
++    /// The correct use would be:
++    ///
++    /// ```rust
++    /// # fn do_stuff() -> Result<String, String> { Ok(String::new()) }
++    /// # fn log_err_msg(foo: String) -> Result<String, String> { Ok(foo) }
++    /// # fn format_msg(foo: String) -> String { String::new() }
++    /// let x: Result<String, String> = do_stuff();
++    /// if let Ok(msg) = x {
++    ///     log_err_msg(msg);
++    /// };
++    /// # let x: Result<String, String> = do_stuff();
++    /// if let Ok(msg) = x {
++    ///     log_err_msg(format_msg(msg));
++    /// };
++    /// ```
++    pub RESULT_MAP_UNIT_FN,
++    complexity,
++    "using `result.map(f)`, where `f` is a function or closure that returns `()`"
++}
++
++declare_lint_pass!(MapUnit => [OPTION_MAP_UNIT_FN, RESULT_MAP_UNIT_FN]);
++
++fn is_unit_type(ty: Ty<'_>) -> bool {
++    match ty.kind {
++        ty::Tuple(slice) => slice.is_empty(),
++        ty::Never => true,
++        _ => false,
++    }
++}
++
++fn is_unit_function(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) -> bool {
++    let ty = cx.tables.expr_ty(expr);
++
++    if let ty::FnDef(id, _) = ty.kind {
++        if let Some(fn_type) = cx.tcx.fn_sig(id).no_bound_vars() {
++            return is_unit_type(fn_type.output());
++        }
++    }
++    false
++}
++
++fn is_unit_expression(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) -> bool {
++    is_unit_type(cx.tables.expr_ty(expr))
++}
++
++/// The expression inside a closure may or may not have surrounding braces and
++/// semicolons, which causes problems when generating a suggestion. Given an
++/// expression that evaluates to '()' or '!', recursively remove useless braces
++/// and semi-colons until is suitable for including in the suggestion template
++fn reduce_unit_expression<'a>(cx: &LateContext<'_, '_>, expr: &'a hir::Expr<'_>) -> Option<Span> {
++    if !is_unit_expression(cx, expr) {
++        return None;
++    }
++
++    match expr.kind {
++        hir::ExprKind::Call(_, _) | hir::ExprKind::MethodCall(_, _, _) => {
++            // Calls can't be reduced any more
++            Some(expr.span)
++        },
++        hir::ExprKind::Block(ref block, _) => {
++            match (&block.stmts[..], block.expr.as_ref()) {
++                (&[], Some(inner_expr)) => {
++                    // If block only contains an expression,
++                    // reduce `{ X }` to `X`
++                    reduce_unit_expression(cx, inner_expr)
++                },
++                (&[ref inner_stmt], None) => {
++                    // If block only contains statements,
++                    // reduce `{ X; }` to `X` or `X;`
++                    match inner_stmt.kind {
++                        hir::StmtKind::Local(ref local) => Some(local.span),
++                        hir::StmtKind::Expr(ref e) => Some(e.span),
++                        hir::StmtKind::Semi(..) => Some(inner_stmt.span),
++                        hir::StmtKind::Item(..) => None,
++                    }
++                },
++                _ => {
++                    // For closures that contain multiple statements
++                    // it's difficult to get a correct suggestion span
++                    // for all cases (multi-line closures specifically)
++                    //
++                    // We do not attempt to build a suggestion for those right now.
++                    None
++                },
++            }
++        },
++        _ => None,
++    }
++}
++
++fn unit_closure<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &'a hir::Expr<'a>,
++) -> Option<(&'tcx hir::Param<'tcx>, &'a hir::Expr<'a>)> {
++    if let hir::ExprKind::Closure(_, ref decl, inner_expr_id, _, _) = expr.kind {
++        let body = cx.tcx.hir().body(inner_expr_id);
++        let body_expr = &body.value;
++
++        if_chain! {
++            if decl.inputs.len() == 1;
++            if is_unit_expression(cx, body_expr);
++            if let Some(binding) = iter_input_pats(&decl, body).next();
++            then {
++                return Some((binding, body_expr));
++            }
++        }
++    }
++    None
++}
++
++/// Builds a name for the let binding variable (`var_arg`)
++///
++/// `x.field` => `x_field`
++/// `y` => `_y`
++///
++/// Anything else will return `a`.
++fn let_binding_name(cx: &LateContext<'_, '_>, var_arg: &hir::Expr<'_>) -> String {
++    match &var_arg.kind {
++        hir::ExprKind::Field(_, _) => snippet(cx, var_arg.span, "_").replace(".", "_"),
++        hir::ExprKind::Path(_) => format!("_{}", snippet(cx, var_arg.span, "")),
++        _ => "a".to_string(),
++    }
++}
++
++#[must_use]
++fn suggestion_msg(function_type: &str, map_type: &str) -> String {
++    format!(
++        "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type",
++        map_type, function_type
++    )
++}
++
++fn lint_map_unit_fn(cx: &LateContext<'_, '_>, stmt: &hir::Stmt<'_>, expr: &hir::Expr<'_>, map_args: &[hir::Expr<'_>]) {
++    let var_arg = &map_args[0];
++
++    let (map_type, variant, lint) = if is_type_diagnostic_item(cx, cx.tables.expr_ty(var_arg), sym!(option_type)) {
++        ("Option", "Some", OPTION_MAP_UNIT_FN)
++    } else if is_type_diagnostic_item(cx, cx.tables.expr_ty(var_arg), sym!(result_type)) {
++        ("Result", "Ok", RESULT_MAP_UNIT_FN)
++    } else {
++        return;
++    };
++    let fn_arg = &map_args[1];
++
++    if is_unit_function(cx, fn_arg) {
++        let msg = suggestion_msg("function", map_type);
++        let suggestion = format!(
++            "if let {0}({binding}) = {1} {{ {2}({binding}) }}",
++            variant,
++            snippet(cx, var_arg.span, "_"),
++            snippet(cx, fn_arg.span, "_"),
++            binding = let_binding_name(cx, var_arg)
++        );
++
++        span_lint_and_then(cx, lint, expr.span, &msg, |diag| {
++            diag.span_suggestion(stmt.span, "try this", suggestion, Applicability::MachineApplicable);
++        });
++    } else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) {
++        let msg = suggestion_msg("closure", map_type);
++
++        span_lint_and_then(cx, lint, expr.span, &msg, |diag| {
++            if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) {
++                let suggestion = format!(
++                    "if let {0}({1}) = {2} {{ {3} }}",
++                    variant,
++                    snippet(cx, binding.pat.span, "_"),
++                    snippet(cx, var_arg.span, "_"),
++                    snippet(cx, reduced_expr_span, "_")
++                );
++                diag.span_suggestion(
++                    stmt.span,
++                    "try this",
++                    suggestion,
++                    Applicability::MachineApplicable, // snippet
++                );
++            } else {
++                let suggestion = format!(
++                    "if let {0}({1}) = {2} {{ ... }}",
++                    variant,
++                    snippet(cx, binding.pat.span, "_"),
++                    snippet(cx, var_arg.span, "_"),
++                );
++                diag.span_suggestion(stmt.span, "try this", suggestion, Applicability::HasPlaceholders);
++            }
++        });
++    }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MapUnit {
++    fn check_stmt(&mut self, cx: &LateContext<'_, '_>, stmt: &hir::Stmt<'_>) {
++        if stmt.span.from_expansion() {
++            return;
++        }
++
++        if let hir::StmtKind::Semi(ref expr) = stmt.kind {
++            if let Some(arglists) = method_chain_args(expr, &["map"]) {
++                lint_map_unit_fn(cx, stmt, expr, arglists[0]);
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4071406cc84c3366cd29e94d6e0012038ebe0cf1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,89 @@@
++use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, MatchSource};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `match vec[idx]` or `match vec[n..m]`.
++    ///
++    /// **Why is this bad?** This can panic at runtime.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust, no_run
++    /// let arr = vec![0, 1, 2, 3];
++    /// let idx = 1;
++    ///
++    /// // Bad
++    /// match arr[idx] {
++    ///     0 => println!("{}", 0),
++    ///     1 => println!("{}", 3),
++    ///     _ => {},
++    /// }
++    /// ```
++    /// Use instead:
++    /// ```rust, no_run
++    /// let arr = vec![0, 1, 2, 3];
++    /// let idx = 1;
++    ///
++    /// // Good
++    /// match arr.get(idx) {
++    ///     Some(0) => println!("{}", 0),
++    ///     Some(1) => println!("{}", 3),
++    ///     _ => {},
++    /// }
++    /// ```
++    pub MATCH_ON_VEC_ITEMS,
++    correctness,
++    "matching on vector elements can panic"
++}
++
++declare_lint_pass!(MatchOnVecItems => [MATCH_ON_VEC_ITEMS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MatchOnVecItems {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) {
++        if_chain! {
++            if !in_external_macro(cx.sess(), expr.span);
++            if let ExprKind::Match(ref match_expr, _, MatchSource::Normal) = expr.kind;
++            if let Some(idx_expr) = is_vec_indexing(cx, match_expr);
++            if let ExprKind::Index(vec, idx) = idx_expr.kind;
++
++            then {
++                // FIXME: could be improved to suggest surrounding every pattern with Some(_),
++                // but only when `or_patterns` are stabilized.
++                span_lint_and_sugg(
++                    cx,
++                    MATCH_ON_VEC_ITEMS,
++                    match_expr.span,
++                    "indexing into a vector may panic",
++                    "try this",
++                    format!(
++                        "{}.get({})",
++                        snippet(cx, vec.span, ".."),
++                        snippet(cx, idx.span, "..")
++                    ),
++                    Applicability::MaybeIncorrect
++                );
++            }
++        }
++    }
++}
++
++fn is_vec_indexing<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
++    if_chain! {
++        if let ExprKind::Index(ref array, _) = expr.kind;
++        let ty = cx.tables.expr_ty(array);
++        let ty = walk_ptrs_ty(ty);
++        if is_type_diagnostic_item(cx, ty, sym!(vec_type));
++
++        then {
++            return Some(expr);
++        }
++    }
++
++    None
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8f86535ef1e0f25aeea9de1792099c8541815bc0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1242 @@@
++use crate::consts::{constant, miri_to_const, Constant};
++use crate::utils::paths;
++use crate::utils::sugg::Sugg;
++use crate::utils::usage::is_unused;
++use crate::utils::{
++    expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
++    is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet,
++    snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
++    span_lint_and_then, walk_ptrs_ty,
++};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::def::CtorKind;
++use rustc_hir::{
++    Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, Node, Pat, PatKind,
++    QPath, RangeEnd,
++};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++use std::cmp::Ordering;
++use std::collections::Bound;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for matches with a single arm where an `if let`
++    /// will usually suffice.
++    ///
++    /// **Why is this bad?** Just readability – `if let` nests less than a `match`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # fn bar(stool: &str) {}
++    /// # let x = Some("abc");
++    /// match x {
++    ///     Some(ref foo) => bar(foo),
++    ///     _ => (),
++    /// }
++    /// ```
++    pub SINGLE_MATCH,
++    style,
++    "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for matches with two arms where an `if let else` will
++    /// usually suffice.
++    ///
++    /// **Why is this bad?** Just readability – `if let` nests less than a `match`.
++    ///
++    /// **Known problems:** Personal style preferences may differ.
++    ///
++    /// **Example:**
++    ///
++    /// Using `match`:
++    ///
++    /// ```rust
++    /// # fn bar(foo: &usize) {}
++    /// # let other_ref: usize = 1;
++    /// # let x: Option<&usize> = Some(&1);
++    /// match x {
++    ///     Some(ref foo) => bar(foo),
++    ///     _ => bar(&other_ref),
++    /// }
++    /// ```
++    ///
++    /// Using `if let` with `else`:
++    ///
++    /// ```rust
++    /// # fn bar(foo: &usize) {}
++    /// # let other_ref: usize = 1;
++    /// # let x: Option<&usize> = Some(&1);
++    /// if let Some(ref foo) = x {
++    ///     bar(foo);
++    /// } else {
++    ///     bar(&other_ref);
++    /// }
++    /// ```
++    pub SINGLE_MATCH_ELSE,
++    pedantic,
++    "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for matches where all arms match a reference,
++    /// suggesting to remove the reference and deref the matched expression
++    /// instead. It also checks for `if let &foo = bar` blocks.
++    ///
++    /// **Why is this bad?** It just makes the code less readable. That reference
++    /// destructuring adds nothing to the code.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// match x {
++    ///     &A(ref y) => foo(y),
++    ///     &B => bar(),
++    ///     _ => frob(&x),
++    /// }
++    /// ```
++    pub MATCH_REF_PATS,
++    style,
++    "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for matches where match expression is a `bool`. It
++    /// suggests to replace the expression with an `if...else` block.
++    ///
++    /// **Why is this bad?** It makes the code less readable.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # fn foo() {}
++    /// # fn bar() {}
++    /// let condition: bool = true;
++    /// match condition {
++    ///     true => foo(),
++    ///     false => bar(),
++    /// }
++    /// ```
++    /// Use if/else instead:
++    /// ```rust
++    /// # fn foo() {}
++    /// # fn bar() {}
++    /// let condition: bool = true;
++    /// if condition {
++    ///     foo();
++    /// } else {
++    ///     bar();
++    /// }
++    /// ```
++    pub MATCH_BOOL,
++    pedantic,
++    "a `match` on a boolean expression instead of an `if..else` block"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for overlapping match arms.
++    ///
++    /// **Why is this bad?** It is likely to be an error and if not, makes the code
++    /// less obvious.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x = 5;
++    /// match x {
++    ///     1...10 => println!("1 ... 10"),
++    ///     5...15 => println!("5 ... 15"),
++    ///     _ => (),
++    /// }
++    /// ```
++    pub MATCH_OVERLAPPING_ARM,
++    style,
++    "a `match` with overlapping arms"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for arm which matches all errors with `Err(_)`
++    /// and take drastic actions like `panic!`.
++    ///
++    /// **Why is this bad?** It is generally a bad practice, just like
++    /// catching all exceptions in java with `catch(Exception)`
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x: Result<i32, &str> = Ok(3);
++    /// match x {
++    ///     Ok(_) => println!("ok"),
++    ///     Err(_) => panic!("err"),
++    /// }
++    /// ```
++    pub MATCH_WILD_ERR_ARM,
++    style,
++    "a `match` with `Err(_)` arm and take drastic actions"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for match which is used to add a reference to an
++    /// `Option` value.
++    ///
++    /// **Why is this bad?** Using `as_ref()` or `as_mut()` instead is shorter.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x: Option<()> = None;
++    /// let r: Option<&()> = match x {
++    ///     None => None,
++    ///     Some(ref v) => Some(v),
++    /// };
++    /// ```
++    pub MATCH_AS_REF,
++    complexity,
++    "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for wildcard enum matches using `_`.
++    ///
++    /// **Why is this bad?** New enum variants added by library updates can be missed.
++    ///
++    /// **Known problems:** Suggested replacements may be incorrect if guards exhaustively cover some
++    /// variants, and also may not use correct path to enum if it's not present in the current scope.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # enum Foo { A(usize), B(usize) }
++    /// # let x = Foo::B(1);
++    /// match x {
++    ///     A => {},
++    ///     _ => {},
++    /// }
++    /// ```
++    pub WILDCARD_ENUM_MATCH_ARM,
++    restriction,
++    "a wildcard enum match arm using `_`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for wildcard pattern used with others patterns in same match arm.
++    ///
++    /// **Why is this bad?** Wildcard pattern already covers any other pattern as it will match anyway.
++    /// It makes the code less readable, especially to spot wildcard pattern use in match arm.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// match "foo" {
++    ///     "a" => {},
++    ///     "bar" | _ => {},
++    /// }
++    /// ```
++    pub WILDCARD_IN_OR_PATTERNS,
++    complexity,
++    "a wildcard pattern used with others patterns in same match arm"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for matches being used to destructure a single-variant enum
++    /// or tuple struct where a `let` will suffice.
++    ///
++    /// **Why is this bad?** Just readability – `let` doesn't nest, whereas a `match` does.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// enum Wrapper {
++    ///     Data(i32),
++    /// }
++    ///
++    /// let wrapper = Wrapper::Data(42);
++    ///
++    /// let data = match wrapper {
++    ///     Wrapper::Data(i) => i,
++    /// };
++    /// ```
++    ///
++    /// The correct use would be:
++    /// ```rust
++    /// enum Wrapper {
++    ///     Data(i32),
++    /// }
++    ///
++    /// let wrapper = Wrapper::Data(42);
++    /// let Wrapper::Data(data) = wrapper;
++    /// ```
++    pub INFALLIBLE_DESTRUCTURING_MATCH,
++    style,
++    "a `match` statement with a single infallible arm instead of a `let`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for useless match that binds to only one value.
++    ///
++    /// **Why is this bad?** Readability and needless complexity.
++    ///
++    /// **Known problems:**  Suggested replacements may be incorrect when `match`
++    /// is actually binding temporary value, bringing a 'dropped while borrowed' error.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let a = 1;
++    /// # let b = 2;
++    ///
++    /// // Bad
++    /// match (a, b) {
++    ///     (c, d) => {
++    ///         // useless match
++    ///     }
++    /// }
++    ///
++    /// // Good
++    /// let (c, d) = (a, b);
++    /// ```
++    pub MATCH_SINGLE_BINDING,
++    complexity,
++    "a match with a single binding instead of using `let` statement"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
++    ///
++    /// **Why is this bad?** Correctness and readability. It's like having a wildcard pattern after
++    /// matching all enum variants explicitly.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # struct A { a: i32 }
++    /// let a = A { a: 5 };
++    ///
++    /// // Bad
++    /// match a {
++    ///     A { a: 5, .. } => {},
++    ///     _ => {},
++    /// }
++    ///
++    /// // Good
++    /// match a {
++    ///     A { a: 5 } => {},
++    ///     _ => {},
++    /// }
++    /// ```
++    pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
++    restriction,
++    "a match on a struct that binds all fields but still uses the wildcard pattern"
++}
++
++#[derive(Default)]
++pub struct Matches {
++    infallible_destructuring_match_linted: bool,
++}
++
++impl_lint_pass!(Matches => [
++    SINGLE_MATCH,
++    MATCH_REF_PATS,
++    MATCH_BOOL,
++    SINGLE_MATCH_ELSE,
++    MATCH_OVERLAPPING_ARM,
++    MATCH_WILD_ERR_ARM,
++    MATCH_AS_REF,
++    WILDCARD_ENUM_MATCH_ARM,
++    WILDCARD_IN_OR_PATTERNS,
++    MATCH_SINGLE_BINDING,
++    INFALLIBLE_DESTRUCTURING_MATCH,
++    REST_PAT_IN_FULLY_BOUND_STRUCTS
++]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if in_external_macro(cx.sess(), expr.span) {
++            return;
++        }
++        if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind {
++            check_single_match(cx, ex, arms, expr);
++            check_match_bool(cx, ex, arms, expr);
++            check_overlapping_arms(cx, ex, arms);
++            check_wild_err_arm(cx, ex, arms);
++            check_wild_enum_match(cx, ex, arms);
++            check_match_as_ref(cx, ex, arms, expr);
++            check_wild_in_or_pats(cx, arms);
++
++            if self.infallible_destructuring_match_linted {
++                self.infallible_destructuring_match_linted = false;
++            } else {
++                check_match_single_binding(cx, ex, arms, expr);
++            }
++        }
++        if let ExprKind::Match(ref ex, ref arms, _) = expr.kind {
++            check_match_ref_pats(cx, ex, arms, expr);
++        }
++    }
++
++    fn check_local(&mut self, cx: &LateContext<'a, 'tcx>, local: &'tcx Local<'_>) {
++        if_chain! {
++            if !in_external_macro(cx.sess(), local.span);
++            if !in_macro(local.span);
++            if let Some(ref expr) = local.init;
++            if let ExprKind::Match(ref target, ref arms, MatchSource::Normal) = expr.kind;
++            if arms.len() == 1 && arms[0].guard.is_none();
++            if let PatKind::TupleStruct(
++                QPath::Resolved(None, ref variant_name), ref args, _) = arms[0].pat.kind;
++            if args.len() == 1;
++            if let Some(arg) = get_arg_name(&args[0]);
++            let body = remove_blocks(&arms[0].body);
++            if match_var(body, arg);
++
++            then {
++                let mut applicability = Applicability::MachineApplicable;
++                self.infallible_destructuring_match_linted = true;
++                span_lint_and_sugg(
++                    cx,
++                    INFALLIBLE_DESTRUCTURING_MATCH,
++                    local.span,
++                    "you seem to be trying to use `match` to destructure a single infallible pattern. \
++                    Consider using `let`",
++                    "try this",
++                    format!(
++                        "let {}({}) = {};",
++                        snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
++                        snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
++                        snippet_with_applicability(cx, target.span, "..", &mut applicability),
++                    ),
++                    applicability,
++                );
++            }
++        }
++    }
++
++    fn check_pat(&mut self, cx: &LateContext<'a, 'tcx>, pat: &'tcx Pat<'_>) {
++        if_chain! {
++            if !in_external_macro(cx.sess(), pat.span);
++            if !in_macro(pat.span);
++            if let PatKind::Struct(ref qpath, fields, true) = pat.kind;
++            if let QPath::Resolved(_, ref path) = qpath;
++            if let Some(def_id) = path.res.opt_def_id();
++            let ty = cx.tcx.type_of(def_id);
++            if let ty::Adt(def, _) = ty.kind;
++            if def.is_struct() || def.is_union();
++            if fields.len() == def.non_enum_variant().fields.len();
++
++            then {
++                span_lint_and_help(
++                    cx,
++                    REST_PAT_IN_FULLY_BOUND_STRUCTS,
++                    pat.span,
++                    "unnecessary use of `..` pattern in struct binding. All fields were already bound",
++                    None,
++                    "consider removing `..` from this binding",
++                );
++            }
++        }
++    }
++}
++
++#[rustfmt::skip]
++fn check_single_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
++    if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
++        if in_macro(expr.span) {
++            // Don't lint match expressions present in
++            // macro_rules! block
++            return;
++        }
++        if let PatKind::Or(..) = arms[0].pat.kind {
++            // don't lint for or patterns for now, this makes
++            // the lint noisy in unnecessary situations
++            return;
++        }
++        let els = remove_blocks(&arms[1].body);
++        let els = if is_unit_expr(els) {
++            None
++        } else if let ExprKind::Block(_, _) = els.kind {
++            // matches with blocks that contain statements are prettier as `if let + else`
++            Some(els)
++        } else {
++            // allow match arms with just expressions
++            return;
++        };
++        let ty = cx.tables.expr_ty(ex);
++        if ty.kind != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) {
++            check_single_match_single_pattern(cx, ex, arms, expr, els);
++            check_single_match_opt_like(cx, ex, arms, expr, ty, els);
++        }
++    }
++}
++
++fn check_single_match_single_pattern(
++    cx: &LateContext<'_, '_>,
++    ex: &Expr<'_>,
++    arms: &[Arm<'_>],
++    expr: &Expr<'_>,
++    els: Option<&Expr<'_>>,
++) {
++    if is_wild(&arms[1].pat) {
++        report_single_match_single_pattern(cx, ex, arms, expr, els);
++    }
++}
++
++fn report_single_match_single_pattern(
++    cx: &LateContext<'_, '_>,
++    ex: &Expr<'_>,
++    arms: &[Arm<'_>],
++    expr: &Expr<'_>,
++    els: Option<&Expr<'_>>,
++) {
++    let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
++    let els_str = els.map_or(String::new(), |els| {
++        format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
++    });
++    span_lint_and_sugg(
++        cx,
++        lint,
++        expr.span,
++        "you seem to be trying to use match for destructuring a single pattern. Consider using `if \
++         let`",
++        "try this",
++        format!(
++            "if let {} = {} {}{}",
++            snippet(cx, arms[0].pat.span, ".."),
++            snippet(cx, ex.span, ".."),
++            expr_block(cx, &arms[0].body, None, "..", Some(expr.span)),
++            els_str,
++        ),
++        Applicability::HasPlaceholders,
++    );
++}
++
++fn check_single_match_opt_like(
++    cx: &LateContext<'_, '_>,
++    ex: &Expr<'_>,
++    arms: &[Arm<'_>],
++    expr: &Expr<'_>,
++    ty: Ty<'_>,
++    els: Option<&Expr<'_>>,
++) {
++    // list of candidate `Enum`s we know will never get any more members
++    let candidates = &[
++        (&paths::COW, "Borrowed"),
++        (&paths::COW, "Cow::Borrowed"),
++        (&paths::COW, "Cow::Owned"),
++        (&paths::COW, "Owned"),
++        (&paths::OPTION, "None"),
++        (&paths::RESULT, "Err"),
++        (&paths::RESULT, "Ok"),
++    ];
++
++    let path = match arms[1].pat.kind {
++        PatKind::TupleStruct(ref path, ref inner, _) => {
++            // Contains any non wildcard patterns (e.g., `Err(err)`)?
++            if !inner.iter().all(is_wild) {
++                return;
++            }
++            rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
++        },
++        PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None) => ident.to_string(),
++        PatKind::Path(ref path) => {
++            rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
++        },
++        _ => return,
++    };
++
++    for &(ty_path, pat_path) in candidates {
++        if path == *pat_path && match_type(cx, ty, ty_path) {
++            report_single_match_single_pattern(cx, ex, arms, expr, els);
++        }
++    }
++}
++
++fn check_match_bool(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
++    // Type of expression is `bool`.
++    if cx.tables.expr_ty(ex).kind == ty::Bool {
++        span_lint_and_then(
++            cx,
++            MATCH_BOOL,
++            expr.span,
++            "you seem to be trying to match on a boolean expression",
++            move |diag| {
++                if arms.len() == 2 {
++                    // no guards
++                    let exprs = if let PatKind::Lit(ref arm_bool) = arms[0].pat.kind {
++                        if let ExprKind::Lit(ref lit) = arm_bool.kind {
++                            match lit.node {
++                                LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
++                                LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
++                                _ => None,
++                            }
++                        } else {
++                            None
++                        }
++                    } else {
++                        None
++                    };
++
++                    if let Some((true_expr, false_expr)) = exprs {
++                        let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
++                            (false, false) => Some(format!(
++                                "if {} {} else {}",
++                                snippet(cx, ex.span, "b"),
++                                expr_block(cx, true_expr, None, "..", Some(expr.span)),
++                                expr_block(cx, false_expr, None, "..", Some(expr.span))
++                            )),
++                            (false, true) => Some(format!(
++                                "if {} {}",
++                                snippet(cx, ex.span, "b"),
++                                expr_block(cx, true_expr, None, "..", Some(expr.span))
++                            )),
++                            (true, false) => {
++                                let test = Sugg::hir(cx, ex, "..");
++                                Some(format!(
++                                    "if {} {}",
++                                    !test,
++                                    expr_block(cx, false_expr, None, "..", Some(expr.span))
++                                ))
++                            },
++                            (true, true) => None,
++                        };
++
++                        if let Some(sugg) = sugg {
++                            diag.span_suggestion(
++                                expr.span,
++                                "consider using an `if`/`else` expression",
++                                sugg,
++                                Applicability::HasPlaceholders,
++                            );
++                        }
++                    }
++                }
++            },
++        );
++    }
++}
++
++fn check_overlapping_arms<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
++    if arms.len() >= 2 && cx.tables.expr_ty(ex).is_integral() {
++        let ranges = all_ranges(cx, arms, cx.tables.expr_ty(ex));
++        let type_ranges = type_ranges(&ranges);
++        if !type_ranges.is_empty() {
++            if let Some((start, end)) = overlapping(&type_ranges) {
++                span_lint_and_note(
++                    cx,
++                    MATCH_OVERLAPPING_ARM,
++                    start.span,
++                    "some ranges overlap",
++                    Some(end.span),
++                    "overlaps with this",
++                );
++            }
++        }
++    }
++}
++
++fn check_wild_err_arm(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
++    let ex_ty = walk_ptrs_ty(cx.tables.expr_ty(ex));
++    if is_type_diagnostic_item(cx, ex_ty, sym!(result_type)) {
++        for arm in arms {
++            if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind {
++                let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
++                if path_str == "Err" {
++                    let mut matching_wild = inner.iter().any(is_wild);
++                    let mut ident_bind_name = String::from("_");
++                    if !matching_wild {
++                        // Looking for unused bindings (i.e.: `_e`)
++                        inner.iter().for_each(|pat| {
++                            if let PatKind::Binding(.., ident, None) = &pat.kind {
++                                if ident.as_str().starts_with('_') && is_unused(ident, arm.body) {
++                                    ident_bind_name = (&ident.name.as_str()).to_string();
++                                    matching_wild = true;
++                                }
++                            }
++                        });
++                    }
++                    if_chain! {
++                        if matching_wild;
++                        if let ExprKind::Block(ref block, _) = arm.body.kind;
++                        if is_panic_block(block);
++                        then {
++                            // `Err(_)` or `Err(_e)` arm with `panic!` found
++                            span_lint_and_note(cx,
++                                MATCH_WILD_ERR_ARM,
++                                arm.pat.span,
++                                &format!("`Err({})` matches all errors", &ident_bind_name),
++                                None,
++                                "match each error separately or use the error output",
++                            );
++                        }
++                    }
++                }
++            }
++        }
++    }
++}
++
++fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
++    let ty = cx.tables.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;
++    }
++
++    // 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;
++    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);
++        }
++    }
++
++    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.
++
++        let mut missing_variants = vec![];
++        if let ty::Adt(def, _) = ty.kind {
++            for variant in &def.variants {
++                missing_variants.push(variant);
++            }
++        }
++
++        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(ref path, ..) = arm.pat.kind {
++                if let QPath::Resolved(_, p) = path {
++                    missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
++                }
++            }
++        }
++
++        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("_"));
++            }
++        }
++
++        span_lint_and_sugg(
++            cx,
++            WILDCARD_ENUM_MATCH_ARM,
++            wildcard_span,
++            message,
++            "try this",
++            suggestion.join(" | "),
++            Applicability::MachineApplicable,
++        )
++    }
++}
++
++// If the block contains only a `panic!` macro (as expression or statement)
++fn is_panic_block(block: &Block<'_>) -> bool {
++    match (&block.expr, block.stmts.len(), block.stmts.first()) {
++        (&Some(ref exp), 0, _) => {
++            is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none()
++        },
++        (&None, 1, Some(stmt)) => {
++            is_expn_of(stmt.span, "panic").is_some() && is_expn_of(stmt.span, "unreachable").is_none()
++        },
++        _ => false,
++    }
++}
++
++fn check_match_ref_pats(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
++    if has_only_ref_pats(arms) {
++        let mut suggs = Vec::with_capacity(arms.len() + 1);
++        let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = ex.kind {
++            let span = ex.span.source_callsite();
++            suggs.push((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
++            (
++                "you don't need to add `&` to both the expression and the patterns",
++                "try",
++            )
++        } else {
++            let span = ex.span.source_callsite();
++            suggs.push((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
++            (
++                "you don't need to add `&` to all patterns",
++                "instead of prefixing all patterns with `&`, you can dereference the expression",
++            )
++        };
++
++        suggs.extend(arms.iter().filter_map(|a| {
++            if let PatKind::Ref(ref refp, _) = a.pat.kind {
++                Some((a.pat.span, snippet(cx, refp.span, "..").to_string()))
++            } else {
++                None
++            }
++        }));
++
++        span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| {
++            if !expr.span.from_expansion() {
++                multispan_sugg(diag, msg.to_owned(), suggs);
++            }
++        });
++    }
++}
++
++fn check_match_as_ref(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
++    if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
++        let arm_ref: Option<BindingAnnotation> = if is_none_arm(&arms[0]) {
++            is_ref_some_arm(&arms[1])
++        } else if is_none_arm(&arms[1]) {
++            is_ref_some_arm(&arms[0])
++        } else {
++            None
++        };
++        if let Some(rb) = arm_ref {
++            let suggestion = if rb == BindingAnnotation::Ref {
++                "as_ref"
++            } else {
++                "as_mut"
++            };
++
++            let output_ty = cx.tables.expr_ty(expr);
++            let input_ty = cx.tables.expr_ty(ex);
++
++            let cast = if_chain! {
++                if let ty::Adt(_, substs) = input_ty.kind;
++                let input_ty = substs.type_at(0);
++                if let ty::Adt(_, substs) = output_ty.kind;
++                let output_ty = substs.type_at(0);
++                if let ty::Ref(_, output_ty, _) = output_ty.kind;
++                if input_ty != output_ty;
++                then {
++                    ".map(|x| x as _)"
++                } else {
++                    ""
++                }
++            };
++
++            let mut applicability = Applicability::MachineApplicable;
++            span_lint_and_sugg(
++                cx,
++                MATCH_AS_REF,
++                expr.span,
++                &format!("use `{}()` instead", suggestion),
++                "try this",
++                format!(
++                    "{}.{}(){}",
++                    snippet_with_applicability(cx, ex.span, "_", &mut applicability),
++                    suggestion,
++                    cast,
++                ),
++                applicability,
++            )
++        }
++    }
++}
++
++fn check_wild_in_or_pats(cx: &LateContext<'_, '_>, arms: &[Arm<'_>]) {
++    for arm in arms {
++        if let PatKind::Or(ref fields) = arm.pat.kind {
++            // look for multiple fields in this arm that contains at least one Wild pattern
++            if fields.len() > 1 && fields.iter().any(is_wild) {
++                span_lint_and_help(
++                    cx,
++                    WILDCARD_IN_OR_PATTERNS,
++                    arm.pat.span,
++                    "wildcard pattern covers any other pattern as it will match anyway.",
++                    None,
++                    "Consider handling `_` separately.",
++                );
++            }
++        }
++    }
++}
++
++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;
++    }
++    let matched_vars = ex.span;
++    let bind_names = arms[0].pat.span;
++    let match_body = remove_blocks(&arms[0].body);
++    let mut snippet_body = if match_body.span.from_expansion() {
++        Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string()
++    } else {
++        snippet_block(cx, match_body.span, "..", Some(expr.span)).to_string()
++    };
++
++    // Do we need to add ';' to suggestion ?
++    match match_body.kind {
++        ExprKind::Block(block, _) => {
++            // macro + expr_ty(body) == ()
++            if block.span.from_expansion() && cx.tables.expr_ty(&match_body).is_unit() {
++                snippet_body.push(';');
++            }
++        },
++        _ => {
++            // expr_ty(body) == ()
++            if cx.tables.expr_ty(&match_body).is_unit() {
++                snippet_body.push(';');
++            }
++        },
++    }
++
++    let mut applicability = Applicability::MaybeIncorrect;
++    match arms[0].pat.kind {
++        PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => {
++            // If this match is in a local (`let`) stmt
++            let (target_span, sugg) = if let Some(parent_let_node) = opt_parent_let(cx, ex) {
++                (
++                    parent_let_node.span,
++                    format!(
++                        "let {} = {};\n{}let {} = {};",
++                        snippet_with_applicability(cx, bind_names, "..", &mut applicability),
++                        snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
++                        " ".repeat(indent_of(cx, expr.span).unwrap_or(0)),
++                        snippet_with_applicability(cx, parent_let_node.pat.span, "..", &mut applicability),
++                        snippet_body
++                    ),
++                )
++            } else {
++                // If we are in closure, we need curly braces around suggestion
++                let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0));
++                let (mut cbrace_start, mut cbrace_end) = ("".to_string(), "".to_string());
++                if let Some(parent_expr) = get_parent_expr(cx, expr) {
++                    if let ExprKind::Closure(..) = parent_expr.kind {
++                        cbrace_end = format!("\n{}}}", indent);
++                        // Fix body indent due to the closure
++                        indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
++                        cbrace_start = format!("{{\n{}", indent);
++                    }
++                };
++                (
++                    expr.span,
++                    format!(
++                        "{}let {} = {};\n{}{}{}",
++                        cbrace_start,
++                        snippet_with_applicability(cx, bind_names, "..", &mut applicability),
++                        snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
++                        indent,
++                        snippet_body,
++                        cbrace_end
++                    ),
++                )
++            };
++            span_lint_and_sugg(
++                cx,
++                MATCH_SINGLE_BINDING,
++                target_span,
++                "this match could be written as a `let` statement",
++                "consider using `let` statement",
++                sugg,
++                applicability,
++            );
++        },
++        PatKind::Wild => {
++            span_lint_and_sugg(
++                cx,
++                MATCH_SINGLE_BINDING,
++                expr.span,
++                "this match could be replaced by its body itself",
++                "consider using the match body instead",
++                snippet_body,
++                Applicability::MachineApplicable,
++            );
++        },
++        _ => (),
++    }
++}
++
++/// Returns true if the `ex` match expression is in a local (`let`) statement
++fn opt_parent_let<'a>(cx: &LateContext<'_, 'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> {
++    if_chain! {
++        let map = &cx.tcx.hir();
++        if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id));
++        if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id));
++        then {
++            return Some(parent_let_expr);
++        }
++    }
++    None
++}
++
++/// Gets all arms that are unbounded `PatRange`s.
++fn all_ranges<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    arms: &'tcx [Arm<'_>],
++    ty: Ty<'tcx>,
++) -> Vec<SpannedRange<Constant>> {
++    arms.iter()
++        .flat_map(|arm| {
++            if let Arm {
++                ref pat, guard: None, ..
++            } = *arm
++            {
++                if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
++                    let lhs = match lhs {
++                        Some(lhs) => constant(cx, cx.tables, lhs)?.0,
++                        None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
++                    };
++                    let rhs = match rhs {
++                        Some(rhs) => constant(cx, cx.tables, rhs)?.0,
++                        None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
++                    };
++                    let rhs = match range_end {
++                        RangeEnd::Included => Bound::Included(rhs),
++                        RangeEnd::Excluded => Bound::Excluded(rhs),
++                    };
++                    return Some(SpannedRange {
++                        span: pat.span,
++                        node: (lhs, rhs),
++                    });
++                }
++
++                if let PatKind::Lit(ref value) = pat.kind {
++                    let value = constant(cx, cx.tables, value)?.0;
++                    return Some(SpannedRange {
++                        span: pat.span,
++                        node: (value.clone(), Bound::Included(value)),
++                    });
++                }
++            }
++            None
++        })
++        .collect()
++}
++
++#[derive(Debug, Eq, PartialEq)]
++pub struct SpannedRange<T> {
++    pub span: Span,
++    pub node: (T, Bound<T>),
++}
++
++type TypedRanges = Vec<SpannedRange<u128>>;
++
++/// Gets all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
++/// and other types than
++/// `Uint` and `Int` probably don't make sense.
++fn type_ranges(ranges: &[SpannedRange<Constant>]) -> TypedRanges {
++    ranges
++        .iter()
++        .filter_map(|range| match range.node {
++            (Constant::Int(start), Bound::Included(Constant::Int(end))) => Some(SpannedRange {
++                span: range.span,
++                node: (start, Bound::Included(end)),
++            }),
++            (Constant::Int(start), Bound::Excluded(Constant::Int(end))) => Some(SpannedRange {
++                span: range.span,
++                node: (start, Bound::Excluded(end)),
++            }),
++            (Constant::Int(start), Bound::Unbounded) => Some(SpannedRange {
++                span: range.span,
++                node: (start, Bound::Unbounded),
++            }),
++            _ => None,
++        })
++        .collect()
++}
++
++fn is_unit_expr(expr: &Expr<'_>) -> bool {
++    match expr.kind {
++        ExprKind::Tup(ref v) if v.is_empty() => true,
++        ExprKind::Block(ref b, _) if b.stmts.is_empty() && b.expr.is_none() => true,
++        _ => false,
++    }
++}
++
++// Checks if arm has the form `None => None`
++fn is_none_arm(arm: &Arm<'_>) -> bool {
++    match arm.pat.kind {
++        PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => true,
++        _ => false,
++    }
++}
++
++// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
++fn is_ref_some_arm(arm: &Arm<'_>) -> Option<BindingAnnotation> {
++    if_chain! {
++        if let PatKind::TupleStruct(ref path, ref pats, _) = arm.pat.kind;
++        if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME);
++        if let PatKind::Binding(rb, .., ident, _) = pats[0].kind;
++        if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
++        if let ExprKind::Call(ref e, ref args) = remove_blocks(&arm.body).kind;
++        if let ExprKind::Path(ref some_path) = e.kind;
++        if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1;
++        if let ExprKind::Path(ref qpath) = args[0].kind;
++        if let &QPath::Resolved(_, ref path2) = qpath;
++        if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
++        then {
++            return Some(rb)
++        }
++    }
++    None
++}
++
++fn has_only_ref_pats(arms: &[Arm<'_>]) -> bool {
++    let mapped = arms
++        .iter()
++        .map(|a| {
++            match a.pat.kind {
++                PatKind::Ref(..) => Some(true), // &-patterns
++                PatKind::Wild => Some(false),   // an "anything" wildcard is also fine
++                _ => None,                      // any other pattern is not fine
++            }
++        })
++        .collect::<Option<Vec<bool>>>();
++    // look for Some(v) where there's at least one true element
++    mapped.map_or(false, |v| v.iter().any(|el| *el))
++}
++
++pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
++where
++    T: Copy + Ord,
++{
++    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
++    enum Kind<'a, T> {
++        Start(T, &'a SpannedRange<T>),
++        End(Bound<T>, &'a SpannedRange<T>),
++    }
++
++    impl<'a, T: Copy> Kind<'a, T> {
++        fn range(&self) -> &'a SpannedRange<T> {
++            match *self {
++                Kind::Start(_, r) | Kind::End(_, r) => r,
++            }
++        }
++
++        fn value(self) -> Bound<T> {
++            match self {
++                Kind::Start(t, _) => Bound::Included(t),
++                Kind::End(t, _) => t,
++            }
++        }
++    }
++
++    impl<'a, T: Copy + Ord> PartialOrd for Kind<'a, T> {
++        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
++            Some(self.cmp(other))
++        }
++    }
++
++    impl<'a, T: Copy + Ord> Ord for Kind<'a, T> {
++        fn cmp(&self, other: &Self) -> Ordering {
++            match (self.value(), other.value()) {
++                (Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
++                // Range patterns cannot be unbounded (yet)
++                (Bound::Unbounded, _) | (_, Bound::Unbounded) => unimplemented!(),
++                (Bound::Included(a), Bound::Excluded(b)) => match a.cmp(&b) {
++                    Ordering::Equal => Ordering::Greater,
++                    other => other,
++                },
++                (Bound::Excluded(a), Bound::Included(b)) => match a.cmp(&b) {
++                    Ordering::Equal => Ordering::Less,
++                    other => other,
++                },
++            }
++        }
++    }
++
++    let mut values = Vec::with_capacity(2 * ranges.len());
++
++    for r in ranges {
++        values.push(Kind::Start(r.node.0, r));
++        values.push(Kind::End(r.node.1, r));
++    }
++
++    values.sort();
++
++    for (a, b) in values.iter().zip(values.iter().skip(1)) {
++        match (a, b) {
++            (&Kind::Start(_, ra), &Kind::End(_, rb)) => {
++                if ra.node != rb.node {
++                    return Some((ra, rb));
++                }
++            },
++            (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
++            _ => return Some((a.range(), b.range())),
++        }
++    }
++
++    None
++}
++
++#[test]
++fn test_overlapping() {
++    use rustc_span::source_map::DUMMY_SP;
++
++    let sp = |s, e| SpannedRange {
++        span: DUMMY_SP,
++        node: (s, e),
++    };
++
++    assert_eq!(None, overlapping::<u8>(&[]));
++    assert_eq!(None, overlapping(&[sp(1, Bound::Included(4))]));
++    assert_eq!(
++        None,
++        overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6))])
++    );
++    assert_eq!(
++        None,
++        overlapping(&[
++            sp(1, Bound::Included(4)),
++            sp(5, Bound::Included(6)),
++            sp(10, Bound::Included(11))
++        ],)
++    );
++    assert_eq!(
++        Some((&sp(1, Bound::Included(4)), &sp(3, Bound::Included(6)))),
++        overlapping(&[sp(1, Bound::Included(4)), sp(3, Bound::Included(6))])
++    );
++    assert_eq!(
++        Some((&sp(5, Bound::Included(6)), &sp(6, Bound::Included(11)))),
++        overlapping(&[
++            sp(1, Bound::Included(4)),
++            sp(5, Bound::Included(6)),
++            sp(6, Bound::Included(11))
++        ],)
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3f953655670cfcee0cb5cf6bffbc4b8e4afc28de
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,81 @@@
++use crate::utils::{match_def_path, paths, snippet, span_lint_and_then, walk_ptrs_ty_depth};
++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! {
++    /// **What it does:** Checks for calls of `mem::discriminant()` on a non-enum type.
++    ///
++    /// **Why is this bad?** The value of `mem::discriminant()` on non-enum types
++    /// is unspecified.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// use std::mem;
++    ///
++    /// mem::discriminant(&"hello");
++    /// mem::discriminant(&&Some(2));
++    /// ```
++    pub MEM_DISCRIMINANT_NON_ENUM,
++    correctness,
++    "calling `mem::descriminant` on non-enum type"
++}
++
++declare_lint_pass!(MemDiscriminant => [MEM_DISCRIMINANT_NON_ENUM]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemDiscriminant {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            if let ExprKind::Call(ref func, ref func_args) = expr.kind;
++            // is `mem::discriminant`
++            if let ExprKind::Path(ref func_qpath) = func.kind;
++            if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id();
++            if match_def_path(cx, def_id, &paths::MEM_DISCRIMINANT);
++            // type is non-enum
++            let ty_param = cx.tables.node_substs(func.hir_id).type_at(0);
++            if !ty_param.is_enum();
++
++            then {
++                span_lint_and_then(
++                    cx,
++                    MEM_DISCRIMINANT_NON_ENUM,
++                    expr.span,
++                    &format!("calling `mem::discriminant` on non-enum type `{}`", ty_param),
++                    |diag| {
++                        // if this is a reference to an enum, suggest dereferencing
++                        let (base_ty, ptr_depth) = walk_ptrs_ty_depth(ty_param);
++                        if ptr_depth >= 1 && base_ty.is_enum() {
++                            let param = &func_args[0];
++
++                            // cancel out '&'s first
++                            let mut derefs_needed = ptr_depth;
++                            let mut cur_expr = param;
++                            while derefs_needed > 0  {
++                                if let ExprKind::AddrOf(BorrowKind::Ref, _, ref inner_expr) = cur_expr.kind {
++                                    derefs_needed -= 1;
++                                    cur_expr = inner_expr;
++                                } else {
++                                    break;
++                                }
++                            }
++
++                            let derefs: String = iter::repeat('*').take(derefs_needed).collect();
++                            diag.span_suggestion(
++                                param.span,
++                                "try dereferencing",
++                                format!("{}{}", derefs, snippet(cx, cur_expr.span, "<param>")),
++                                Applicability::MachineApplicable,
++                            );
++                        }
++                    },
++                )
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c6ddc5de63b0ed3a962777c28a497dcbc25b1a4b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++use crate::utils::{match_def_path, paths, qpath_res, span_lint};
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `std::mem::forget(t)` where `t` is
++    /// `Drop`.
++    ///
++    /// **Why is this bad?** `std::mem::forget(t)` prevents `t` from running its
++    /// destructor, possibly causing leaks.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # use std::mem;
++    /// # use std::rc::Rc;
++    /// mem::forget(Rc::new(55))
++    /// ```
++    pub MEM_FORGET,
++    restriction,
++    "`mem::forget` usage on `Drop` types, likely to cause memory leaks"
++}
++
++declare_lint_pass!(MemForget => [MEM_FORGET]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemForget {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        if let ExprKind::Call(ref path_expr, ref args) = e.kind {
++            if let ExprKind::Path(ref qpath) = path_expr.kind {
++                if let Some(def_id) = qpath_res(cx, qpath, path_expr.hir_id).opt_def_id() {
++                    if match_def_path(cx, def_id, &paths::MEM_FORGET) {
++                        let forgot_ty = cx.tables.expr_ty(&args[0]);
++
++                        if forgot_ty.ty_adt_def().map_or(false, |def| def.has_dtor(cx.tcx)) {
++                            span_lint(cx, MEM_FORGET, e.span, "usage of `mem::forget` on `Drop` type");
++                        }
++                    }
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ab6865bf0f3b7675edd08a150d2c0dd0c4eee121
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,217 @@@
++use crate::utils::{
++    in_macro, match_def_path, match_qpath, paths, snippet, snippet_with_applicability, span_lint_and_help,
++    span_lint_and_sugg, span_lint_and_then,
++};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++use rustc_span::symbol::sym;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `mem::replace()` on an `Option` with
++    /// `None`.
++    ///
++    /// **Why is this bad?** `Option` already has the method `take()` for
++    /// taking its current value (Some(..) or None) and replacing it with
++    /// `None`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// use std::mem;
++    ///
++    /// let mut an_option = Some(0);
++    /// let replaced = mem::replace(&mut an_option, None);
++    /// ```
++    /// Is better expressed with:
++    /// ```rust
++    /// let mut an_option = Some(0);
++    /// let taken = an_option.take();
++    /// ```
++    pub MEM_REPLACE_OPTION_WITH_NONE,
++    style,
++    "replacing an `Option` with `None` instead of `take()`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `mem::replace(&mut _, mem::uninitialized())`
++    /// and `mem::replace(&mut _, mem::zeroed())`.
++    ///
++    /// **Why is this bad?** This will lead to undefined behavior even if the
++    /// value is overwritten later, because the uninitialized value may be
++    /// observed in the case of a panic.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```
++    /// use std::mem;
++    ///# fn may_panic(v: Vec<i32>) -> Vec<i32> { v }
++    ///
++    /// #[allow(deprecated, invalid_value)]
++    /// fn myfunc (v: &mut Vec<i32>) {
++    ///     let taken_v = unsafe { mem::replace(v, mem::uninitialized()) };
++    ///     let new_v = may_panic(taken_v); // undefined behavior on panic
++    ///     mem::forget(mem::replace(v, new_v));
++    /// }
++    /// ```
++    ///
++    /// The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,
++    /// at the cost of either lazily creating a replacement value or aborting
++    /// on panic, to ensure that the uninitialized value cannot be observed.
++    pub MEM_REPLACE_WITH_UNINIT,
++    correctness,
++    "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `std::mem::replace` on a value of type
++    /// `T` with `T::default()`.
++    ///
++    /// **Why is this bad?** `std::mem` module already has the method `take` to
++    /// take the current value and replace it with the default value of that type.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let mut text = String::from("foo");
++    /// let replaced = std::mem::replace(&mut text, String::default());
++    /// ```
++    /// Is better expressed with:
++    /// ```rust
++    /// let mut text = String::from("foo");
++    /// let taken = std::mem::take(&mut text);
++    /// ```
++    pub MEM_REPLACE_WITH_DEFAULT,
++    style,
++    "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
++}
++
++declare_lint_pass!(MemReplace =>
++    [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
++
++fn check_replace_option_with_none(cx: &LateContext<'_, '_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
++    if let ExprKind::Path(ref replacement_qpath) = src.kind {
++        // Check that second argument is `Option::None`
++        if match_qpath(replacement_qpath, &paths::OPTION_NONE) {
++            // Since this is a late pass (already type-checked),
++            // and we already know that the second argument is an
++            // `Option`, we do not need to check the first
++            // argument's type. All that's left is to get
++            // replacee's path.
++            let replaced_path = match dest.kind {
++                ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref replaced) => {
++                    if let ExprKind::Path(QPath::Resolved(None, ref replaced_path)) = replaced.kind {
++                        replaced_path
++                    } else {
++                        return;
++                    }
++                },
++                ExprKind::Path(QPath::Resolved(None, ref replaced_path)) => replaced_path,
++                _ => return,
++            };
++
++            let mut applicability = Applicability::MachineApplicable;
++            span_lint_and_sugg(
++                cx,
++                MEM_REPLACE_OPTION_WITH_NONE,
++                expr_span,
++                "replacing an `Option` with `None`",
++                "consider `Option::take()` instead",
++                format!(
++                    "{}.take()",
++                    snippet_with_applicability(cx, replaced_path.span, "", &mut applicability)
++                ),
++                applicability,
++            );
++        }
++    }
++}
++
++fn check_replace_with_uninit(cx: &LateContext<'_, '_>, src: &Expr<'_>, expr_span: Span) {
++    if let ExprKind::Call(ref repl_func, ref repl_args) = src.kind {
++        if_chain! {
++            if repl_args.is_empty();
++            if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
++            if let Some(repl_def_id) = cx.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
++            then {
++                if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) {
++                    span_lint_and_help(
++                        cx,
++                        MEM_REPLACE_WITH_UNINIT,
++                        expr_span,
++                        "replacing with `mem::uninitialized()`",
++                        None,
++                        "consider using the `take_mut` crate instead",
++                    );
++                } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) &&
++                        !cx.tables.expr_ty(src).is_primitive() {
++                    span_lint_and_help(
++                        cx,
++                        MEM_REPLACE_WITH_UNINIT,
++                        expr_span,
++                        "replacing with `mem::zeroed()`",
++                        None,
++                        "consider using a default value or the `take_mut` crate instead",
++                    );
++                }
++            }
++        }
++    }
++}
++
++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.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
++            if match_def_path(cx, repl_def_id, &paths::DEFAULT_TRAIT_METHOD);
++            then {
++                span_lint_and_then(
++                    cx,
++                    MEM_REPLACE_WITH_DEFAULT,
++                    expr_span,
++                    "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
++                    |diag| {
++                        if !in_macro(expr_span) {
++                            let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, ""));
++
++                            diag.span_suggestion(
++                                expr_span,
++                                "consider using",
++                                suggestion,
++                                Applicability::MachineApplicable
++                            );
++                        }
++                    }
++                );
++            }
++        }
++    }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            // Check that `expr` is a call to `mem::replace()`
++            if let ExprKind::Call(ref func, ref func_args) = expr.kind;
++            if let ExprKind::Path(ref func_qpath) = func.kind;
++            if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id();
++            if match_def_path(cx, def_id, &paths::MEM_REPLACE);
++            if let [dest, src] = &**func_args;
++            then {
++                check_replace_option_with_none(cx, src, dest, expr.span);
++                check_replace_with_uninit(cx, src, expr.span);
++                check_replace_with_default(cx, src, dest, expr.span);
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..06138ab9783c34f19d8b1340a05d846c20237a3f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++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 if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::LateContext;
++use rustc_middle::ty::{self, Ty};
++
++/// Checks for the `INEFFICIENT_TO_STRING` lint
++pub fn lint<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'tcx>) {
++    if_chain! {
++        if let Some(to_string_meth_did) = cx.tables.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.tables.node_substs_opt(expr.hir_id);
++        let self_ty = substs.type_at(0);
++        let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty);
++        if deref_count >= 1;
++        if specializes_tostring(cx, deref_self_ty);
++        then {
++            span_lint_and_then(
++                cx,
++                INEFFICIENT_TO_STRING,
++                expr.span,
++                &format!("calling `to_string` on `{}`", arg_ty),
++                |diag| {
++                    diag.help(&format!(
++                        "`{}` implements `ToString` through a slower blanket impl, but `{}` has a fast specialization of `ToString`",
++                        self_ty, deref_self_ty
++                    ));
++                    let mut applicability = Applicability::MachineApplicable;
++                    let arg_snippet = snippet_with_applicability(cx, arg.span, "..", &mut applicability);
++                    diag.span_suggestion(
++                        expr.span,
++                        "try dereferencing the receiver",
++                        format!("({}{}).to_string()", "*".repeat(deref_count), arg_snippet),
++                        applicability,
++                    );
++                },
++            );
++        }
++    }
++}
++
++/// Returns whether `ty` specializes `ToString`.
++/// Currently, these are `str`, `String`, and `Cow<'_, str>`.
++fn specializes_tostring(cx: &LateContext<'_, '_>, ty: Ty<'_>) -> bool {
++    if let ty::Str = ty.kind {
++        return true;
++    }
++
++    if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
++        return true;
++    }
++
++    if let ty::Adt(adt, substs) = ty.kind {
++        match_def_path(cx, adt.did, &paths::COW) && substs.type_at(1).is_str()
++    } else {
++        false
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aaed6d75048c6299dae5c8cf8138f7bd19cbc311
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,176 @@@
++use crate::utils::{match_qpath, snippet_with_applicability, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_ast::ast;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::LateContext;
++use rustc_target::abi::LayoutOf;
++
++pub fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[&[hir::Expr<'_>]], arith: &str) {
++    let unwrap_arg = &args[0][1];
++    let arith_lhs = &args[1][0];
++    let arith_rhs = &args[1][1];
++
++    let ty = cx.tables.expr_ty(arith_lhs);
++    if !ty.is_integral() {
++        return;
++    }
++
++    let mm = if let Some(mm) = is_min_or_max(cx, unwrap_arg) {
++        mm
++    } else {
++        return;
++    };
++
++    if ty.is_signed() {
++        use self::{
++            MinMax::{Max, Min},
++            Sign::{Neg, Pos},
++        };
++
++        let sign = if let Some(sign) = lit_sign(arith_rhs) {
++            sign
++        } else {
++            return;
++        };
++
++        match (arith, sign, mm) {
++            ("add", Pos, Max) | ("add", Neg, Min) | ("sub", Neg, Max) | ("sub", Pos, Min) => (),
++            // "mul" is omitted because lhs can be negative.
++            _ => return,
++        }
++
++        let mut applicability = Applicability::MachineApplicable;
++        span_lint_and_sugg(
++            cx,
++            super::MANUAL_SATURATING_ARITHMETIC,
++            expr.span,
++            "manual saturating arithmetic",
++            &format!("try using `saturating_{}`", arith),
++            format!(
++                "{}.saturating_{}({})",
++                snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
++                arith,
++                snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
++            ),
++            applicability,
++        );
++    } else {
++        match (mm, arith) {
++            (MinMax::Max, "add") | (MinMax::Max, "mul") | (MinMax::Min, "sub") => (),
++            _ => return,
++        }
++
++        let mut applicability = Applicability::MachineApplicable;
++        span_lint_and_sugg(
++            cx,
++            super::MANUAL_SATURATING_ARITHMETIC,
++            expr.span,
++            "manual saturating arithmetic",
++            &format!("try using `saturating_{}`", arith),
++            format!(
++                "{}.saturating_{}({})",
++                snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
++                arith,
++                snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
++            ),
++            applicability,
++        );
++    }
++}
++
++#[derive(PartialEq, Eq)]
++enum MinMax {
++    Min,
++    Max,
++}
++
++fn is_min_or_max<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &hir::Expr<'_>) -> Option<MinMax> {
++    // `T::max_value()` `T::min_value()` inherent methods
++    if_chain! {
++        if let hir::ExprKind::Call(func, args) = &expr.kind;
++        if args.is_empty();
++        if let hir::ExprKind::Path(path) = &func.kind;
++        if let hir::QPath::TypeRelative(_, segment) = path;
++        then {
++            match &*segment.ident.as_str() {
++                "max_value" => return Some(MinMax::Max),
++                "min_value" => return Some(MinMax::Min),
++                _ => {}
++            }
++        }
++    }
++
++    let ty = cx.tables.expr_ty(expr);
++    let ty_str = ty.to_string();
++
++    // `std::T::MAX` `std::T::MIN` constants
++    if let hir::ExprKind::Path(path) = &expr.kind {
++        if match_qpath(path, &["core", &ty_str, "MAX"][..]) {
++            return Some(MinMax::Max);
++        }
++
++        if match_qpath(path, &["core", &ty_str, "MIN"][..]) {
++            return Some(MinMax::Min);
++        }
++    }
++
++    // Literals
++    let bits = cx.layout_of(ty).unwrap().size.bits();
++    let (minval, maxval): (u128, u128) = if ty.is_signed() {
++        let minval = 1 << (bits - 1);
++        let mut maxval = !(1 << (bits - 1));
++        if bits != 128 {
++            maxval &= (1 << bits) - 1;
++        }
++        (minval, maxval)
++    } else {
++        (0, if bits == 128 { !0 } else { (1 << bits) - 1 })
++    };
++
++    let check_lit = |expr: &hir::Expr<'_>, check_min: bool| {
++        if let hir::ExprKind::Lit(lit) = &expr.kind {
++            if let ast::LitKind::Int(value, _) = lit.node {
++                if value == maxval {
++                    return Some(MinMax::Max);
++                }
++
++                if check_min && value == minval {
++                    return Some(MinMax::Min);
++                }
++            }
++        }
++
++        None
++    };
++
++    if let r @ Some(_) = check_lit(expr, !ty.is_signed()) {
++        return r;
++    }
++
++    if ty.is_signed() {
++        if let hir::ExprKind::Unary(hir::UnOp::UnNeg, val) = &expr.kind {
++            return check_lit(val, true);
++        }
++    }
++
++    None
++}
++
++#[derive(PartialEq, Eq)]
++enum Sign {
++    Pos,
++    Neg,
++}
++
++fn lit_sign(expr: &hir::Expr<'_>) -> Option<Sign> {
++    if let hir::ExprKind::Unary(hir::UnOp::UnNeg, inner) = &expr.kind {
++        if let hir::ExprKind::Lit(..) = &inner.kind {
++            return Some(Sign::Neg);
++        }
++    } else if let hir::ExprKind::Lit(..) = &expr.kind {
++        return Some(Sign::Pos);
++    }
++
++    None
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3676dc5b09d21d7fdddcda2a79e4fd5677f9d780
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3657 @@@
++mod inefficient_to_string;
++mod manual_saturating_arithmetic;
++mod option_map_unwrap_or;
++mod unnecessary_filter_map;
++
++use std::borrow::Cow;
++use std::fmt;
++use std::iter;
++
++use if_chain::if_chain;
++use rustc_ast::ast;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::intravisit::{self, Visitor};
++use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty::subst::GenericArgKind;
++use rustc_middle::ty::{self, Predicate, Ty};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++use rustc_span::symbol::{sym, SymbolStr};
++
++use crate::consts::{constant, Constant};
++use crate::utils::usage::mutated_variables;
++use crate::utils::{
++    get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, in_macro, is_copy,
++    is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment,
++    match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths,
++    remove_blocks, return_ty, same_tys, single_segment_path, snippet, snippet_with_applicability,
++    snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
++    span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq,
++};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `.unwrap()` calls on `Option`s.
++    ///
++    /// **Why is this bad?** Usually it is better to handle the `None` case, or to
++    /// at least call `.expect(_)` with a more helpful message. Still, for a lot of
++    /// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is
++    /// `Allow` by default.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// Using unwrap on an `Option`:
++    ///
++    /// ```rust
++    /// let opt = Some(1);
++    /// opt.unwrap();
++    /// ```
++    ///
++    /// Better:
++    ///
++    /// ```rust
++    /// let opt = Some(1);
++    /// opt.expect("more helpful message");
++    /// ```
++    pub OPTION_UNWRAP_USED,
++    restriction,
++    "using `Option.unwrap()`, which should at least get a better message using `expect()`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `.unwrap()` calls on `Result`s.
++    ///
++    /// **Why is this bad?** `result.unwrap()` will let the thread panic on `Err`
++    /// values. Normally, you want to implement more sophisticated error handling,
++    /// and propagate errors upwards with `?` operator.
++    ///
++    /// Even if you want to panic on errors, not all `Error`s implement good
++    /// messages on display. Therefore, it may be beneficial to look at the places
++    /// where they may get displayed. Activate this lint to do just that.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// Using unwrap on an `Result`:
++    ///
++    /// ```rust
++    /// let res: Result<usize, ()> = Ok(1);
++    /// res.unwrap();
++    /// ```
++    ///
++    /// Better:
++    ///
++    /// ```rust
++    /// let res: Result<usize, ()> = Ok(1);
++    /// res.expect("more helpful message");
++    /// ```
++    pub RESULT_UNWRAP_USED,
++    restriction,
++    "using `Result.unwrap()`, which might be better handled"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `.expect()` calls on `Option`s.
++    ///
++    /// **Why is this bad?** Usually it is better to handle the `None` case. Still,
++    ///  for a lot of quick-and-dirty code, `expect` is a good choice, which is why
++    ///  this lint is `Allow` by default.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// Using expect on an `Option`:
++    ///
++    /// ```rust
++    /// let opt = Some(1);
++    /// opt.expect("one");
++    /// ```
++    ///
++    /// Better:
++    ///
++    /// ```rust,ignore
++    /// let opt = Some(1);
++    /// opt?;
++    /// ```
++    pub OPTION_EXPECT_USED,
++    restriction,
++    "using `Option.expect()`, which might be better handled"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `.expect()` calls on `Result`s.
++    ///
++    /// **Why is this bad?** `result.expect()` will let the thread panic on `Err`
++    /// values. Normally, you want to implement more sophisticated error handling,
++    /// and propagate errors upwards with `?` operator.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// Using expect on an `Result`:
++    ///
++    /// ```rust
++    /// let res: Result<usize, ()> = Ok(1);
++    /// res.expect("one");
++    /// ```
++    ///
++    /// Better:
++    ///
++    /// ```rust
++    /// let res: Result<usize, ()> = Ok(1);
++    /// res?;
++    /// # Ok::<(), ()>(())
++    /// ```
++    pub RESULT_EXPECT_USED,
++    restriction,
++    "using `Result.expect()`, which might be better handled"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for methods that should live in a trait
++    /// implementation of a `std` trait (see [llogiq's blog
++    /// post](http://llogiq.github.io/2015/07/30/traits.html) for further
++    /// information) instead of an inherent implementation.
++    ///
++    /// **Why is this bad?** Implementing the traits improve ergonomics for users of
++    /// the code, often with very little cost. Also people seeing a `mul(...)`
++    /// method
++    /// may expect `*` to work equally, so you should have good reason to disappoint
++    /// them.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// struct X;
++    /// impl X {
++    ///     fn add(&self, other: &X) -> X {
++    ///         // ..
++    /// # X
++    ///     }
++    /// }
++    /// ```
++    pub SHOULD_IMPLEMENT_TRAIT,
++    style,
++    "defining a method that should be implementing a std trait"
++}
++
++declare_clippy_lint! {
++    /// **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`               |
++    ///
++    /// **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
++    /// mutable reference to a `as_..` function.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # struct X;
++    /// impl X {
++    ///     fn as_str(self) -> &'static str {
++    ///         // ..
++    /// # ""
++    ///     }
++    /// }
++    /// ```
++    pub WRONG_SELF_CONVENTION,
++    style,
++    "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** This is the same as
++    /// [`wrong_self_convention`](#wrong_self_convention), but for public items.
++    ///
++    /// **Why is this bad?** See [`wrong_self_convention`](#wrong_self_convention).
++    ///
++    /// **Known problems:** Actually *renaming* the function may break clients if
++    /// the function is part of the public interface. In that case, be mindful of
++    /// the stability guarantees you've given your users.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # struct X;
++    /// impl<'a> X {
++    ///     pub fn as_str(self) -> &'a str {
++    ///         "foo"
++    ///     }
++    /// }
++    /// ```
++    pub WRONG_PUB_SELF_CONVENTION,
++    restriction,
++    "defining a public method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `ok().expect(..)`.
++    ///
++    /// **Why is this bad?** Because you usually call `expect()` on the `Result`
++    /// directly to get a better error message.
++    ///
++    /// **Known problems:** The error type needs to implement `Debug`
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let x = Ok::<_, ()>(());
++    /// x.ok().expect("why did I do this again?")
++    /// ```
++    pub OK_EXPECT,
++    style,
++    "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `_.map(_).unwrap_or(_)`.
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely as
++    /// `_.map_or(_, _)`.
++    ///
++    /// **Known problems:** The order of the arguments is not in execution order
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let x = Some(1);
++    /// x.map(|a| a + 1).unwrap_or(0);
++    /// ```
++    pub OPTION_MAP_UNWRAP_OR,
++    pedantic,
++    "using `Option.map(f).unwrap_or(a)`, which is more succinctly expressed as `map_or(a, f)`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `_.map(_).unwrap_or_else(_)`.
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely as
++    /// `_.map_or_else(_, _)`.
++    ///
++    /// **Known problems:** The order of the arguments is not in execution order.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let x = Some(1);
++    /// # fn some_function() -> usize { 1 }
++    /// x.map(|a| a + 1).unwrap_or_else(some_function);
++    /// ```
++    pub OPTION_MAP_UNWRAP_OR_ELSE,
++    pedantic,
++    "using `Option.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `map_or_else(g, f)`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `result.map(_).unwrap_or_else(_)`.
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely as
++    /// `result.map_or_else(_, _)`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let x: Result<usize, ()> = Ok(1);
++    /// # fn some_function(foo: ()) -> usize { 1 }
++    /// x.map(|a| a + 1).unwrap_or_else(some_function);
++    /// ```
++    pub RESULT_MAP_UNWRAP_OR_ELSE,
++    pedantic,
++    "using `Result.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `.map_or_else(g, f)`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `_.map_or(None, _)`.
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely as
++    /// `_.and_then(_)`.
++    ///
++    /// **Known problems:** The order of the arguments is not in execution order.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let opt = Some(1);
++    /// opt.map_or(None, |a| Some(a + 1))
++    /// # ;
++    /// ```
++    pub OPTION_MAP_OR_NONE,
++    style,
++    "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `_.map_or(None, Some)`.
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely as
++    /// `_.ok()`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// Bad:
++    /// ```rust
++    /// # let r: Result<u32, &str> = Ok(1);
++    /// assert_eq!(Some(1), r.map_or(None, Some));
++    /// ```
++    ///
++    /// Good:
++    /// ```rust
++    /// # let r: Result<u32, &str> = Ok(1);
++    /// assert_eq!(Some(1), r.ok());
++    /// ```
++    pub RESULT_MAP_OR_INTO_OPTION,
++    style,
++    "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `_.and_then(|x| Some(y))`.
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely as
++    /// `_.map(|x| y)`.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// let x = Some("foo");
++    /// let _ = x.and_then(|s| Some(s.len()));
++    /// ```
++    ///
++    /// The correct use would be:
++    ///
++    /// ```rust
++    /// let x = Some("foo");
++    /// let _ = x.map(|s| s.len());
++    /// ```
++    pub OPTION_AND_THEN_SOME,
++    complexity,
++    "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `_.filter(_).next()`.
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely as
++    /// `_.find(_)`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let vec = vec![1];
++    /// vec.iter().filter(|x| **x == 0).next();
++    /// ```
++    /// Could be written as
++    /// ```rust
++    /// # let vec = vec![1];
++    /// vec.iter().find(|x| **x == 0);
++    /// ```
++    pub FILTER_NEXT,
++    complexity,
++    "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `_.skip_while(condition).next()`.
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely as
++    /// `_.find(!condition)`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let vec = vec![1];
++    /// vec.iter().skip_while(|x| **x == 0).next();
++    /// ```
++    /// Could be written as
++    /// ```rust
++    /// # let vec = vec![1];
++    /// vec.iter().find(|x| **x != 0);
++    /// ```
++    pub SKIP_WHILE_NEXT,
++    complexity,
++    "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `_.map(_).flatten(_)`,
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely as a
++    /// single method call.
++    ///
++    /// **Known problems:**
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let vec = vec![vec![1]];
++    /// vec.iter().map(|x| x.iter()).flatten();
++    /// ```
++    pub MAP_FLATTEN,
++    pedantic,
++    "using combinations of `flatten` and `map` which can usually be written as a single method call"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `_.filter(_).map(_)`,
++    /// `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar.
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely as a
++    /// single method call.
++    ///
++    /// **Known problems:** Often requires a condition + Option/Iterator creation
++    /// inside the closure.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let vec = vec![1];
++    /// vec.iter().filter(|x| **x == 0).map(|x| *x * 2);
++    /// ```
++    pub FILTER_MAP,
++    pedantic,
++    "using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `_.filter_map(_).next()`.
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely as a
++    /// single method call.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// ```rust
++    ///  (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();
++    /// ```
++    /// Can be written as
++    ///
++    /// ```rust
++    ///  (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
++    /// ```
++    pub FILTER_MAP_NEXT,
++    pedantic,
++    "using combination of `filter_map` and `next` which can usually be written as a single method call"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `flat_map(|x| x)`.
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely by using `flatten`.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let iter = vec![vec![0]].into_iter();
++    /// iter.flat_map(|x| x);
++    /// ```
++    /// Can be written as
++    /// ```rust
++    /// # let iter = vec![vec![0]].into_iter();
++    /// iter.flatten();
++    /// ```
++    pub FLAT_MAP_IDENTITY,
++    complexity,
++    "call to `flat_map` where `flatten` is sufficient"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `_.find(_).map(_)`.
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely as a
++    /// single method call.
++    ///
++    /// **Known problems:** Often requires a condition + Option/Iterator creation
++    /// inside the closure.
++    ///
++    /// **Example:**
++    /// ```rust
++    ///  (0..3).find(|x| *x == 2).map(|x| x * 2);
++    /// ```
++    /// Can be written as
++    /// ```rust
++    ///  (0..3).find_map(|x| if x == 2 { Some(x * 2) } else { None });
++    /// ```
++    pub FIND_MAP,
++    pedantic,
++    "using a combination of `find` and `map` can usually be written as a single method call"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for an iterator search (such as `find()`,
++    /// `position()`, or `rposition()`) followed by a call to `is_some()`.
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely as
++    /// `_.any(_)`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let vec = vec![1];
++    /// vec.iter().find(|x| **x == 0).is_some();
++    /// ```
++    /// Could be written as
++    /// ```rust
++    /// # let vec = vec![1];
++    /// vec.iter().any(|x| *x == 0);
++    /// ```
++    pub SEARCH_IS_SOME,
++    complexity,
++    "using an iterator search followed by `is_some()`, which is more succinctly expressed as a call to `any()`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `.chars().next()` on a `str` to check
++    /// if it starts with a given char.
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely as
++    /// `_.starts_with(_)`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let name = "foo";
++    /// if name.chars().next() == Some('_') {};
++    /// ```
++    /// Could be written as
++    /// ```rust
++    /// let name = "foo";
++    /// if name.starts_with('_') {};
++    /// ```
++    pub CHARS_NEXT_CMP,
++    style,
++    "using `.chars().next()` to check if a string starts with a char"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,
++    /// etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or
++    /// `unwrap_or_default` instead.
++    ///
++    /// **Why is this bad?** The function will always be called and potentially
++    /// allocate an object acting as the default.
++    ///
++    /// **Known problems:** If the function has side-effects, not calling it will
++    /// change the semantic of the program, but you shouldn't rely on that anyway.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let foo = Some(String::new());
++    /// foo.unwrap_or(String::new());
++    /// ```
++    /// this can instead be written:
++    /// ```rust
++    /// # let foo = Some(String::new());
++    /// foo.unwrap_or_else(String::new);
++    /// ```
++    /// or
++    /// ```rust
++    /// # let foo = Some(String::new());
++    /// foo.unwrap_or_default();
++    /// ```
++    pub OR_FUN_CALL,
++    perf,
++    "using any `*or` method with a function call, which suggests `*or_else`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,
++    /// etc., and suggests to use `unwrap_or_else` instead
++    ///
++    /// **Why is this bad?** The function will always be called.
++    ///
++    /// **Known problems:** If the function has side-effects, not calling it will
++    /// change the semantics of the program, but you shouldn't rely on that anyway.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let foo = Some(String::new());
++    /// # let err_code = "418";
++    /// # let err_msg = "I'm a teapot";
++    /// foo.expect(&format!("Err {}: {}", err_code, err_msg));
++    /// ```
++    /// or
++    /// ```rust
++    /// # let foo = Some(String::new());
++    /// # let err_code = "418";
++    /// # let err_msg = "I'm a teapot";
++    /// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str());
++    /// ```
++    /// this can instead be written:
++    /// ```rust
++    /// # let foo = Some(String::new());
++    /// # let err_code = "418";
++    /// # let err_msg = "I'm a teapot";
++    /// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
++    /// ```
++    pub EXPECT_FUN_CALL,
++    perf,
++    "using any `expect` method with a function call"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `.clone()` on a `Copy` type.
++    ///
++    /// **Why is this bad?** The only reason `Copy` types implement `Clone` is for
++    /// generics, not for using the `clone` method on a concrete type.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// 42u64.clone();
++    /// ```
++    pub CLONE_ON_COPY,
++    complexity,
++    "using `clone` on a `Copy` type"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `.clone()` on a ref-counted pointer,
++    /// (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified
++    /// function syntax instead (e.g., `Rc::clone(foo)`).
++    ///
++    /// **Why is this bad?** Calling '.clone()' on an Rc, Arc, or Weak
++    /// can obscure the fact that only the pointer is being cloned, not the underlying
++    /// data.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # use std::rc::Rc;
++    /// let x = Rc::new(1);
++    /// x.clone();
++    /// ```
++    pub CLONE_ON_REF_PTR,
++    restriction,
++    "using 'clone' on a ref-counted pointer"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `.clone()` on an `&&T`.
++    ///
++    /// **Why is this bad?** Cloning an `&&T` copies the inner `&T`, instead of
++    /// cloning the underlying `T`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// fn main() {
++    ///     let x = vec![1];
++    ///     let y = &&x;
++    ///     let z = y.clone();
++    ///     println!("{:p} {:p}", *y, z); // prints out the same pointer
++    /// }
++    /// ```
++    pub CLONE_DOUBLE_REF,
++    correctness,
++    "using `clone` on `&&T`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `.to_string()` on an `&&T` where
++    /// `T` implements `ToString` directly (like `&&str` or `&&String`).
++    ///
++    /// **Why is this bad?** This bypasses the specialized implementation of
++    /// `ToString` and instead goes through the more expensive string formatting
++    /// facilities.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// // Generic implementation for `T: Display` is used (slow)
++    /// ["foo", "bar"].iter().map(|s| s.to_string());
++    ///
++    /// // OK, the specialized impl is used
++    /// ["foo", "bar"].iter().map(|&s| s.to_string());
++    /// ```
++    pub INEFFICIENT_TO_STRING,
++    pedantic,
++    "using `to_string` on `&&T` where `T: ToString`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `new` not returning a type that contains `Self`.
++    ///
++    /// **Why is this bad?** As a convention, `new` methods are used to make a new
++    /// instance of a type.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # struct Foo;
++    /// # struct NotAFoo;
++    /// impl Foo {
++    ///     fn new() -> NotAFoo {
++    /// # NotAFoo
++    ///     }
++    /// }
++    /// ```
++    ///
++    /// ```rust
++    /// # struct Foo;
++    /// # struct FooError;
++    /// impl Foo {
++    ///     // Good. Return type contains `Self`
++    ///     fn new() -> Result<Foo, FooError> {
++    ///         # Ok(Foo)
++    ///     }
++    /// }
++    /// ```
++    ///
++    /// ```rust
++    /// # struct Foo;
++    /// struct Bar(Foo);
++    /// impl Foo {
++    ///     // Bad. The type name must contain `Self`.
++    ///     fn new() -> Bar {
++    ///         # Bar(Foo)
++    ///     }
++    /// }
++    /// ```
++    pub NEW_RET_NO_SELF,
++    style,
++    "not returning type containing `Self` in a `new` method"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for string methods that receive a single-character
++    /// `str` as an argument, e.g., `_.split("x")`.
++    ///
++    /// **Why is this bad?** Performing these methods using a `char` is faster than
++    /// using a `str`.
++    ///
++    /// **Known problems:** Does not catch multi-byte unicode characters.
++    ///
++    /// **Example:**
++    /// `_.split("x")` could be `_.split('x')`
++    pub SINGLE_CHAR_PATTERN,
++    perf,
++    "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for getting the inner pointer of a temporary
++    /// `CString`.
++    ///
++    /// **Why is this bad?** The inner pointer of a `CString` is only valid as long
++    /// as the `CString` is alive.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # use std::ffi::CString;
++    /// # fn call_some_ffi_func(_: *const i8) {}
++    /// #
++    /// let c_str = CString::new("foo").unwrap().as_ptr();
++    /// unsafe {
++    ///     call_some_ffi_func(c_str);
++    /// }
++    /// ```
++    /// Here `c_str` point to a freed address. The correct use would be:
++    /// ```rust
++    /// # use std::ffi::CString;
++    /// # fn call_some_ffi_func(_: *const i8) {}
++    /// #
++    /// let c_str = CString::new("foo").unwrap();
++    /// unsafe {
++    ///     call_some_ffi_func(c_str.as_ptr());
++    /// }
++    /// ```
++    pub TEMPORARY_CSTRING_AS_PTR,
++    correctness,
++    "getting the inner pointer of a temporary `CString`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for calling `.step_by(0)` on iterators which panics.
++    ///
++    /// **Why is this bad?** This very much looks like an oversight. Use `panic!()` instead if you
++    /// actually intend to panic.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,should_panic
++    /// for x in (0..100).step_by(0) {
++    ///     //..
++    /// }
++    /// ```
++    pub ITERATOR_STEP_BY_ZERO,
++    correctness,
++    "using `Iterator::step_by(0)`, which will panic at runtime"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for the use of `iter.nth(0)`.
++    ///
++    /// **Why is this bad?** `iter.next()` is equivalent to
++    /// `iter.nth(0)`, as they both consume the next element,
++    ///  but is more readable.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// # use std::collections::HashSet;
++    /// // Bad
++    /// # let mut s = HashSet::new();
++    /// # s.insert(1);
++    /// let x = s.iter().nth(0);
++    ///
++    /// // Good
++    /// # let mut s = HashSet::new();
++    /// # s.insert(1);
++    /// let x = s.iter().next();
++    /// ```
++    pub ITER_NTH_ZERO,
++    style,
++    "replace `iter.nth(0)` with `iter.next()`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for use of `.iter().nth()` (and the related
++    /// `.iter_mut().nth()`) on standard library types with O(1) element access.
++    ///
++    /// **Why is this bad?** `.get()` and `.get_mut()` are more efficient and more
++    /// readable.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let some_vec = vec![0, 1, 2, 3];
++    /// let bad_vec = some_vec.iter().nth(3);
++    /// let bad_slice = &some_vec[..].iter().nth(3);
++    /// ```
++    /// The correct use would be:
++    /// ```rust
++    /// let some_vec = vec![0, 1, 2, 3];
++    /// let bad_vec = some_vec.get(3);
++    /// let bad_slice = &some_vec[..].get(3);
++    /// ```
++    pub ITER_NTH,
++    perf,
++    "using `.iter().nth()` on a standard library type with O(1) element access"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for use of `.skip(x).next()` on iterators.
++    ///
++    /// **Why is this bad?** `.nth(x)` is cleaner
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let some_vec = vec![0, 1, 2, 3];
++    /// let bad_vec = some_vec.iter().skip(3).next();
++    /// let bad_slice = &some_vec[..].iter().skip(3).next();
++    /// ```
++    /// The correct use would be:
++    /// ```rust
++    /// let some_vec = vec![0, 1, 2, 3];
++    /// let bad_vec = some_vec.iter().nth(3);
++    /// let bad_slice = &some_vec[..].iter().nth(3);
++    /// ```
++    pub ITER_SKIP_NEXT,
++    style,
++    "using `.skip(x).next()` on an iterator"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for use of `.get().unwrap()` (or
++    /// `.get_mut().unwrap`) on a standard library type which implements `Index`
++    ///
++    /// **Why is this bad?** Using the Index trait (`[]`) is more clear and more
++    /// concise.
++    ///
++    /// **Known problems:** Not a replacement for error handling: Using either
++    /// `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`
++    /// if the value being accessed is `None`. If the use of `.get().unwrap()` is a
++    /// temporary placeholder for dealing with the `Option` type, then this does
++    /// not mitigate the need for error handling. If there is a chance that `.get()`
++    /// will be `None` in your program, then it is advisable that the `None` case
++    /// is handled in a future refactor instead of using `.unwrap()` or the Index
++    /// trait.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let mut some_vec = vec![0, 1, 2, 3];
++    /// let last = some_vec.get(3).unwrap();
++    /// *some_vec.get_mut(0).unwrap() = 1;
++    /// ```
++    /// The correct use would be:
++    /// ```rust
++    /// let mut some_vec = vec![0, 1, 2, 3];
++    /// let last = some_vec[3];
++    /// some_vec[0] = 1;
++    /// ```
++    pub GET_UNWRAP,
++    restriction,
++    "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for the use of `.extend(s.chars())` where s is a
++    /// `&str` or `String`.
++    ///
++    /// **Why is this bad?** `.push_str(s)` is clearer
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let abc = "abc";
++    /// let def = String::from("def");
++    /// let mut s = String::new();
++    /// s.extend(abc.chars());
++    /// s.extend(def.chars());
++    /// ```
++    /// The correct use would be:
++    /// ```rust
++    /// let abc = "abc";
++    /// let def = String::from("def");
++    /// let mut s = String::new();
++    /// s.push_str(abc);
++    /// s.push_str(&def);
++    /// ```
++    pub STRING_EXTEND_CHARS,
++    style,
++    "using `x.extend(s.chars())` where s is a `&str` or `String`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for the use of `.cloned().collect()` on slice to
++    /// create a `Vec`.
++    ///
++    /// **Why is this bad?** `.to_vec()` is clearer
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let s = [1, 2, 3, 4, 5];
++    /// let s2: Vec<isize> = s[..].iter().cloned().collect();
++    /// ```
++    /// The better use would be:
++    /// ```rust
++    /// let s = [1, 2, 3, 4, 5];
++    /// let s2: Vec<isize> = s.to_vec();
++    /// ```
++    pub ITER_CLONED_COLLECT,
++    style,
++    "using `.cloned().collect()` on slice to create a `Vec`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `.chars().last()` or
++    /// `.chars().next_back()` on a `str` to check if it ends with a given char.
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely as
++    /// `_.ends_with(_)`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let name = "_";
++    /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-')
++    /// # ;
++    /// ```
++    pub CHARS_LAST_CMP,
++    style,
++    "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `.as_ref()` or `.as_mut()` where the
++    /// types before and after the call are the same.
++    ///
++    /// **Why is this bad?** The call is unnecessary.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # fn do_stuff(x: &[i32]) {}
++    /// let x: &[i32] = &[1, 2, 3, 4, 5];
++    /// do_stuff(x.as_ref());
++    /// ```
++    /// The correct use would be:
++    /// ```rust
++    /// # fn do_stuff(x: &[i32]) {}
++    /// let x: &[i32] = &[1, 2, 3, 4, 5];
++    /// do_stuff(x);
++    /// ```
++    pub USELESS_ASREF,
++    complexity,
++    "using `as_ref` where the types before and after the call are the same"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for using `fold` when a more succinct alternative exists.
++    /// Specifically, this checks for `fold`s which could be replaced by `any`, `all`,
++    /// `sum` or `product`.
++    ///
++    /// **Why is this bad?** Readability.
++    ///
++    /// **Known problems:** False positive in pattern guards. Will be resolved once
++    /// non-lexical lifetimes are stable.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let _ = (0..3).fold(false, |acc, x| acc || x > 2);
++    /// ```
++    /// This could be written as:
++    /// ```rust
++    /// let _ = (0..3).any(|x| x > 2);
++    /// ```
++    pub UNNECESSARY_FOLD,
++    style,
++    "using `fold` when a more succinct alternative exists"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `filter_map` calls which could be replaced by `filter` or `map`.
++    /// More specifically it checks if the closure provided is only performing one of the
++    /// filter or map operations and suggests the appropriate option.
++    ///
++    /// **Why is this bad?** Complexity. The intent is also clearer if only a single
++    /// operation is being performed.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });
++    /// ```
++    /// As there is no transformation of the argument this could be written as:
++    /// ```rust
++    /// let _ = (0..3).filter(|&x| x > 2);
++    /// ```
++    ///
++    /// ```rust
++    /// let _ = (0..4).filter_map(|x| Some(x + 1));
++    /// ```
++    /// As there is no conditional check on the argument this could be written as:
++    /// ```rust
++    /// let _ = (0..4).map(|x| x + 1);
++    /// ```
++    pub UNNECESSARY_FILTER_MAP,
++    complexity,
++    "using `filter_map` when a more succinct alternative exists"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `into_iter` calls on references which should be replaced by `iter`
++    /// or `iter_mut`.
++    ///
++    /// **Why is this bad?** Readability. Calling `into_iter` on a reference will not move out its
++    /// content into the resulting iterator, which is confusing. It is better just call `iter` or
++    /// `iter_mut` directly.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// let _ = (&vec![3, 4, 5]).into_iter();
++    /// ```
++    pub INTO_ITER_ON_REF,
++    style,
++    "using `.into_iter()` on a reference"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for calls to `map` followed by a `count`.
++    ///
++    /// **Why is this bad?** It looks suspicious. Maybe `map` was confused with `filter`.
++    /// If the `map` call is intentional, this should be rewritten. Or, if you intend to
++    /// drive the iterator to completion, you can just use `for_each` instead.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// let _ = (0..3).map(|x| x + 2).count();
++    /// ```
++    pub SUSPICIOUS_MAP,
++    complexity,
++    "suspicious usage of map"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `MaybeUninit::uninit().assume_init()`.
++    ///
++    /// **Why is this bad?** For most types, this is undefined behavior.
++    ///
++    /// **Known problems:** For now, we accept empty tuples and tuples / arrays
++    /// of `MaybeUninit`. There may be other types that allow uninitialized
++    /// data, but those are not yet rigorously defined.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// // Beware the UB
++    /// use std::mem::MaybeUninit;
++    ///
++    /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
++    /// ```
++    ///
++    /// Note that the following is OK:
++    ///
++    /// ```rust
++    /// use std::mem::MaybeUninit;
++    ///
++    /// let _: [MaybeUninit<bool>; 5] = unsafe {
++    ///     MaybeUninit::uninit().assume_init()
++    /// };
++    /// ```
++    pub UNINIT_ASSUMED_INIT,
++    correctness,
++    "`MaybeUninit::uninit().assume_init()`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.
++    ///
++    /// **Why is this bad?** These can be written simply with `saturating_add/sub` methods.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// # let y: u32 = 0;
++    /// # let x: u32 = 100;
++    /// let add = x.checked_add(y).unwrap_or(u32::MAX);
++    /// let sub = x.checked_sub(y).unwrap_or(u32::MIN);
++    /// ```
++    ///
++    /// can be written using dedicated methods for saturating addition/subtraction as:
++    ///
++    /// ```rust
++    /// # let y: u32 = 0;
++    /// # let x: u32 = 100;
++    /// let add = x.saturating_add(y);
++    /// let sub = x.saturating_sub(y);
++    /// ```
++    pub MANUAL_SATURATING_ARITHMETIC,
++    style,
++    "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
++    /// zero-sized types
++    ///
++    /// **Why is this bad?** This is a no-op, and likely unintended
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// ```rust
++    /// unsafe { (&() as *const ()).offset(1) };
++    /// ```
++    pub ZST_OFFSET,
++    correctness,
++    "Check for offset calculations on raw pointers to zero-sized types"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `FileType::is_file()`.
++    ///
++    /// **Why is this bad?** When people testing a file type with `FileType::is_file`
++    /// they are testing whether a path is something they can get bytes from. But
++    /// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover
++    /// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// # || {
++    /// let metadata = std::fs::metadata("foo.txt")?;
++    /// let filetype = metadata.file_type();
++    ///
++    /// if filetype.is_file() {
++    ///     // read file
++    /// }
++    /// # Ok::<_, std::io::Error>(())
++    /// # };
++    /// ```
++    ///
++    /// should be written as:
++    ///
++    /// ```rust
++    /// # || {
++    /// let metadata = std::fs::metadata("foo.txt")?;
++    /// let filetype = metadata.file_type();
++    ///
++    /// if !filetype.is_dir() {
++    ///     // read file
++    /// }
++    /// # Ok::<_, std::io::Error>(())
++    /// # };
++    /// ```
++    pub FILETYPE_IS_FILE,
++    restriction,
++    "`FileType::is_file` is not recommended to test for readable file type"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).
++    ///
++    /// **Why is this bad?** Readability, this can be written more concisely as a
++    /// single method call.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let opt = Some("".to_string());
++    /// opt.as_ref().map(String::as_str)
++    /// # ;
++    /// ```
++    /// Can be written as
++    /// ```rust
++    /// # let opt = Some("".to_string());
++    /// opt.as_deref()
++    /// # ;
++    /// ```
++    pub OPTION_AS_REF_DEREF,
++    complexity,
++    "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
++}
++
++declare_lint_pass!(Methods => [
++    OPTION_UNWRAP_USED,
++    RESULT_UNWRAP_USED,
++    OPTION_EXPECT_USED,
++    RESULT_EXPECT_USED,
++    SHOULD_IMPLEMENT_TRAIT,
++    WRONG_SELF_CONVENTION,
++    WRONG_PUB_SELF_CONVENTION,
++    OK_EXPECT,
++    OPTION_MAP_UNWRAP_OR,
++    OPTION_MAP_UNWRAP_OR_ELSE,
++    RESULT_MAP_UNWRAP_OR_ELSE,
++    RESULT_MAP_OR_INTO_OPTION,
++    OPTION_MAP_OR_NONE,
++    OPTION_AND_THEN_SOME,
++    OR_FUN_CALL,
++    EXPECT_FUN_CALL,
++    CHARS_NEXT_CMP,
++    CHARS_LAST_CMP,
++    CLONE_ON_COPY,
++    CLONE_ON_REF_PTR,
++    CLONE_DOUBLE_REF,
++    INEFFICIENT_TO_STRING,
++    NEW_RET_NO_SELF,
++    SINGLE_CHAR_PATTERN,
++    SEARCH_IS_SOME,
++    TEMPORARY_CSTRING_AS_PTR,
++    FILTER_NEXT,
++    SKIP_WHILE_NEXT,
++    FILTER_MAP,
++    FILTER_MAP_NEXT,
++    FLAT_MAP_IDENTITY,
++    FIND_MAP,
++    MAP_FLATTEN,
++    ITERATOR_STEP_BY_ZERO,
++    ITER_NTH,
++    ITER_NTH_ZERO,
++    ITER_SKIP_NEXT,
++    GET_UNWRAP,
++    STRING_EXTEND_CHARS,
++    ITER_CLONED_COLLECT,
++    USELESS_ASREF,
++    UNNECESSARY_FOLD,
++    UNNECESSARY_FILTER_MAP,
++    INTO_ITER_ON_REF,
++    SUSPICIOUS_MAP,
++    UNINIT_ASSUMED_INIT,
++    MANUAL_SATURATING_ARITHMETIC,
++    ZST_OFFSET,
++    FILETYPE_IS_FILE,
++    OPTION_AS_REF_DEREF,
++]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
++    #[allow(clippy::too_many_lines)]
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if in_macro(expr.span) {
++            return;
++        }
++
++        let (method_names, arg_lists, method_spans) = method_calls(expr, 2);
++        let method_names: Vec<SymbolStr> = method_names.iter().map(|s| s.as_str()).collect();
++        let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect();
++
++        match method_names.as_slice() {
++            ["unwrap", "get"] => lint_get_unwrap(cx, expr, arg_lists[1], false),
++            ["unwrap", "get_mut"] => lint_get_unwrap(cx, expr, arg_lists[1], true),
++            ["unwrap", ..] => lint_unwrap(cx, expr, arg_lists[0]),
++            ["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]),
++            ["expect", ..] => lint_expect(cx, expr, arg_lists[0]),
++            ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]),
++            ["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]),
++            ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
++            ["and_then", ..] => lint_option_and_then_some(cx, expr, arg_lists[0]),
++            ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]),
++            ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]),
++            ["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]),
++            ["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]),
++            ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1]),
++            ["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]),
++            ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
++            ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
++            ["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0], method_spans[0]),
++            ["flatten", "map"] => lint_map_flatten(cx, expr, arg_lists[1]),
++            ["is_some", "find"] => lint_search_is_some(cx, expr, "find", arg_lists[1], arg_lists[0], method_spans[1]),
++            ["is_some", "position"] => {
++                lint_search_is_some(cx, expr, "position", arg_lists[1], arg_lists[0], method_spans[1])
++            },
++            ["is_some", "rposition"] => {
++                lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1])
++            },
++            ["extend", ..] => lint_extend(cx, expr, arg_lists[0]),
++            ["as_ptr", "unwrap"] | ["as_ptr", "expect"] => {
++                lint_cstring_as_ptr(cx, expr, &arg_lists[1][0], &arg_lists[0][0])
++            },
++            ["nth", "iter"] => lint_iter_nth(cx, expr, &arg_lists, false),
++            ["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true),
++            ["nth", ..] => lint_iter_nth_zero(cx, expr, arg_lists[0]),
++            ["step_by", ..] => lint_step_by(cx, expr, arg_lists[0]),
++            ["next", "skip"] => lint_iter_skip_next(cx, expr),
++            ["collect", "cloned"] => lint_iter_cloned_collect(cx, expr, arg_lists[1]),
++            ["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]),
++            ["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]),
++            ["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0], method_spans[0]),
++            ["filter_map", ..] => unnecessary_filter_map::lint(cx, expr, arg_lists[0]),
++            ["count", "map"] => lint_suspicious_map(cx, expr),
++            ["assume_init"] => lint_maybe_uninit(cx, &arg_lists[0][0], expr),
++            ["unwrap_or", arith @ "checked_add"]
++            | ["unwrap_or", arith @ "checked_sub"]
++            | ["unwrap_or", arith @ "checked_mul"] => {
++                manual_saturating_arithmetic::lint(cx, expr, &arg_lists, &arith["checked_".len()..])
++            },
++            ["add"] | ["offset"] | ["sub"] | ["wrapping_offset"] | ["wrapping_add"] | ["wrapping_sub"] => {
++                check_pointer_offset(cx, expr, arg_lists[0])
++            },
++            ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]),
++            ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false),
++            ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true),
++            _ => {},
++        }
++
++        match expr.kind {
++            hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args) => {
++                lint_or_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
++                lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
++
++                let self_ty = cx.tables.expr_ty_adjusted(&args[0]);
++                if args.len() == 1 && method_call.ident.name == sym!(clone) {
++                    lint_clone_on_copy(cx, expr, &args[0], self_ty);
++                    lint_clone_on_ref_ptr(cx, expr, &args[0]);
++                }
++                if args.len() == 1 && method_call.ident.name == sym!(to_string) {
++                    inefficient_to_string::lint(cx, expr, &args[0], self_ty);
++                }
++
++                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 {
++                                lint_single_char_pattern(cx, expr, &args[pos]);
++                            }
++                        }
++                    },
++                    ty::Ref(..) if method_call.ident.name == sym!(into_iter) => {
++                        lint_into_iter(cx, expr, self_ty, *method_span);
++                    },
++                    _ => (),
++                }
++            },
++            hir::ExprKind::Binary(op, ref lhs, ref rhs)
++                if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne =>
++            {
++                let mut info = BinaryExprInfo {
++                    expr,
++                    chain: lhs,
++                    other: rhs,
++                    eq: op.node == hir::BinOpKind::Eq,
++                };
++                lint_binary_expr_with_method_call(cx, &mut info);
++            }
++            _ => (),
++        }
++    }
++
++    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
++        if in_external_macro(cx.sess(), impl_item.span) {
++            return;
++        }
++        let name = impl_item.ident.name.as_str();
++        let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id);
++        let item = cx.tcx.hir().expect_item(parent);
++        let def_id = cx.tcx.hir().local_def_id(item.hir_id);
++        let self_ty = cx.tcx.type_of(def_id);
++        if_chain! {
++            if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind;
++            if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir().body(id)).next();
++            if let hir::ItemKind::Impl{ of_trait: None, .. } = item.kind;
++
++            let method_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
++            let method_sig = cx.tcx.fn_sig(method_def_id);
++            let method_sig = cx.tcx.erase_late_bound_regions(&method_sig);
++
++            let first_arg_ty = &method_sig.inputs().iter().next();
++
++            // check conventions w.r.t. conversion method names and predicates
++            if let Some(first_arg_ty) = first_arg_ty;
++
++            then {
++                if cx.access_levels.is_exported(impl_item.hir_id) {
++                // check missing trait implementations
++                    for &(method_name, n_args, fn_header, self_kind, out_type, trait_name) in &TRAIT_METHODS {
++                        if name == method_name &&
++                            sig.decl.inputs.len() == n_args &&
++                            out_type.matches(cx, &sig.decl.output) &&
++                            self_kind.matches(cx, self_ty, first_arg_ty) &&
++                            fn_header_equals(*fn_header, sig.header) {
++                            span_lint(cx, SHOULD_IMPLEMENT_TRAIT, impl_item.span, &format!(
++                                "defining a method called `{}` on this type; consider implementing \
++                                the `{}` trait or choosing a less ambiguous name", name, trait_name));
++                        }
++                    }
++                }
++
++                if let Some((ref conv, self_kinds)) = &CONVENTIONS
++                    .iter()
++                    .find(|(ref conv, _)| conv.check(&name))
++                {
++                    if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
++                        let lint = if item.vis.node.is_pub() {
++                            WRONG_PUB_SELF_CONVENTION
++                        } else {
++                            WRONG_SELF_CONVENTION
++                        };
++
++                        span_lint(
++                            cx,
++                            lint,
++                            first_arg.pat.span,
++                            &format!(
++                               "methods called `{}` usually take {}; consider choosing a less \
++                                 ambiguous name",
++                                conv,
++                                &self_kinds
++                                    .iter()
++                                    .map(|k| k.description())
++                                    .collect::<Vec<_>>()
++                                    .join(" or ")
++                            ),
++                        );
++                    }
++                }
++            }
++        }
++
++        if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
++            let ret_ty = return_ty(cx, impl_item.hir_id);
++
++            let contains_self_ty = |ty: Ty<'tcx>| {
++                ty.walk().any(|inner| match inner.unpack() {
++                    GenericArgKind::Type(inner_ty) => same_tys(cx, self_ty, inner_ty),
++
++                    GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
++                })
++            };
++
++            // walk the return type and check for Self (this does not check associated types)
++            if contains_self_ty(ret_ty) {
++                return;
++            }
++
++            // if return type is impl trait, check the associated types
++            if let ty::Opaque(def_id, _) = ret_ty.kind {
++                // one of the associated types must be Self
++                for predicate in cx.tcx.predicates_of(def_id).predicates {
++                    match predicate {
++                        (Predicate::Projection(poly_projection_predicate), _) => {
++                            let binder = poly_projection_predicate.ty();
++                            let associated_type = binder.skip_binder();
++
++                            // walk the associated type and check for Self
++                            if contains_self_ty(associated_type) {
++                                return;
++                            }
++                        },
++                        (_, _) => {},
++                    }
++                }
++            }
++
++            if name == "new" && !same_tys(cx, ret_ty, self_ty) {
++                span_lint(
++                    cx,
++                    NEW_RET_NO_SELF,
++                    impl_item.span,
++                    "methods called `new` usually return `Self`",
++                );
++            }
++        }
++    }
++}
++
++/// Checks for the `OR_FUN_CALL` lint.
++#[allow(clippy::too_many_lines)]
++fn lint_or_fun_call<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &hir::Expr<'_>,
++    method_span: Span,
++    name: &str,
++    args: &'tcx [hir::Expr<'_>],
++) {
++    // Searches an expression for method calls or function calls that aren't ctors
++    struct FunCallFinder<'a, 'tcx> {
++        cx: &'a LateContext<'a, 'tcx>,
++        found: bool,
++    }
++
++    impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> {
++        type Map = Map<'tcx>;
++
++        fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
++            let call_found = match &expr.kind {
++                // ignore enum and struct constructors
++                hir::ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr),
++                hir::ExprKind::MethodCall(..) => true,
++                _ => false,
++            };
++
++            if call_found {
++                self.found |= true;
++            }
++
++            if !self.found {
++                intravisit::walk_expr(self, expr);
++            }
++        }
++
++        fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
++            intravisit::NestedVisitorMap::None
++        }
++    }
++
++    /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
++    fn check_unwrap_or_default(
++        cx: &LateContext<'_, '_>,
++        name: &str,
++        fun: &hir::Expr<'_>,
++        self_expr: &hir::Expr<'_>,
++        arg: &hir::Expr<'_>,
++        or_has_args: bool,
++        span: Span,
++    ) -> bool {
++        if_chain! {
++            if !or_has_args;
++            if name == "unwrap_or";
++            if let hir::ExprKind::Path(ref qpath) = fun.kind;
++            let path = &*last_path_segment(qpath).ident.as_str();
++            if ["default", "new"].contains(&path);
++            let arg_ty = cx.tables.expr_ty(arg);
++            if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT);
++            if implements_trait(cx, arg_ty, default_trait_id, &[]);
++
++            then {
++                let mut applicability = Applicability::MachineApplicable;
++                span_lint_and_sugg(
++                    cx,
++                    OR_FUN_CALL,
++                    span,
++                    &format!("use of `{}` followed by a call to `{}`", name, path),
++                    "try this",
++                    format!(
++                        "{}.unwrap_or_default()",
++                        snippet_with_applicability(cx, self_expr.span, "_", &mut applicability)
++                    ),
++                    applicability,
++                );
++
++                true
++            } else {
++                false
++            }
++        }
++    }
++
++    /// Checks for `*or(foo())`.
++    #[allow(clippy::too_many_arguments)]
++    fn check_general_case<'a, 'tcx>(
++        cx: &LateContext<'a, 'tcx>,
++        name: &str,
++        method_span: Span,
++        fun_span: Span,
++        self_expr: &hir::Expr<'_>,
++        arg: &'tcx hir::Expr<'_>,
++        or_has_args: bool,
++        span: Span,
++    ) {
++        // (path, fn_has_argument, methods, suffix)
++        let know_types: &[(&[_], _, &[_], _)] = &[
++            (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
++            (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
++            (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
++            (&paths::RESULT, true, &["or", "unwrap_or"], "else"),
++        ];
++
++        if_chain! {
++            if know_types.iter().any(|k| k.2.contains(&name));
++
++            let mut finder = FunCallFinder { cx: &cx, found: false };
++            if { finder.visit_expr(&arg); finder.found };
++            if !contains_return(&arg);
++
++            let self_ty = cx.tables.expr_ty(self_expr);
++
++            if let Some(&(_, fn_has_arguments, poss, suffix)) =
++                   know_types.iter().find(|&&i| match_type(cx, self_ty, i.0));
++
++            if poss.contains(&name);
++
++            then {
++                let sugg: Cow<'_, _> = match (fn_has_arguments, !or_has_args) {
++                    (true, _) => format!("|_| {}", snippet_with_macro_callsite(cx, arg.span, "..")).into(),
++                    (false, false) => format!("|| {}", snippet_with_macro_callsite(cx, arg.span, "..")).into(),
++                    (false, true) => snippet_with_macro_callsite(cx, fun_span, ".."),
++                };
++                let span_replace_word = method_span.with_hi(span.hi());
++                span_lint_and_sugg(
++                    cx,
++                    OR_FUN_CALL,
++                    span_replace_word,
++                    &format!("use of `{}` followed by a function call", name),
++                    "try this",
++                    format!("{}_{}({})", name, suffix, sugg),
++                    Applicability::HasPlaceholders,
++                );
++            }
++        }
++    }
++
++    if args.len() == 2 {
++        match args[1].kind {
++            hir::ExprKind::Call(ref fun, ref or_args) => {
++                let or_has_args = !or_args.is_empty();
++                if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) {
++                    check_general_case(
++                        cx,
++                        name,
++                        method_span,
++                        fun.span,
++                        &args[0],
++                        &args[1],
++                        or_has_args,
++                        expr.span,
++                    );
++                }
++            },
++            hir::ExprKind::MethodCall(_, span, ref or_args) => check_general_case(
++                cx,
++                name,
++                method_span,
++                span,
++                &args[0],
++                &args[1],
++                !or_args.is_empty(),
++                expr.span,
++            ),
++            _ => {},
++        }
++    }
++}
++
++/// Checks for the `EXPECT_FUN_CALL` lint.
++#[allow(clippy::too_many_lines)]
++fn lint_expect_fun_call(
++    cx: &LateContext<'_, '_>,
++    expr: &hir::Expr<'_>,
++    method_span: Span,
++    name: &str,
++    args: &[hir::Expr<'_>],
++) {
++    // Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or
++    // `&str`
++    fn get_arg_root<'a>(cx: &LateContext<'_, '_>, arg: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
++        let mut arg_root = arg;
++        loop {
++            arg_root = match &arg_root.kind {
++                hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr,
++                hir::ExprKind::MethodCall(method_name, _, call_args) => {
++                    if call_args.len() == 1
++                        && (method_name.ident.name == sym!(as_str) || method_name.ident.name == sym!(as_ref))
++                        && {
++                            let arg_type = cx.tables.expr_ty(&call_args[0]);
++                            let base_type = walk_ptrs_ty(arg_type);
++                            base_type.kind == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type))
++                        }
++                    {
++                        &call_args[0]
++                    } else {
++                        break;
++                    }
++                },
++                _ => break,
++            };
++        }
++        arg_root
++    }
++
++    // Only `&'static str` or `String` can be used directly in the `panic!`. Other types should be
++    // converted to string.
++    fn requires_to_string(cx: &LateContext<'_, '_>, arg: &hir::Expr<'_>) -> bool {
++        let arg_ty = cx.tables.expr_ty(arg);
++        if is_type_diagnostic_item(cx, arg_ty, sym!(string_type)) {
++            return false;
++        }
++        if let ty::Ref(_, ty, ..) = arg_ty.kind {
++            if ty.kind == ty::Str && can_be_static_str(cx, arg) {
++                return false;
++            }
++        };
++        true
++    }
++
++    // Check if an expression could have type `&'static str`, knowing that it
++    // has type `&str` for some lifetime.
++    fn can_be_static_str(cx: &LateContext<'_, '_>, arg: &hir::Expr<'_>) -> bool {
++        match arg.kind {
++            hir::ExprKind::Lit(_) => true,
++            hir::ExprKind::Call(fun, _) => {
++                if let hir::ExprKind::Path(ref p) = fun.kind {
++                    match cx.tables.qpath_res(p, fun.hir_id) {
++                        hir::def::Res::Def(hir::def::DefKind::Fn, def_id)
++                        | hir::def::Res::Def(hir::def::DefKind::AssocFn, def_id) => matches!(
++                            cx.tcx.fn_sig(def_id).output().skip_binder().kind,
++                            ty::Ref(ty::ReStatic, ..)
++                        ),
++                        _ => false,
++                    }
++                } else {
++                    false
++                }
++            },
++            hir::ExprKind::MethodCall(..) => cx.tables.type_dependent_def_id(arg.hir_id).map_or(false, |method_id| {
++                matches!(
++                    cx.tcx.fn_sig(method_id).output().skip_binder().kind,
++                    ty::Ref(ty::ReStatic, ..)
++                )
++            }),
++            hir::ExprKind::Path(ref p) => match cx.tables.qpath_res(p, arg.hir_id) {
++                hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _) => true,
++                _ => false,
++            },
++            _ => false,
++        }
++    }
++
++    fn generate_format_arg_snippet(
++        cx: &LateContext<'_, '_>,
++        a: &hir::Expr<'_>,
++        applicability: &mut Applicability,
++    ) -> Vec<String> {
++        if_chain! {
++            if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref format_arg) = a.kind;
++            if let hir::ExprKind::Match(ref format_arg_expr, _, _) = format_arg.kind;
++            if let hir::ExprKind::Tup(ref format_arg_expr_tup) = format_arg_expr.kind;
++
++            then {
++                format_arg_expr_tup
++                    .iter()
++                    .map(|a| snippet_with_applicability(cx, a.span, "..", applicability).into_owned())
++                    .collect()
++            } else {
++                unreachable!()
++            }
++        }
++    }
++
++    fn is_call(node: &hir::ExprKind<'_>) -> bool {
++        match node {
++            hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => {
++                is_call(&expr.kind)
++            },
++            hir::ExprKind::Call(..)
++            | hir::ExprKind::MethodCall(..)
++            // These variants are debatable or require further examination
++            | hir::ExprKind::Match(..)
++            | hir::ExprKind::Block{ .. } => true,
++            _ => false,
++        }
++    }
++
++    if args.len() != 2 || name != "expect" || !is_call(&args[1].kind) {
++        return;
++    }
++
++    let receiver_type = cx.tables.expr_ty_adjusted(&args[0]);
++    let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym!(option_type)) {
++        "||"
++    } else if is_type_diagnostic_item(cx, receiver_type, sym!(result_type)) {
++        "|_|"
++    } else {
++        return;
++    };
++
++    let arg_root = get_arg_root(cx, &args[1]);
++
++    let span_replace_word = method_span.with_hi(expr.span.hi());
++
++    let mut applicability = Applicability::MachineApplicable;
++
++    //Special handling for `format!` as arg_root
++    if_chain! {
++        if let hir::ExprKind::Block(block, None) = &arg_root.kind;
++        if block.stmts.len() == 1;
++        if let hir::StmtKind::Local(local) = &block.stmts[0].kind;
++        if let Some(arg_root) = &local.init;
++        if let hir::ExprKind::Call(ref inner_fun, ref inner_args) = arg_root.kind;
++        if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1;
++        if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind;
++        then {
++            let fmt_spec = &format_args[0];
++            let fmt_args = &format_args[1];
++
++            let mut args = vec![snippet(cx, fmt_spec.span, "..").into_owned()];
++
++            args.extend(generate_format_arg_snippet(cx, fmt_args, &mut applicability));
++
++            let sugg = args.join(", ");
++
++            span_lint_and_sugg(
++                cx,
++                EXPECT_FUN_CALL,
++                span_replace_word,
++                &format!("use of `{}` followed by a function call", name),
++                "try this",
++                format!("unwrap_or_else({} panic!({}))", closure_args, sugg),
++                applicability,
++            );
++
++            return;
++        }
++    }
++
++    let mut arg_root_snippet: Cow<'_, _> = snippet_with_applicability(cx, arg_root.span, "..", &mut applicability);
++    if requires_to_string(cx, arg_root) {
++        arg_root_snippet.to_mut().push_str(".to_string()");
++    }
++
++    span_lint_and_sugg(
++        cx,
++        EXPECT_FUN_CALL,
++        span_replace_word,
++        &format!("use of `{}` followed by a function call", name),
++        "try this",
++        format!("unwrap_or_else({} {{ panic!({}) }})", closure_args, arg_root_snippet),
++        applicability,
++    );
++}
++
++/// Checks for the `CLONE_ON_COPY` lint.
++fn lint_clone_on_copy(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'_>) {
++    let ty = cx.tables.expr_ty(expr);
++    if let ty::Ref(_, inner, _) = arg_ty.kind {
++        if let ty::Ref(_, innermost, _) = inner.kind {
++            span_lint_and_then(
++                cx,
++                CLONE_DOUBLE_REF,
++                expr.span,
++                "using `clone` on a double-reference; \
++                 this will copy the reference instead of cloning the inner type",
++                |diag| {
++                    if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
++                        let mut ty = innermost;
++                        let mut n = 0;
++                        while let ty::Ref(_, inner, _) = ty.kind {
++                            ty = inner;
++                            n += 1;
++                        }
++                        let refs: String = iter::repeat('&').take(n + 1).collect();
++                        let derefs: String = iter::repeat('*').take(n).collect();
++                        let explicit = format!("<{}{}>::clone({})", refs, ty, snip);
++                        diag.span_suggestion(
++                            expr.span,
++                            "try dereferencing it",
++                            format!("{}({}{}).clone()", refs, derefs, snip.deref()),
++                            Applicability::MaybeIncorrect,
++                        );
++                        diag.span_suggestion(
++                            expr.span,
++                            "or try being explicit if you are sure, that you want to clone a reference",
++                            explicit,
++                            Applicability::MaybeIncorrect,
++                        );
++                    }
++                },
++            );
++            return; // don't report clone_on_copy
++        }
++    }
++
++    if is_copy(cx, ty) {
++        let snip;
++        if let Some(snippet) = sugg::Sugg::hir_opt(cx, arg) {
++            let parent = cx.tcx.hir().get_parent_node(expr.hir_id);
++            match &cx.tcx.hir().get(parent) {
++                hir::Node::Expr(parent) => match parent.kind {
++                    // &*x is a nop, &x.clone() is not
++                    hir::ExprKind::AddrOf(..) => return,
++                    // (*x).func() is useless, x.clone().func() can work in case func borrows mutably
++                    hir::ExprKind::MethodCall(_, _, parent_args) if expr.hir_id == parent_args[0].hir_id => return,
++
++                    _ => {},
++                },
++                hir::Node::Stmt(stmt) => {
++                    if let hir::StmtKind::Local(ref loc) = stmt.kind {
++                        if let hir::PatKind::Ref(..) = loc.pat.kind {
++                            // let ref y = *x borrows x, let ref y = x.clone() does not
++                            return;
++                        }
++                    }
++                },
++                _ => {},
++            }
++
++            // x.clone() might have dereferenced x, possibly through Deref impls
++            if cx.tables.expr_ty(arg) == ty {
++                snip = Some(("try removing the `clone` call", format!("{}", snippet)));
++            } else {
++                let deref_count = cx
++                    .tables
++                    .expr_adjustments(arg)
++                    .iter()
++                    .filter(|adj| {
++                        if let ty::adjustment::Adjust::Deref(_) = adj.kind {
++                            true
++                        } else {
++                            false
++                        }
++                    })
++                    .count();
++                let derefs: String = iter::repeat('*').take(deref_count).collect();
++                snip = Some(("try dereferencing it", format!("{}{}", derefs, snippet)));
++            }
++        } else {
++            snip = None;
++        }
++        span_lint_and_then(cx, CLONE_ON_COPY, expr.span, "using `clone` on a `Copy` type", |diag| {
++            if let Some((text, snip)) = snip {
++                diag.span_suggestion(expr.span, text, snip, Applicability::Unspecified);
++            }
++        });
++    }
++}
++
++fn lint_clone_on_ref_ptr(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
++    let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(arg));
++
++    if let ty::Adt(_, subst) = obj_ty.kind {
++        let caller_type = if is_type_diagnostic_item(cx, obj_ty, sym::Rc) {
++            "Rc"
++        } else if is_type_diagnostic_item(cx, obj_ty, sym::Arc) {
++            "Arc"
++        } else if match_type(cx, obj_ty, &paths::WEAK_RC) || match_type(cx, obj_ty, &paths::WEAK_ARC) {
++            "Weak"
++        } else {
++            return;
++        };
++
++        span_lint_and_sugg(
++            cx,
++            CLONE_ON_REF_PTR,
++            expr.span,
++            "using `.clone()` on a ref-counted pointer",
++            "try this",
++            format!(
++                "{}::<{}>::clone(&{})",
++                caller_type,
++                subst.type_at(0),
++                snippet(cx, arg.span, "_")
++            ),
++            Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak
++        );
++    }
++}
++
++fn lint_string_extend(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
++    let arg = &args[1];
++    if let Some(arglists) = method_chain_args(arg, &["chars"]) {
++        let target = &arglists[0][0];
++        let self_ty = walk_ptrs_ty(cx.tables.expr_ty(target));
++        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,
++        );
++    }
++}
++
++fn lint_extend(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
++    let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&args[0]));
++    if is_type_diagnostic_item(cx, obj_ty, sym!(string_type)) {
++        lint_string_extend(cx, expr, args);
++    }
++}
++
++fn lint_cstring_as_ptr(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, source: &hir::Expr<'_>, unwrap: &hir::Expr<'_>) {
++    if_chain! {
++        let source_type = cx.tables.expr_ty(source);
++        if let ty::Adt(def, substs) = source_type.kind;
++        if cx.tcx.is_diagnostic_item(sym!(result_type), def.did);
++        if match_type(cx, substs.type_at(0), &paths::CSTRING);
++        then {
++            span_lint_and_then(
++                cx,
++                TEMPORARY_CSTRING_AS_PTR,
++                expr.span,
++                "you are getting the inner pointer of a temporary `CString`",
++                |diag| {
++                    diag.note("that pointer will be invalid outside this expression");
++                    diag.span_help(unwrap.span, "assign the `CString` to a variable to extend its lifetime");
++                });
++        }
++    }
++}
++
++fn lint_iter_cloned_collect<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &hir::Expr<'_>,
++    iter_args: &'tcx [hir::Expr<'_>],
++) {
++    if_chain! {
++        if is_type_diagnostic_item(cx, cx.tables.expr_ty(expr), sym!(vec_type));
++        if let Some(slice) = derefs_to_slice(cx, &iter_args[0], cx.tables.expr_ty(&iter_args[0]));
++        if let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite());
++
++        then {
++            span_lint_and_sugg(
++                cx,
++                ITER_CLONED_COLLECT,
++                to_replace,
++                "called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \
++                 more readable",
++                "try",
++                ".to_vec()".to_string(),
++                Applicability::MachineApplicable,
++            );
++        }
++    }
++}
++
++fn lint_unnecessary_fold(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, fold_args: &[hir::Expr<'_>], fold_span: Span) {
++    fn check_fold_with_op(
++        cx: &LateContext<'_, '_>,
++        expr: &hir::Expr<'_>,
++        fold_args: &[hir::Expr<'_>],
++        fold_span: Span,
++        op: hir::BinOpKind,
++        replacement_method_name: &str,
++        replacement_has_args: bool,
++    ) {
++        if_chain! {
++            // Extract the body of the closure passed to fold
++            if let hir::ExprKind::Closure(_, _, body_id, _, _) = fold_args[2].kind;
++            let closure_body = cx.tcx.hir().body(body_id);
++            let closure_expr = remove_blocks(&closure_body.value);
++
++            // Check if the closure body is of the form `acc <op> some_expr(x)`
++            if let hir::ExprKind::Binary(ref bin_op, ref left_expr, ref right_expr) = closure_expr.kind;
++            if bin_op.node == op;
++
++            // Extract the names of the two arguments to the closure
++            if let Some(first_arg_ident) = get_arg_name(&closure_body.params[0].pat);
++            if let Some(second_arg_ident) = get_arg_name(&closure_body.params[1].pat);
++
++            if match_var(&*left_expr, first_arg_ident);
++            if replacement_has_args || match_var(&*right_expr, second_arg_ident);
++
++            then {
++                let mut applicability = Applicability::MachineApplicable;
++                let sugg = if replacement_has_args {
++                    format!(
++                        "{replacement}(|{s}| {r})",
++                        replacement = replacement_method_name,
++                        s = second_arg_ident,
++                        r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability),
++                    )
++                } else {
++                    format!(
++                        "{replacement}()",
++                        replacement = replacement_method_name,
++                    )
++                };
++
++                span_lint_and_sugg(
++                    cx,
++                    UNNECESSARY_FOLD,
++                    fold_span.with_hi(expr.span.hi()),
++                    // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f)
++                    "this `.fold` can be written more succinctly using another method",
++                    "try",
++                    sugg,
++                    applicability,
++                );
++            }
++        }
++    }
++
++    // Check that this is a call to Iterator::fold rather than just some function called fold
++    if !match_trait_method(cx, expr, &paths::ITERATOR) {
++        return;
++    }
++
++    assert!(
++        fold_args.len() == 3,
++        "Expected fold_args to have three entries - the receiver, the initial value and the closure"
++    );
++
++    // Check if the first argument to .fold is a suitable literal
++    if let hir::ExprKind::Lit(ref lit) = fold_args[1].kind {
++        match lit.node {
++            ast::LitKind::Bool(false) => {
++                check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Or, "any", true)
++            },
++            ast::LitKind::Bool(true) => {
++                check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::And, "all", true)
++            },
++            ast::LitKind::Int(0, _) => {
++                check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Add, "sum", false)
++            },
++            ast::LitKind::Int(1, _) => {
++                check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Mul, "product", false)
++            },
++            _ => (),
++        }
++    }
++}
++
++fn lint_step_by<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr<'_>, args: &'tcx [hir::Expr<'_>]) {
++    if match_trait_method(cx, expr, &paths::ITERATOR) {
++        if let Some((Constant::Int(0), _)) = constant(cx, cx.tables, &args[1]) {
++            span_lint(
++                cx,
++                ITERATOR_STEP_BY_ZERO,
++                expr.span,
++                "Iterator::step_by(0) will panic at runtime",
++            );
++        }
++    }
++}
++
++fn lint_iter_nth<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &hir::Expr<'_>,
++    nth_and_iter_args: &[&'tcx [hir::Expr<'tcx>]],
++    is_mut: bool,
++) {
++    let iter_args = nth_and_iter_args[1];
++    let mut_str = if is_mut { "_mut" } else { "" };
++    let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.tables.expr_ty(&iter_args[0])).is_some() {
++        "slice"
++    } else if is_type_diagnostic_item(cx, cx.tables.expr_ty(&iter_args[0]), sym!(vec_type)) {
++        "Vec"
++    } else if is_type_diagnostic_item(cx, cx.tables.expr_ty(&iter_args[0]), sym!(vecdeque_type)) {
++        "VecDeque"
++    } else {
++        let nth_args = nth_and_iter_args[0];
++        lint_iter_nth_zero(cx, expr, &nth_args);
++        return; // caller is not a type that we want to lint
++    };
++
++    span_lint_and_help(
++        cx,
++        ITER_NTH,
++        expr.span,
++        &format!("called `.iter{0}().nth()` on a {1}", mut_str, caller_type),
++        None,
++        &format!("calling `.get{}()` is both faster and more readable", mut_str),
++    );
++}
++
++fn lint_iter_nth_zero<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr<'_>, nth_args: &'tcx [hir::Expr<'_>]) {
++    if_chain! {
++        if match_trait_method(cx, expr, &paths::ITERATOR);
++        if let Some((Constant::Int(0), _)) = constant(cx, cx.tables, &nth_args[1]);
++        then {
++            let mut applicability = Applicability::MachineApplicable;
++            span_lint_and_sugg(
++                cx,
++                ITER_NTH_ZERO,
++                expr.span,
++                "called `.nth(0)` on a `std::iter::Iterator`",
++                "try calling",
++                format!("{}.next()", snippet_with_applicability(cx, nth_args[0].span, "..", &mut applicability)),
++                applicability,
++            );
++        }
++    }
++}
++
++fn lint_get_unwrap<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &hir::Expr<'_>,
++    get_args: &'tcx [hir::Expr<'_>],
++    is_mut: bool,
++) {
++    // Note: we don't want to lint `get_mut().unwrap` for `HashMap` or `BTreeMap`,
++    // because they do not implement `IndexMut`
++    let mut applicability = Applicability::MachineApplicable;
++    let expr_ty = cx.tables.expr_ty(&get_args[0]);
++    let get_args_str = if get_args.len() > 1 {
++        snippet_with_applicability(cx, get_args[1].span, "_", &mut applicability)
++    } else {
++        return; // not linting on a .get().unwrap() chain or variant
++    };
++    let mut needs_ref;
++    let caller_type = if derefs_to_slice(cx, &get_args[0], expr_ty).is_some() {
++        needs_ref = get_args_str.parse::<usize>().is_ok();
++        "slice"
++    } else if is_type_diagnostic_item(cx, expr_ty, sym!(vec_type)) {
++        needs_ref = get_args_str.parse::<usize>().is_ok();
++        "Vec"
++    } else if is_type_diagnostic_item(cx, expr_ty, sym!(vecdeque_type)) {
++        needs_ref = get_args_str.parse::<usize>().is_ok();
++        "VecDeque"
++    } else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym!(hashmap_type)) {
++        needs_ref = true;
++        "HashMap"
++    } else if !is_mut && match_type(cx, expr_ty, &paths::BTREEMAP) {
++        needs_ref = true;
++        "BTreeMap"
++    } else {
++        return; // caller is not a type that we want to lint
++    };
++
++    let mut span = expr.span;
++
++    // Handle the case where the result is immediately dereferenced
++    // by not requiring ref and pulling the dereference into the
++    // suggestion.
++    if_chain! {
++        if needs_ref;
++        if let Some(parent) = get_parent_expr(cx, expr);
++        if let hir::ExprKind::Unary(hir::UnOp::UnDeref, _) = parent.kind;
++        then {
++            needs_ref = false;
++            span = parent.span;
++        }
++    }
++
++    let mut_str = if is_mut { "_mut" } else { "" };
++    let borrow_str = if !needs_ref {
++        ""
++    } else if is_mut {
++        "&mut "
++    } else {
++        "&"
++    };
++
++    span_lint_and_sugg(
++        cx,
++        GET_UNWRAP,
++        span,
++        &format!(
++            "called `.get{0}().unwrap()` on a {1}. Using `[]` is more clear and more concise",
++            mut_str, caller_type
++        ),
++        "try this",
++        format!(
++            "{}{}[{}]",
++            borrow_str,
++            snippet_with_applicability(cx, get_args[0].span, "_", &mut applicability),
++            get_args_str
++        ),
++        applicability,
++    );
++}
++
++fn lint_iter_skip_next(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) {
++    // lint if caller of skip is an Iterator
++    if match_trait_method(cx, expr, &paths::ITERATOR) {
++        span_lint_and_help(
++            cx,
++            ITER_SKIP_NEXT,
++            expr.span,
++            "called `skip(x).next()` on an iterator",
++            None,
++            "this is more succinctly expressed by calling `nth(x)`",
++        );
++    }
++}
++
++fn derefs_to_slice<'a, 'tcx>(
++    cx: &LateContext<'a, '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) => {
++                if let Some(size) = size.try_eval_usize(cx.tcx, cx.param_env) {
++                    size < 32
++                } else {
++                    false
++                }
++            },
++            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.tables.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,
++        }
++    }
++}
++
++/// lint use of `unwrap()` for `Option`s and `Result`s
++fn lint_unwrap(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::Expr<'_>]) {
++    let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&unwrap_args[0]));
++
++    let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) {
++        Some((OPTION_UNWRAP_USED, "an Option", "None"))
++    } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) {
++        Some((RESULT_UNWRAP_USED, "a Result", "Err"))
++    } else {
++        None
++    };
++
++    if let Some((lint, kind, none_value)) = mess {
++        span_lint_and_help(
++            cx,
++            lint,
++            expr.span,
++            &format!("used `unwrap()` on `{}` value", kind,),
++            None,
++            &format!(
++                "if you don't want to handle the `{}` case gracefully, consider \
++                 using `expect()` to provide a better panic message",
++                none_value,
++            ),
++        );
++    }
++}
++
++/// lint use of `expect()` for `Option`s and `Result`s
++fn lint_expect(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, expect_args: &[hir::Expr<'_>]) {
++    let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&expect_args[0]));
++
++    let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) {
++        Some((OPTION_EXPECT_USED, "an Option", "None"))
++    } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) {
++        Some((RESULT_EXPECT_USED, "a Result", "Err"))
++    } else {
++        None
++    };
++
++    if let Some((lint, kind, none_value)) = mess {
++        span_lint_and_help(
++            cx,
++            lint,
++            expr.span,
++            &format!("used `expect()` on `{}` value", kind,),
++            None,
++            &format!("if this value is an `{}`, it will panic", none_value,),
++        );
++    }
++}
++
++/// lint use of `ok().expect()` for `Result`s
++fn lint_ok_expect(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, ok_args: &[hir::Expr<'_>]) {
++    if_chain! {
++        // lint if the caller of `ok()` is a `Result`
++        if is_type_diagnostic_item(cx, cx.tables.expr_ty(&ok_args[0]), sym!(result_type));
++        let result_type = cx.tables.expr_ty(&ok_args[0]);
++        if let Some(error_type) = get_error_type(cx, result_type);
++        if has_debug_impl(error_type, cx);
++
++        then {
++            span_lint_and_help(
++                cx,
++                OK_EXPECT,
++                expr.span,
++                "called `ok().expect()` on a `Result` value",
++                None,
++                "you can call `expect()` directly on the `Result`",
++            );
++        }
++    }
++}
++
++/// lint use of `map().flatten()` for `Iterators` and 'Options'
++fn lint_map_flatten<'a, 'tcx>(cx: &LateContext<'a, '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) {
++        let msg = "called `map(..).flatten()` on an `Iterator`. \
++                   This is more succinctly expressed by calling `.flat_map(..)`";
++        let self_snippet = snippet(cx, map_args[0].span, "..");
++        let func_snippet = snippet(cx, map_args[1].span, "..");
++        let hint = format!("{0}.flat_map({1})", self_snippet, func_snippet);
++        span_lint_and_sugg(
++            cx,
++            MAP_FLATTEN,
++            expr.span,
++            msg,
++            "try using `flat_map` instead",
++            hint,
++            Applicability::MachineApplicable,
++        );
++    }
++
++    // lint if caller of `.map().flatten()` is an Option
++    if is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_args[0]), sym!(option_type)) {
++        let msg = "called `map(..).flatten()` on an `Option`. \
++                    This is more succinctly expressed by calling `.and_then(..)`";
++        let self_snippet = snippet(cx, map_args[0].span, "..");
++        let func_snippet = snippet(cx, map_args[1].span, "..");
++        let hint = format!("{0}.and_then({1})", self_snippet, func_snippet);
++        span_lint_and_sugg(
++            cx,
++            MAP_FLATTEN,
++            expr.span,
++            msg,
++            "try using `and_then` instead",
++            hint,
++            Applicability::MachineApplicable,
++        );
++    }
++}
++
++/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
++fn lint_map_unwrap_or_else<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &'tcx hir::Expr<'_>,
++    map_args: &'tcx [hir::Expr<'_>],
++    unwrap_args: &'tcx [hir::Expr<'_>],
++) {
++    // lint if the caller of `map()` is an `Option`
++    let is_option = is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_args[0]), sym!(option_type));
++    let is_result = is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_args[0]), sym!(result_type));
++
++    if is_option || is_result {
++        // Don't make a suggestion that may fail to compile due to mutably borrowing
++        // the same variable twice.
++        let map_mutated_vars = mutated_variables(&map_args[0], cx);
++        let unwrap_mutated_vars = mutated_variables(&unwrap_args[1], cx);
++        if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) {
++            if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() {
++                return;
++            }
++        } else {
++            return;
++        }
++
++        // lint message
++        let msg = if is_option {
++            "called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling \
++             `map_or_else(g, f)` instead"
++        } else {
++            "called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling \
++             `.map_or_else(g, f)` instead"
++        };
++        // get snippets for args to map() and unwrap_or_else()
++        let map_snippet = snippet(cx, map_args[1].span, "..");
++        let unwrap_snippet = snippet(cx, unwrap_args[1].span, "..");
++        // lint, with note if neither arg is > 1 line and both map() and
++        // unwrap_or_else() have the same span
++        let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
++        let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt();
++        if same_span && !multiline {
++            span_lint_and_note(
++                cx,
++                if is_option {
++                    OPTION_MAP_UNWRAP_OR_ELSE
++                } else {
++                    RESULT_MAP_UNWRAP_OR_ELSE
++                },
++                expr.span,
++                msg,
++                None,
++                &format!(
++                    "replace `map({0}).unwrap_or_else({1})` with `map_or_else({1}, {0})`",
++                    map_snippet, unwrap_snippet,
++                ),
++            );
++        } else if same_span && multiline {
++            span_lint(
++                cx,
++                if is_option {
++                    OPTION_MAP_UNWRAP_OR_ELSE
++                } else {
++                    RESULT_MAP_UNWRAP_OR_ELSE
++                },
++                expr.span,
++                msg,
++            );
++        };
++    }
++}
++
++/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
++fn lint_map_or_none<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &'tcx hir::Expr<'_>,
++    map_or_args: &'tcx [hir::Expr<'_>],
++) {
++    let is_option = is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_or_args[0]), sym!(option_type));
++    let is_result = is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_or_args[0]), sym!(result_type));
++
++    // There are two variants of this `map_or` lint:
++    // (1) using `map_or` as an adapter from `Result<T,E>` to `Option<T>`
++    // (2) using `map_or` as a combinator instead of `and_then`
++    //
++    // (For this lint) we don't care if any other type calls `map_or`
++    if !is_option && !is_result {
++        return;
++    }
++
++    let (lint_name, msg, instead, hint) = {
++        let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = map_or_args[1].kind {
++            match_qpath(qpath, &paths::OPTION_NONE)
++        } else {
++            return;
++        };
++
++        if !default_arg_is_none {
++            // nothing to lint!
++            return;
++        }
++
++        let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_or_args[2].kind {
++            match_qpath(qpath, &paths::OPTION_SOME)
++        } else {
++            false
++        };
++
++        if is_option {
++            let self_snippet = snippet(cx, map_or_args[0].span, "..");
++            let func_snippet = snippet(cx, map_or_args[2].span, "..");
++            let msg = "called `map_or(None, f)` on an `Option` value. This can be done more directly by calling \
++                       `and_then(f)` instead";
++            (
++                OPTION_MAP_OR_NONE,
++                msg,
++                "try using `and_then` instead",
++                format!("{0}.and_then({1})", self_snippet, func_snippet),
++            )
++        } else if f_arg_is_some {
++            let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
++                       `ok()` instead";
++            let self_snippet = snippet(cx, map_or_args[0].span, "..");
++            (
++                RESULT_MAP_OR_INTO_OPTION,
++                msg,
++                "try using `ok` instead",
++                format!("{0}.ok()", self_snippet),
++            )
++        } else {
++            // nothing to lint!
++            return;
++        }
++    };
++
++    span_lint_and_sugg(
++        cx,
++        lint_name,
++        expr.span,
++        msg,
++        instead,
++        hint,
++        Applicability::MachineApplicable,
++    );
++}
++
++/// Lint use of `_.and_then(|x| Some(y))` for `Option`s
++fn lint_option_and_then_some(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
++    const LINT_MSG: &str = "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`";
++    const NO_OP_MSG: &str = "using `Option.and_then(Some)`, which is a no-op";
++
++    let ty = cx.tables.expr_ty(&args[0]);
++    if !is_type_diagnostic_item(cx, ty, sym!(option_type)) {
++        return;
++    }
++
++    match args[1].kind {
++        hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => {
++            let closure_body = cx.tcx.hir().body(body_id);
++            let closure_expr = remove_blocks(&closure_body.value);
++            if_chain! {
++                if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.kind;
++                if let hir::ExprKind::Path(ref qpath) = some_expr.kind;
++                if match_qpath(qpath, &paths::OPTION_SOME);
++                if some_args.len() == 1;
++                then {
++                    let inner_expr = &some_args[0];
++
++                    if contains_return(inner_expr) {
++                        return;
++                    }
++
++                    let some_inner_snip = if inner_expr.span.from_expansion() {
++                        snippet_with_macro_callsite(cx, inner_expr.span, "_")
++                    } else {
++                        snippet(cx, inner_expr.span, "_")
++                    };
++
++                    let closure_args_snip = snippet(cx, closure_args_span, "..");
++                    let option_snip = snippet(cx, args[0].span, "..");
++                    let note = format!("{}.map({} {})", option_snip, closure_args_snip, some_inner_snip);
++                    span_lint_and_sugg(
++                        cx,
++                        OPTION_AND_THEN_SOME,
++                        expr.span,
++                        LINT_MSG,
++                        "try this",
++                        note,
++                        Applicability::MachineApplicable,
++                    );
++                }
++            }
++        },
++        // `_.and_then(Some)` case, which is no-op.
++        hir::ExprKind::Path(ref qpath) => {
++            if match_qpath(qpath, &paths::OPTION_SOME) {
++                let option_snip = snippet(cx, args[0].span, "..");
++                let note = format!("{}", option_snip);
++                span_lint_and_sugg(
++                    cx,
++                    OPTION_AND_THEN_SOME,
++                    expr.span,
++                    NO_OP_MSG,
++                    "use the expression directly",
++                    note,
++                    Applicability::MachineApplicable,
++                );
++            }
++        },
++        _ => {},
++    }
++}
++
++/// lint use of `filter().next()` for `Iterators`
++fn lint_filter_next<'a, 'tcx>(
++    cx: &LateContext<'a, '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) {
++        let msg = "called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling \
++                   `.find(p)` instead.";
++        let filter_snippet = snippet(cx, filter_args[1].span, "..");
++        if filter_snippet.lines().count() <= 1 {
++            // add note if not multi-line
++            span_lint_and_note(
++                cx,
++                FILTER_NEXT,
++                expr.span,
++                msg,
++                None,
++                &format!("replace `filter({0}).next()` with `find({0})`", filter_snippet),
++            );
++        } else {
++            span_lint(cx, FILTER_NEXT, expr.span, msg);
++        }
++    }
++}
++
++/// lint use of `skip_while().next()` for `Iterators`
++fn lint_skip_while_next<'a, 'tcx>(
++    cx: &LateContext<'a, '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) {
++        span_lint_and_help(
++            cx,
++            SKIP_WHILE_NEXT,
++            expr.span,
++            "called `skip_while(p).next()` on an `Iterator`",
++            None,
++            "this is more succinctly expressed by calling `.find(!p)` instead",
++        );
++    }
++}
++
++/// lint use of `filter().map()` for `Iterators`
++fn lint_filter_map<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &'tcx hir::Expr<'_>,
++    _filter_args: &'tcx [hir::Expr<'_>],
++    _map_args: &'tcx [hir::Expr<'_>],
++) {
++    // lint if caller of `.filter().map()` is an Iterator
++    if match_trait_method(cx, expr, &paths::ITERATOR) {
++        let msg = "called `filter(p).map(q)` on an `Iterator`";
++        let hint = "this is more succinctly expressed by calling `.filter_map(..)` instead";
++        span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
++    }
++}
++
++/// lint use of `filter_map().next()` for `Iterators`
++fn lint_filter_map_next<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &'tcx hir::Expr<'_>,
++    filter_args: &'tcx [hir::Expr<'_>],
++) {
++    if match_trait_method(cx, expr, &paths::ITERATOR) {
++        let msg = "called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling \
++                   `.find_map(p)` instead.";
++        let filter_snippet = snippet(cx, filter_args[1].span, "..");
++        if filter_snippet.lines().count() <= 1 {
++            span_lint_and_note(
++                cx,
++                FILTER_MAP_NEXT,
++                expr.span,
++                msg,
++                None,
++                &format!("replace `filter_map({0}).next()` with `find_map({0})`", filter_snippet),
++            );
++        } else {
++            span_lint(cx, FILTER_MAP_NEXT, expr.span, msg);
++        }
++    }
++}
++
++/// lint use of `find().map()` for `Iterators`
++fn lint_find_map<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &'tcx hir::Expr<'_>,
++    _find_args: &'tcx [hir::Expr<'_>],
++    map_args: &'tcx [hir::Expr<'_>],
++) {
++    // lint if caller of `.filter().map()` is an Iterator
++    if match_trait_method(cx, &map_args[0], &paths::ITERATOR) {
++        let msg = "called `find(p).map(q)` on an `Iterator`";
++        let hint = "this is more succinctly expressed by calling `.find_map(..)` instead";
++        span_lint_and_help(cx, FIND_MAP, expr.span, msg, None, hint);
++    }
++}
++
++/// lint use of `filter_map().map()` for `Iterators`
++fn lint_filter_map_map<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &'tcx hir::Expr<'_>,
++    _filter_args: &'tcx [hir::Expr<'_>],
++    _map_args: &'tcx [hir::Expr<'_>],
++) {
++    // lint if caller of `.filter().map()` is an Iterator
++    if match_trait_method(cx, expr, &paths::ITERATOR) {
++        let msg = "called `filter_map(p).map(q)` 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);
++    }
++}
++
++/// lint use of `filter().flat_map()` for `Iterators`
++fn lint_filter_flat_map<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &'tcx hir::Expr<'_>,
++    _filter_args: &'tcx [hir::Expr<'_>],
++    _map_args: &'tcx [hir::Expr<'_>],
++) {
++    // lint if caller of `.filter().flat_map()` is an Iterator
++    if match_trait_method(cx, expr, &paths::ITERATOR) {
++        let msg = "called `filter(p).flat_map(q)` on an `Iterator`";
++        let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
++                    and filtering by returning `iter::empty()`";
++        span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
++    }
++}
++
++/// lint use of `filter_map().flat_map()` for `Iterators`
++fn lint_filter_map_flat_map<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &'tcx hir::Expr<'_>,
++    _filter_args: &'tcx [hir::Expr<'_>],
++    _map_args: &'tcx [hir::Expr<'_>],
++) {
++    // lint if caller of `.filter_map().flat_map()` is an Iterator
++    if match_trait_method(cx, expr, &paths::ITERATOR) {
++        let msg = "called `filter_map(p).flat_map(q)` on an `Iterator`";
++        let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
++                    and filtering by returning `iter::empty()`";
++        span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
++    }
++}
++
++/// lint use of `flat_map` for `Iterators` where `flatten` would be sufficient
++fn lint_flat_map_identity<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &'tcx hir::Expr<'_>,
++    flat_map_args: &'tcx [hir::Expr<'_>],
++    flat_map_span: Span,
++) {
++    if match_trait_method(cx, expr, &paths::ITERATOR) {
++        let arg_node = &flat_map_args[1].kind;
++
++        let apply_lint = |message: &str| {
++            span_lint_and_sugg(
++                cx,
++                FLAT_MAP_IDENTITY,
++                flat_map_span.with_hi(expr.span.hi()),
++                message,
++                "try",
++                "flatten()".to_string(),
++                Applicability::MachineApplicable,
++            );
++        };
++
++        if_chain! {
++            if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node;
++            let body = cx.tcx.hir().body(*body_id);
++
++            if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind;
++            if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.kind;
++
++            if path.segments.len() == 1;
++            if path.segments[0].ident.as_str() == binding_ident.as_str();
++
++            then {
++                apply_lint("called `flat_map(|x| x)` on an `Iterator`");
++            }
++        }
++
++        if_chain! {
++            if let hir::ExprKind::Path(ref qpath) = arg_node;
++
++            if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY);
++
++            then {
++                apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`");
++            }
++        }
++    }
++}
++
++/// lint searching an Iterator followed by `is_some()`
++fn lint_search_is_some<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &'tcx hir::Expr<'_>,
++    search_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) {
++        let msg = format!(
++            "called `is_some()` after searching an `Iterator` with {}. This is more succinctly \
++             expressed by calling `any()`.",
++            search_method
++        );
++        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()`
++            // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()`
++            let any_search_snippet = if_chain! {
++                if search_method == "find";
++                if let hir::ExprKind::Closure(_, _, body_id, ..) = search_args[1].kind;
++                let closure_body = cx.tcx.hir().body(body_id);
++                if let Some(closure_arg) = closure_body.params.get(0);
++                then {
++                    if let hir::PatKind::Ref(..) = closure_arg.pat.kind {
++                        Some(search_snippet.replacen('&', "", 1))
++                    } else if let Some(name) = get_arg_name(&closure_arg.pat) {
++                        Some(search_snippet.replace(&format!("*{}", name), &name.as_str()))
++                    } else {
++                        None
++                    }
++                } else {
++                    None
++                }
++            };
++            // add note if not multi-line
++            span_lint_and_sugg(
++                cx,
++                SEARCH_IS_SOME,
++                method_span.with_hi(expr.span.hi()),
++                &msg,
++                "try this",
++                format!(
++                    "any({})",
++                    any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
++                ),
++                Applicability::MachineApplicable,
++            );
++        } else {
++            span_lint(cx, SEARCH_IS_SOME, expr.span, &msg);
++        }
++    }
++}
++
++/// Used for `lint_binary_expr_with_method_call`.
++#[derive(Copy, Clone)]
++struct BinaryExprInfo<'a> {
++    expr: &'a hir::Expr<'a>,
++    chain: &'a hir::Expr<'a>,
++    other: &'a hir::Expr<'a>,
++    eq: bool,
++}
++
++/// 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) => {
++            if !$func($cx, $info) {
++                ::std::mem::swap(&mut $info.chain, &mut $info.other);
++                if $func($cx, $info) {
++                    return;
++                }
++            }
++        };
++    }
++
++    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 = walk_ptrs_ty(cx.tables.expr_ty_adjusted(&args[0][0]));
++
++            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<'a, 'tcx>(cx: &LateContext<'a, '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<'a, 'tcx>(cx: &LateContext<'a, '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<'a, 'tcx>(
++    cx: &LateContext<'a, '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<'a, 'tcx>(cx: &LateContext<'a, '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<'a, 'tcx>(cx: &LateContext<'a, '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")
++    }
++}
++
++/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
++fn lint_single_char_pattern<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    _expr: &'tcx hir::Expr<'_>,
++    arg: &'tcx hir::Expr<'_>,
++) {
++    if_chain! {
++        if let hir::ExprKind::Lit(lit) = &arg.kind;
++        if let ast::LitKind::Str(r, style) = lit.node;
++        if r.as_str().len() == 1;
++        then {
++            let mut applicability = Applicability::MachineApplicable;
++            let snip = snippet_with_applicability(cx, arg.span, "..", &mut 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 });
++            span_lint_and_sugg(
++                cx,
++                SINGLE_CHAR_PATTERN,
++                arg.span,
++                "single-character string constant used as pattern",
++                "try using a `char` instead",
++                hint,
++                applicability,
++            );
++        }
++    }
++}
++
++/// Checks for the `USELESS_ASREF` lint.
++fn lint_asref(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) {
++    // when we get here, we've already checked that the call name is "as_ref" or "as_mut"
++    // check if the call is to the actual `AsRef` or `AsMut` trait
++    if match_trait_method(cx, expr, &paths::ASREF_TRAIT) || match_trait_method(cx, expr, &paths::ASMUT_TRAIT) {
++        // check if the type after `as_ref` or `as_mut` is the same as before
++        let recvr = &as_ref_args[0];
++        let rcv_ty = cx.tables.expr_ty(recvr);
++        let res_ty = cx.tables.expr_ty(expr);
++        let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty);
++        let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty);
++        if base_rcv_ty == base_res_ty && rcv_depth >= res_depth {
++            // allow the `as_ref` or `as_mut` if it is followed by another method call
++            if_chain! {
++                if let Some(parent) = get_parent_expr(cx, expr);
++                if let hir::ExprKind::MethodCall(_, ref span, _) = parent.kind;
++                if span != &expr.span;
++                then {
++                    return;
++                }
++            }
++
++            let mut applicability = Applicability::MachineApplicable;
++            span_lint_and_sugg(
++                cx,
++                USELESS_ASREF,
++                expr.span,
++                &format!("this call to `{}` does nothing", call_name),
++                "try this",
++                snippet_with_applicability(cx, recvr.span, "_", &mut applicability).to_string(),
++                applicability,
++            );
++        }
++    }
++}
++
++fn ty_has_iter_method(cx: &LateContext<'_, '_>, self_ref_ty: Ty<'_>) -> Option<(&'static str, &'static str)> {
++    has_iter_method(cx, self_ref_ty).map(|ty_name| {
++        let mutbl = match self_ref_ty.kind {
++            ty::Ref(_, _, mutbl) => mutbl,
++            _ => unreachable!(),
++        };
++        let method_name = match mutbl {
++            hir::Mutability::Not => "iter",
++            hir::Mutability::Mut => "iter_mut",
++        };
++        (ty_name, method_name)
++    })
++}
++
++fn lint_into_iter(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 move the `{}`",
++                method_name, kind,
++            ),
++            "call directly",
++            method_name.to_string(),
++            Applicability::MachineApplicable,
++        );
++    }
++}
++
++/// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter)
++fn lint_maybe_uninit(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, outer: &hir::Expr<'_>) {
++    if_chain! {
++        if let hir::ExprKind::Call(ref callee, ref args) = expr.kind;
++        if args.is_empty();
++        if let hir::ExprKind::Path(ref path) = callee.kind;
++        if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT);
++        if !is_maybe_uninit_ty_valid(cx, cx.tables.expr_ty_adjusted(outer));
++        then {
++            span_lint(
++                cx,
++                UNINIT_ASSUMED_INIT,
++                outer.span,
++                "this call for this type may be undefined behavior"
++            );
++        }
++    }
++}
++
++fn is_maybe_uninit_ty_valid(cx: &LateContext<'_, '_>, ty: Ty<'_>) -> bool {
++    match ty.kind {
++        ty::Array(ref component, _) => is_maybe_uninit_ty_valid(cx, component),
++        ty::Tuple(ref types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)),
++        ty::Adt(ref adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT),
++        _ => false,
++    }
++}
++
++fn lint_suspicious_map(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`",
++    );
++}
++
++/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
++fn lint_option_as_ref_deref<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &hir::Expr<'_>,
++    as_ref_args: &[hir::Expr<'_>],
++    map_args: &[hir::Expr<'_>],
++    is_mut: bool,
++) {
++    let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not);
++
++    let option_ty = cx.tables.expr_ty(&as_ref_args[0]);
++    if !is_type_diagnostic_item(cx, option_ty, sym!(option_type)) {
++        return;
++    }
++
++    let deref_aliases: [&[&str]; 9] = [
++        &paths::DEREF_TRAIT_METHOD,
++        &paths::DEREF_MUT_TRAIT_METHOD,
++        &paths::CSTRING_AS_C_STR,
++        &paths::OS_STRING_AS_OS_STR,
++        &paths::PATH_BUF_AS_PATH,
++        &paths::STRING_AS_STR,
++        &paths::STRING_AS_MUT_STR,
++        &paths::VEC_AS_SLICE,
++        &paths::VEC_AS_MUT_SLICE,
++    ];
++
++    let is_deref = match map_args[1].kind {
++        hir::ExprKind::Path(ref expr_qpath) => deref_aliases.iter().any(|path| match_qpath(expr_qpath, path)),
++        hir::ExprKind::Closure(_, _, body_id, _, _) => {
++            let closure_body = cx.tcx.hir().body(body_id);
++            let closure_expr = remove_blocks(&closure_body.value);
++
++            match &closure_expr.kind {
++                hir::ExprKind::MethodCall(_, _, args) => {
++                    if_chain! {
++                        if args.len() == 1;
++                        if let hir::ExprKind::Path(qpath) = &args[0].kind;
++                        if let hir::def::Res::Local(local_id) = cx.tables.qpath_res(qpath, args[0].hir_id);
++                        if closure_body.params[0].pat.hir_id == local_id;
++                        let adj = cx.tables.expr_adjustments(&args[0]).iter().map(|x| &x.kind).collect::<Box<[_]>>();
++                        if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj;
++                        then {
++                            let method_did = cx.tables.type_dependent_def_id(closure_expr.hir_id).unwrap();
++                            deref_aliases.iter().any(|path| match_def_path(cx, method_did, path))
++                        } else {
++                            false
++                        }
++                    }
++                },
++                hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, ref inner) if same_mutability(m) => {
++                    if_chain! {
++                        if let hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner1) = inner.kind;
++                        if let hir::ExprKind::Unary(hir::UnOp::UnDeref, ref inner2) = inner1.kind;
++                        if let hir::ExprKind::Path(ref qpath) = inner2.kind;
++                        if let hir::def::Res::Local(local_id) = cx.tables.qpath_res(qpath, inner2.hir_id);
++                        then {
++                            closure_body.params[0].pat.hir_id == local_id
++                        } else {
++                            false
++                        }
++                    }
++                },
++                _ => false,
++            }
++        },
++        _ => false,
++    };
++
++    if is_deref {
++        let current_method = if is_mut {
++            format!(".as_mut().map({})", snippet(cx, map_args[1].span, ".."))
++        } else {
++            format!(".as_ref().map({})", snippet(cx, map_args[1].span, ".."))
++        };
++        let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" };
++        let hint = format!("{}.{}()", snippet(cx, as_ref_args[0].span, ".."), method_hint);
++        let suggestion = format!("try using {} instead", method_hint);
++
++        let msg = format!(
++            "called `{0}` on an Option value. This can be done more directly \
++            by calling `{1}` instead",
++            current_method, hint
++        );
++        span_lint_and_sugg(
++            cx,
++            OPTION_AS_REF_DEREF,
++            expr.span,
++            &msg,
++            &suggestion,
++            hint,
++            Applicability::MachineApplicable,
++        );
++    }
++}
++
++/// Given a `Result<T, E>` type, return its error type (`E`).
++fn get_error_type<'a>(cx: &LateContext<'_, '_>, ty: Ty<'a>) -> Option<Ty<'a>> {
++    match ty.kind {
++        ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym!(result_type)) => substs.types().nth(1),
++        _ => None,
++    }
++}
++
++/// This checks whether a given type is known to implement Debug.
++fn has_debug_impl<'a, 'b>(ty: Ty<'a>, cx: &LateContext<'b, 'a>) -> bool {
++    cx.tcx
++        .get_diagnostic_item(sym::debug_trait)
++        .map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
++}
++
++enum Convention {
++    Eq(&'static str),
++    StartsWith(&'static str),
++}
++
++#[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 FN_HEADER: hir::FnHeader = hir::FnHeader {
++    unsafety: hir::Unsafety::Normal,
++    constness: hir::Constness::NotConst,
++    asyncness: hir::IsAsync::NotAsync,
++    abi: rustc_target::spec::abi::Abi::Rust,
++};
++
++#[rustfmt::skip]
++const TRAIT_METHODS: [(&str, usize, &hir::FnHeader, SelfKind, OutType, &str); 30] = [
++    ("add", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Add"),
++    ("as_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::convert::AsMut"),
++    ("as_ref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::convert::AsRef"),
++    ("bitand", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitAnd"),
++    ("bitor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitOr"),
++    ("bitxor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitXor"),
++    ("borrow", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::borrow::Borrow"),
++    ("borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::borrow::BorrowMut"),
++    ("clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::clone::Clone"),
++    ("cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::cmp::Ord"),
++    ("default", 0, &FN_HEADER, SelfKind::No, OutType::Any, "std::default::Default"),
++    ("deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Deref"),
++    ("deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::DerefMut"),
++    ("div", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Div"),
++    ("drop", 1, &FN_HEADER, SelfKind::RefMut, OutType::Unit, "std::ops::Drop"),
++    ("eq", 2, &FN_HEADER, SelfKind::Ref, OutType::Bool, "std::cmp::PartialEq"),
++    ("from_iter", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::iter::FromIterator"),
++    ("from_str", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::str::FromStr"),
++    ("hash", 2, &FN_HEADER, SelfKind::Ref, OutType::Unit, "std::hash::Hash"),
++    ("index", 2, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Index"),
++    ("index_mut", 2, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::IndexMut"),
++    ("into_iter", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::iter::IntoIterator"),
++    ("mul", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Mul"),
++    ("neg", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Neg"),
++    ("next", 1, &FN_HEADER, SelfKind::RefMut, OutType::Any, "std::iter::Iterator"),
++    ("not", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Not"),
++    ("rem", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Rem"),
++    ("shl", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shl"),
++    ("shr", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shr"),
++    ("sub", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Sub"),
++];
++
++#[rustfmt::skip]
++const PATTERN_METHODS: [(&str, usize); 17] = [
++    ("contains", 1),
++    ("starts_with", 1),
++    ("ends_with", 1),
++    ("find", 1),
++    ("rfind", 1),
++    ("split", 1),
++    ("rsplit", 1),
++    ("split_terminator", 1),
++    ("rsplit_terminator", 1),
++    ("splitn", 2),
++    ("rsplitn", 2),
++    ("matches", 1),
++    ("rmatches", 1),
++    ("match_indices", 1),
++    ("rmatch_indices", 1),
++    ("trim_start_matches", 1),
++    ("trim_end_matches", 1),
++];
++
++#[derive(Clone, Copy, PartialEq, Debug)]
++enum SelfKind {
++    Value,
++    Ref,
++    RefMut,
++    No,
++}
++
++impl SelfKind {
++    fn matches<'a>(self, cx: &LateContext<'_, 'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
++        fn matches_value<'a>(cx: &LateContext<'_, 'a>, parent_ty: Ty<'_>, ty: Ty<'_>) -> bool {
++            if ty == parent_ty {
++                true
++            } else if ty.is_box() {
++                ty.boxed_ty() == parent_ty
++            } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
++                if let ty::Adt(_, substs) = ty.kind {
++                    substs.types().next().map_or(false, |t| t == parent_ty)
++                } else {
++                    false
++                }
++            } else {
++                false
++            }
++        }
++
++        fn matches_ref<'a>(
++            cx: &LateContext<'_, 'a>,
++            mutability: hir::Mutability,
++            parent_ty: Ty<'a>,
++            ty: Ty<'a>,
++        ) -> bool {
++            if let ty::Ref(_, t, m) = ty.kind {
++                return m == mutability && t == parent_ty;
++            }
++
++            let trait_path = match mutability {
++                hir::Mutability::Not => &paths::ASREF_TRAIT,
++                hir::Mutability::Mut => &paths::ASMUT_TRAIT,
++            };
++
++            let trait_def_id = match get_trait_def_id(cx, trait_path) {
++                Some(did) => did,
++                None => return false,
++            };
++            implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
++        }
++
++        match self {
++            Self::Value => matches_value(cx, parent_ty, ty),
++            Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty),
++            Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty),
++            Self::No => ty != 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",
++        }
++    }
++}
++
++impl Convention {
++    #[must_use]
++    fn check(&self, other: &str) -> bool {
++        match *self {
++            Self::Eq(this) => this == other,
++            Self::StartsWith(this) => other.starts_with(this) && this != other,
++        }
++    }
++}
++
++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)),
++        }
++    }
++}
++
++#[derive(Clone, Copy)]
++enum OutType {
++    Unit,
++    Bool,
++    Any,
++    Ref,
++}
++
++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(&[]));
++        match (self, ty) {
++            (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true,
++            (Self::Unit, &hir::FnRetTy::Return(ref ty)) if is_unit(ty) => true,
++            (Self::Bool, &hir::FnRetTy::Return(ref ty)) if is_bool(ty) => true,
++            (Self::Any, &hir::FnRetTy::Return(ref ty)) if !is_unit(ty) => true,
++            (Self::Ref, &hir::FnRetTy::Return(ref ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)),
++            _ => false,
++        }
++    }
++}
++
++fn is_bool(ty: &hir::Ty<'_>) -> bool {
++    if let hir::TyKind::Path(ref p) = ty.kind {
++        match_qpath(p, &["bool"])
++    } else {
++        false
++    }
++}
++
++// Returns `true` if `expr` contains a return expression
++fn contains_return(expr: &hir::Expr<'_>) -> bool {
++    struct RetCallFinder {
++        found: bool,
++    }
++
++    impl<'tcx> intravisit::Visitor<'tcx> for RetCallFinder {
++        type Map = Map<'tcx>;
++
++        fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
++            if self.found {
++                return;
++            }
++            if let hir::ExprKind::Ret(..) = &expr.kind {
++                self.found = true;
++            } else {
++                intravisit::walk_expr(self, expr);
++            }
++        }
++
++        fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
++            intravisit::NestedVisitorMap::None
++        }
++    }
++
++    let mut visitor = RetCallFinder { found: false };
++    visitor.visit_expr(expr);
++    visitor.found
++}
++
++fn check_pointer_offset(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
++    if_chain! {
++        if args.len() == 2;
++        if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.tables.expr_ty(&args[0]).kind;
++        if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty));
++        if layout.is_zst();
++        then {
++            span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value");
++        }
++    }
++}
++
++fn lint_filetype_is_file(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
++    let ty = cx.tables.expr_ty(&args[0]);
++
++    if !match_type(cx, ty, &paths::FILE_TYPE) {
++        return;
++    }
++
++    let span: Span;
++    let verb: &str;
++    let lint_unary: &str;
++    let help_unary: &str;
++    if_chain! {
++        if let Some(parent) = get_parent_expr(cx, expr);
++        if let hir::ExprKind::Unary(op, _) = parent.kind;
++        if op == hir::UnOp::UnNot;
++        then {
++            lint_unary = "!";
++            verb = "denies";
++            help_unary = "";
++            span = parent.span;
++        } else {
++            lint_unary = "";
++            verb = "covers";
++            help_unary = "!";
++            span = expr.span;
++        }
++    }
++    let lint_msg = format!("`{}FileType::is_file()` only {} regular files", lint_unary, verb);
++    let help_msg = format!("use `{}FileType::is_dir()` instead", help_unary);
++    span_lint_and_help(cx, FILETYPE_IS_FILE, span, &lint_msg, None, &help_msg);
++}
++
++fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
++    expected.constness == actual.constness
++        && expected.unsafety == actual.unsafety
++        && expected.asyncness == actual.asyncness
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bf9dd3c9369298ad06f81d97acdaef7f90ad6971
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,135 @@@
++use crate::utils::{differing_macro_contexts, snippet_with_applicability, span_lint_and_then};
++use crate::utils::{is_copy, is_type_diagnostic_item};
++use rustc_data_structures::fx::FxHashSet;
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor};
++use rustc_hir::{self, HirId, Path};
++use rustc_lint::LateContext;
++use rustc_middle::hir::map::Map;
++use rustc_span::source_map::Span;
++use rustc_span::symbol::Symbol;
++
++use super::OPTION_MAP_UNWRAP_OR;
++
++/// lint use of `map().unwrap_or()` for `Option`s
++pub(super) fn lint<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &rustc_hir::Expr<'_>,
++    map_args: &'tcx [rustc_hir::Expr<'_>],
++    unwrap_args: &'tcx [rustc_hir::Expr<'_>],
++    map_span: Span,
++) {
++    // lint if the caller of `map()` is an `Option`
++    if is_type_diagnostic_item(cx, cx.tables.expr_ty(&map_args[0]), sym!(option_type)) {
++        if !is_copy(cx, cx.tables.expr_ty(&unwrap_args[1])) {
++            // Do not lint if the `map` argument uses identifiers in the `map`
++            // argument that are also used in the `unwrap_or` argument
++
++            let mut unwrap_visitor = UnwrapVisitor {
++                cx,
++                identifiers: FxHashSet::default(),
++            };
++            unwrap_visitor.visit_expr(&unwrap_args[1]);
++
++            let mut map_expr_visitor = MapExprVisitor {
++                cx,
++                identifiers: unwrap_visitor.identifiers,
++                found_identifier: false,
++            };
++            map_expr_visitor.visit_expr(&map_args[1]);
++
++            if map_expr_visitor.found_identifier {
++                return;
++            }
++        }
++
++        if differing_macro_contexts(unwrap_args[1].span, map_span) {
++            return;
++        }
++
++        let mut applicability = Applicability::MachineApplicable;
++        // get snippet for unwrap_or()
++        let unwrap_snippet = snippet_with_applicability(cx, unwrap_args[1].span, "..", &mut applicability);
++        // lint message
++        // comparing the snippet from source to raw text ("None") below is safe
++        // because we already have checked the type.
++        let arg = if unwrap_snippet == "None" { "None" } else { "a" };
++        let unwrap_snippet_none = unwrap_snippet == "None";
++        let suggest = if unwrap_snippet_none {
++            "and_then(f)"
++        } else {
++            "map_or(a, f)"
++        };
++        let msg = &format!(
++            "called `map(f).unwrap_or({})` on an `Option` value. \
++             This can be done more directly by calling `{}` instead",
++            arg, suggest
++        );
++
++        span_lint_and_then(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg, |diag| {
++            let map_arg_span = map_args[1].span;
++
++            let mut suggestion = vec![
++                (
++                    map_span,
++                    String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }),
++                ),
++                (expr.span.with_lo(unwrap_args[0].span.hi()), String::from("")),
++            ];
++
++            if !unwrap_snippet_none {
++                suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{}, ", unwrap_snippet)));
++            }
++
++            diag.multipart_suggestion(&format!("use `{}` instead", suggest), suggestion, applicability);
++        });
++    }
++}
++
++struct UnwrapVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    identifiers: FxHashSet<Symbol>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
++        self.identifiers.insert(ident(path));
++        walk_path(self, path);
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::All(self.cx.tcx.hir())
++    }
++}
++
++struct MapExprVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    identifiers: FxHashSet<Symbol>,
++    found_identifier: bool,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
++        if self.identifiers.contains(&ident(path)) {
++            self.found_identifier = true;
++            return;
++        }
++        walk_path(self, path);
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::All(self.cx.tcx.hir())
++    }
++}
++
++fn ident(path: &Path<'_>) -> Symbol {
++    path.segments
++        .last()
++        .expect("segments should be composed of at least 1 element")
++        .ident
++        .name
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..41c9ce7cda3e61ab780854fd44c566e5be870523
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,142 @@@
++use crate::utils::paths;
++use crate::utils::usage::mutated_variables;
++use crate::utils::{match_qpath, match_trait_method, span_lint};
++use rustc_hir as hir;
++use rustc_hir::def::Res;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_lint::LateContext;
++use rustc_middle::hir::map::Map;
++
++use if_chain::if_chain;
++
++use super::UNNECESSARY_FILTER_MAP;
++
++pub(super) fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
++    if !match_trait_method(cx, expr, &paths::ITERATOR) {
++        return;
++    }
++
++    if let hir::ExprKind::Closure(_, _, body_id, ..) = args[1].kind {
++        let body = cx.tcx.hir().body(body_id);
++        let arg_id = body.params[0].pat.hir_id;
++        let mutates_arg =
++            mutated_variables(&body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id));
++
++        let (mut found_mapping, mut found_filtering) = check_expression(&cx, arg_id, &body.value);
++
++        let mut return_visitor = ReturnVisitor::new(&cx, arg_id);
++        return_visitor.visit_expr(&body.value);
++        found_mapping |= return_visitor.found_mapping;
++        found_filtering |= return_visitor.found_filtering;
++
++        if !found_filtering {
++            span_lint(
++                cx,
++                UNNECESSARY_FILTER_MAP,
++                expr.span,
++                "this `.filter_map` can be written more simply using `.map`",
++            );
++            return;
++        }
++
++        if !found_mapping && !mutates_arg {
++            span_lint(
++                cx,
++                UNNECESSARY_FILTER_MAP,
++                expr.span,
++                "this `.filter_map` can be written more simply using `.filter`",
++            );
++            return;
++        }
++    }
++}
++
++// returns (found_mapping, found_filtering)
++fn check_expression<'a, 'tcx>(
++    cx: &'a LateContext<'a, 'tcx>,
++    arg_id: hir::HirId,
++    expr: &'tcx hir::Expr<'_>,
++) -> (bool, bool) {
++    match &expr.kind {
++        hir::ExprKind::Call(ref func, ref args) => {
++            if_chain! {
++                if let hir::ExprKind::Path(ref path) = func.kind;
++                then {
++                    if match_qpath(path, &paths::OPTION_SOME) {
++                        if_chain! {
++                            if let hir::ExprKind::Path(path) = &args[0].kind;
++                            if let Res::Local(ref local) = cx.tables.qpath_res(path, args[0].hir_id);
++                            then {
++                                if arg_id == *local {
++                                    return (false, false)
++                                }
++                            }
++                        }
++                        return (true, false);
++                    } else {
++                        // We don't know. It might do anything.
++                        return (true, true);
++                    }
++                }
++            }
++            (true, true)
++        },
++        hir::ExprKind::Block(ref block, _) => {
++            if let Some(expr) = &block.expr {
++                check_expression(cx, arg_id, &expr)
++            } else {
++                (false, false)
++            }
++        },
++        hir::ExprKind::Match(_, arms, _) => {
++            let mut found_mapping = false;
++            let mut found_filtering = false;
++            for arm in *arms {
++                let (m, f) = check_expression(cx, arg_id, &arm.body);
++                found_mapping |= m;
++                found_filtering |= f;
++            }
++            (found_mapping, found_filtering)
++        },
++        hir::ExprKind::Path(path) if match_qpath(path, &paths::OPTION_NONE) => (false, true),
++        _ => (true, true),
++    }
++}
++
++struct ReturnVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    arg_id: hir::HirId,
++    // Found a non-None return that isn't Some(input)
++    found_mapping: bool,
++    // Found a return that isn't Some
++    found_filtering: bool,
++}
++
++impl<'a, 'tcx> ReturnVisitor<'a, 'tcx> {
++    fn new(cx: &'a LateContext<'a, 'tcx>, arg_id: hir::HirId) -> ReturnVisitor<'a, 'tcx> {
++        ReturnVisitor {
++            cx,
++            arg_id,
++            found_mapping: false,
++            found_filtering: false,
++        }
++    }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for ReturnVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
++        if let hir::ExprKind::Ret(Some(expr)) = &expr.kind {
++            let (found_mapping, found_filtering) = check_expression(self.cx, self.arg_id, expr);
++            self.found_mapping |= found_mapping;
++            self.found_filtering |= found_filtering;
++        } else {
++            walk_expr(self, expr);
++        }
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b02c993de526b0478cacbcfc990f17d752d454f4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,102 @@@
++use crate::consts::{constant_simple, Constant};
++use crate::utils::{match_def_path, paths, span_lint};
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use std::cmp::Ordering;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for expressions where `std::cmp::min` and `max` are
++    /// used to clamp values, but switched so that the result is constant.
++    ///
++    /// **Why is this bad?** This is in all probability not the intended outcome. At
++    /// the least it hurts readability of the code.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// min(0, max(100, x))
++    /// ```
++    /// It will always be equal to `0`. Probably the author meant to clamp the value
++    /// between 0 and 100, but has erroneously swapped `min` and `max`.
++    pub MIN_MAX,
++    correctness,
++    "`min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant"
++}
++
++declare_lint_pass!(MinMaxPass => [MIN_MAX]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MinMaxPass {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if let Some((outer_max, outer_c, oe)) = min_max(cx, expr) {
++            if let Some((inner_max, inner_c, ie)) = min_max(cx, oe) {
++                if outer_max == inner_max {
++                    return;
++                }
++                match (
++                    outer_max,
++                    Constant::partial_cmp(cx.tcx, cx.tables.expr_ty(ie), &outer_c, &inner_c),
++                ) {
++                    (_, None) | (MinMax::Max, Some(Ordering::Less)) | (MinMax::Min, Some(Ordering::Greater)) => (),
++                    _ => {
++                        span_lint(
++                            cx,
++                            MIN_MAX,
++                            expr.span,
++                            "this `min`/`max` combination leads to constant result",
++                        );
++                    },
++                }
++            }
++        }
++    }
++}
++
++#[derive(PartialEq, Eq, Debug)]
++enum MinMax {
++    Min,
++    Max,
++}
++
++fn min_max<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
++    if let ExprKind::Call(ref path, ref args) = expr.kind {
++        if let ExprKind::Path(ref qpath) = path.kind {
++            cx.tables.qpath_res(qpath, path.hir_id).opt_def_id().and_then(|def_id| {
++                if match_def_path(cx, def_id, &paths::CMP_MIN) {
++                    fetch_const(cx, args, MinMax::Min)
++                } else if match_def_path(cx, def_id, &paths::CMP_MAX) {
++                    fetch_const(cx, args, MinMax::Max)
++                } else {
++                    None
++                }
++            })
++        } else {
++            None
++        }
++    } else {
++        None
++    }
++}
++
++fn fetch_const<'a>(
++    cx: &LateContext<'_, '_>,
++    args: &'a [Expr<'a>],
++    m: MinMax,
++) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
++    if args.len() != 2 {
++        return None;
++    }
++    if let Some(c) = constant_simple(cx, cx.tables, &args[0]) {
++        if constant_simple(cx, cx.tables, &args[1]).is_none() {
++            // otherwise ignore
++            Some((m, c, &args[1]))
++        } else {
++            None
++        }
++    } else if let Some(c) = constant_simple(cx, cx.tables, &args[1]) {
++        Some((m, c, &args[0]))
++    } else {
++        None
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e1d524c2231e4841bb1c91366c92a240f1156f0e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,696 @@@
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::FnKind;
++use rustc_hir::{
++    def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt, StmtKind, Ty,
++    TyKind, UnOp,
++};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::hygiene::DesugaringKind;
++use rustc_span::source_map::{ExpnKind, Span};
++
++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_integer_const, iter_input_pats,
++    last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg,
++    span_lint_and_then, span_lint_hir_and_then, walk_ptrs_ty, SpanlessEq,
++};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for function arguments and let bindings denoted as
++    /// `ref`.
++    ///
++    /// **Why is this bad?** The `ref` declaration makes the function take an owned
++    /// value, but turns the argument into a reference (which means that the value
++    /// is destroyed when exiting the function). This adds not much value: either
++    /// take a reference type, or take an owned value and create references in the
++    /// body.
++    ///
++    /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The
++    /// type of `x` is more obvious with the former.
++    ///
++    /// **Known problems:** If the argument is dereferenced within the function,
++    /// removing the `ref` will lead to errors. This can be fixed by removing the
++    /// dereferences, e.g., changing `*x` to `x` within the function.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// fn foo(ref x: u8) -> bool {
++    ///     true
++    /// }
++    /// ```
++    pub TOPLEVEL_REF_ARG,
++    style,
++    "an entire binding declared as `ref`, in a function argument or a `let` statement"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for comparisons to NaN.
++    ///
++    /// **Why is this bad?** NaN does not compare meaningfully to anything – not
++    /// even itself – so those comparisons are simply wrong.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let x = 1.0;
++    ///
++    /// if x == f32::NAN { }
++    /// ```
++    pub CMP_NAN,
++    correctness,
++    "comparisons to `NAN`, which will always return false, probably not intended"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for (in-)equality comparisons on floating-point
++    /// values (apart from zero), except in functions called `*eq*` (which probably
++    /// implement equality for a type involving floats).
++    ///
++    /// **Why is this bad?** Floating point calculations are usually imprecise, so
++    /// asking if two values are *exactly* equal is asking for trouble. For a good
++    /// guide on what to do, see [the floating point
++    /// guide](http://www.floating-point-gui.de/errors/comparison).
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x = 1.2331f64;
++    /// let y = 1.2332f64;
++    /// if y == 1.23f64 { }
++    /// if y != x {} // where both are floats
++    /// ```
++    pub FLOAT_CMP,
++    correctness,
++    "using `==` or `!=` on float values instead of comparing difference with an epsilon"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for conversions to owned values just for the sake
++    /// of a comparison.
++    ///
++    /// **Why is this bad?** The comparison can operate on a reference, so creating
++    /// an owned value effectively throws it away directly afterwards, which is
++    /// needlessly consuming code and heap space.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let x = "foo";
++    /// # let y = String::from("foo");
++    /// if x.to_owned() == y {}
++    /// ```
++    /// Could be written as
++    /// ```rust
++    /// # let x = "foo";
++    /// # let y = String::from("foo");
++    /// if x == y {}
++    /// ```
++    pub CMP_OWNED,
++    perf,
++    "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for getting the remainder of a division by one.
++    ///
++    /// **Why is this bad?** The result can only ever be zero. No one will write
++    /// such code deliberately, unless trying to win an Underhanded Rust
++    /// Contest. Even for that contest, it's probably a bad idea. Use something more
++    /// underhanded.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let x = 1;
++    /// let a = x % 1;
++    /// ```
++    pub MODULO_ONE,
++    correctness,
++    "taking a number modulo 1, which always returns 0"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for the use of bindings with a single leading
++    /// underscore.
++    ///
++    /// **Why is this bad?** A single leading underscore is usually used to indicate
++    /// that a binding will not be used. Using such a binding breaks this
++    /// expectation.
++    ///
++    /// **Known problems:** The lint does not work properly with desugaring and
++    /// macro, it has been allowed in the mean time.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let _x = 0;
++    /// let y = _x + 1; // Here we are using `_x`, even though it has a leading
++    ///                 // underscore. We should rename `_x` to `x`
++    /// ```
++    pub USED_UNDERSCORE_BINDING,
++    pedantic,
++    "using a binding which is prefixed with an underscore"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for the use of short circuit boolean conditions as
++    /// a
++    /// statement.
++    ///
++    /// **Why is this bad?** Using a short circuit boolean condition as a statement
++    /// may hide the fact that the second part is executed or not depending on the
++    /// outcome of the first part.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// f() && g(); // We should write `if f() { g(); }`.
++    /// ```
++    pub SHORT_CIRCUIT_STATEMENT,
++    complexity,
++    "using a short circuit boolean condition as a statement"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Catch casts from `0` to some pointer type
++    ///
++    /// **Why is this bad?** This generally means `null` and is better expressed as
++    /// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// let a = 0 as *const u32;
++    /// ```
++    pub ZERO_PTR,
++    style,
++    "using `0 as *{const, mut} T`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for (in-)equality comparisons on floating-point
++    /// value and constant, except in functions called `*eq*` (which probably
++    /// implement equality for a type involving floats).
++    ///
++    /// **Why is this bad?** Floating point calculations are usually imprecise, so
++    /// asking if two values are *exactly* equal is asking for trouble. For a good
++    /// guide on what to do, see [the floating point
++    /// guide](http://www.floating-point-gui.de/errors/comparison).
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x: f64 = 1.0;
++    /// const ONE: f64 = 1.00;
++    /// x == ONE;  // where both are floats
++    /// ```
++    pub FLOAT_CMP_CONST,
++    restriction,
++    "using `==` or `!=` on float constants instead of comparing difference with an epsilon"
++}
++
++declare_lint_pass!(MiscLints => [
++    TOPLEVEL_REF_ARG,
++    CMP_NAN,
++    FLOAT_CMP,
++    CMP_OWNED,
++    MODULO_ONE,
++    USED_UNDERSCORE_BINDING,
++    SHORT_CIRCUIT_STATEMENT,
++    ZERO_PTR,
++    FLOAT_CMP_CONST
++]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MiscLints {
++    fn check_fn(
++        &mut self,
++        cx: &LateContext<'a, 'tcx>,
++        k: FnKind<'tcx>,
++        decl: &'tcx FnDecl<'_>,
++        body: &'tcx Body<'_>,
++        _: Span,
++        _: HirId,
++    ) {
++        if let FnKind::Closure(_) = k {
++            // Does not apply to closures
++            return;
++        }
++        for arg in iter_input_pats(decl, body) {
++            match arg.pat.kind {
++                PatKind::Binding(BindingAnnotation::Ref, ..) | PatKind::Binding(BindingAnnotation::RefMut, ..) => {
++                    span_lint(
++                        cx,
++                        TOPLEVEL_REF_ARG,
++                        arg.pat.span,
++                        "`ref` directly on a function argument is ignored. Consider using a reference type \
++                         instead.",
++                    );
++                },
++                _ => {},
++            }
++        }
++    }
++
++    fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) {
++        if_chain! {
++            if let StmtKind::Local(ref local) = stmt.kind;
++            if let PatKind::Binding(an, .., name, None) = local.pat.kind;
++            if let Some(ref init) = local.init;
++            if !higher::is_from_for_desugar(local);
++            then {
++                if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut {
++                    let sugg_init = if init.span.from_expansion() {
++                        Sugg::hir_with_macro_callsite(cx, init, "..")
++                    } else {
++                        Sugg::hir(cx, init, "..")
++                    };
++                    let (mutopt, initref) = if an == BindingAnnotation::RefMut {
++                        ("mut ", sugg_init.mut_addr())
++                    } else {
++                        ("", sugg_init.addr())
++                    };
++                    let tyopt = if let Some(ref ty) = local.ty {
++                        format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "_"))
++                    } else {
++                        String::new()
++                    };
++                    span_lint_hir_and_then(
++                        cx,
++                        TOPLEVEL_REF_ARG,
++                        init.hir_id,
++                        local.pat.span,
++                        "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead",
++                        |diag| {
++                            diag.span_suggestion(
++                                stmt.span,
++                                "try",
++                                format!(
++                                    "let {name}{tyopt} = {initref};",
++                                    name=snippet(cx, name.span, "_"),
++                                    tyopt=tyopt,
++                                    initref=initref,
++                                ),
++                                Applicability::MachineApplicable,
++                            );
++                        }
++                    );
++                }
++            }
++        };
++        if_chain! {
++            if let StmtKind::Semi(ref expr) = stmt.kind;
++            if let ExprKind::Binary(ref binop, ref a, ref b) = expr.kind;
++            if binop.node == BinOpKind::And || binop.node == BinOpKind::Or;
++            if let Some(sugg) = Sugg::hir_opt(cx, a);
++            then {
++                span_lint_and_then(cx,
++                    SHORT_CIRCUIT_STATEMENT,
++                    stmt.span,
++                    "boolean short circuit operator in statement may be clearer using an explicit test",
++                    |diag| {
++                        let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg };
++                        diag.span_suggestion(
++                            stmt.span,
++                            "replace it with",
++                            format!(
++                                "if {} {{ {}; }}",
++                                sugg,
++                                &snippet(cx, b.span, ".."),
++                            ),
++                            Applicability::MachineApplicable, // snippet
++                        );
++                    });
++            }
++        };
++    }
++
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        match expr.kind {
++            ExprKind::Cast(ref e, ref ty) => {
++                check_cast(cx, expr.span, e, ty);
++                return;
++            },
++            ExprKind::Binary(ref cmp, ref left, ref right) => {
++                let op = cmp.node;
++                if op.is_comparison() {
++                    check_nan(cx, left, expr);
++                    check_nan(cx, right, expr);
++                    check_to_owned(cx, left, right);
++                    check_to_owned(cx, right, left);
++                }
++                if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
++                    if is_allowed(cx, left) || is_allowed(cx, right) {
++                        return;
++                    }
++
++                    // Allow comparing the results of signum()
++                    if is_signum(cx, left) && is_signum(cx, right) {
++                        return;
++                    }
++
++                    if let Some(name) = get_item_name(cx, expr) {
++                        let name = name.as_str();
++                        if name == "eq"
++                            || name == "ne"
++                            || name == "is_nan"
++                            || name.starts_with("eq_")
++                            || name.ends_with("_eq")
++                        {
++                            return;
++                        }
++                    }
++                    let is_comparing_arrays = is_array(cx, left) || is_array(cx, right);
++                    let (lint, msg) = get_lint_and_message(
++                        is_named_constant(cx, left) || is_named_constant(cx, right),
++                        is_comparing_arrays,
++                    );
++                    span_lint_and_then(cx, lint, expr.span, msg, |diag| {
++                        let lhs = Sugg::hir(cx, left, "..");
++                        let rhs = Sugg::hir(cx, right, "..");
++
++                        if !is_comparing_arrays {
++                            diag.span_suggestion(
++                                expr.span,
++                                "consider comparing them within some error",
++                                format!(
++                                    "({}).abs() {} error",
++                                    lhs - rhs,
++                                    if op == BinOpKind::Eq { '<' } else { '>' }
++                                ),
++                                Applicability::HasPlaceholders, // snippet
++                            );
++                        }
++                        diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error`");
++                    });
++                } else if op == BinOpKind::Rem && is_integer_const(cx, right, 1) {
++                    span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
++                }
++            },
++            _ => {},
++        }
++        if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) {
++            // Don't lint things expanded by #[derive(...)], etc or `await` desugaring
++            return;
++        }
++        let binding = match expr.kind {
++            ExprKind::Path(ref qpath) => {
++                let binding = last_path_segment(qpath).ident.as_str();
++                if binding.starts_with('_') &&
++                    !binding.starts_with("__") &&
++                    binding != "_result" && // FIXME: #944
++                    is_used(cx, expr) &&
++                    // don't lint if the declaration is in a macro
++                    non_macro_local(cx, cx.tables.qpath_res(qpath, expr.hir_id))
++                {
++                    Some(binding)
++                } else {
++                    None
++                }
++            },
++            ExprKind::Field(_, ident) => {
++                let name = ident.as_str();
++                if name.starts_with('_') && !name.starts_with("__") {
++                    Some(name)
++                } else {
++                    None
++                }
++            },
++            _ => None,
++        };
++        if let Some(binding) = binding {
++            span_lint(
++                cx,
++                USED_UNDERSCORE_BINDING,
++                expr.span,
++                &format!(
++                    "used binding `{}` which is prefixed with an underscore. A leading \
++                     underscore signals that a binding will not be used.",
++                    binding
++                ),
++            );
++        }
++    }
++}
++
++fn get_lint_and_message(
++    is_comparing_constants: bool,
++    is_comparing_arrays: bool,
++) -> (&'static rustc_lint::Lint, &'static str) {
++    if is_comparing_constants {
++        (
++            FLOAT_CMP_CONST,
++            if is_comparing_arrays {
++                "strict comparison of `f32` or `f64` constant arrays"
++            } else {
++                "strict comparison of `f32` or `f64` constant"
++            },
++        )
++    } else {
++        (
++            FLOAT_CMP,
++            if is_comparing_arrays {
++                "strict comparison of `f32` or `f64` arrays"
++            } else {
++                "strict comparison of `f32` or `f64`"
++            },
++        )
++    }
++}
++
++fn check_nan(cx: &LateContext<'_, '_>, expr: &Expr<'_>, cmp_expr: &Expr<'_>) {
++    if_chain! {
++        if !in_constant(cx, cmp_expr.hir_id);
++        if let Some((value, _)) = constant(cx, cx.tables, expr);
++        then {
++            let needs_lint = match value {
++                Constant::F32(num) => num.is_nan(),
++                Constant::F64(num) => num.is_nan(),
++                _ => false,
++            };
++
++            if needs_lint {
++                span_lint(
++                    cx,
++                    CMP_NAN,
++                    cmp_expr.span,
++                    "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead",
++                );
++            }
++        }
++    }
++}
++
++fn is_named_constant<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> bool {
++    if let Some((_, res)) = constant(cx, cx.tables, expr) {
++        res
++    } else {
++        false
++    }
++}
++
++fn is_allowed<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> bool {
++    match constant(cx, cx.tables, expr) {
++        Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(),
++        Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(),
++        Some((Constant::Vec(vec), _)) => vec.iter().all(|f| match f {
++            Constant::F32(f) => *f == 0.0 || (*f).is_infinite(),
++            Constant::F64(f) => *f == 0.0 || (*f).is_infinite(),
++            _ => false,
++        }),
++        _ => false,
++    }
++}
++
++// Return true if `expr` is the result of `signum()` invoked on a float value.
++fn is_signum(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++    // The negation of a signum is still a signum
++    if let ExprKind::Unary(UnOp::UnNeg, ref child_expr) = expr.kind {
++        return is_signum(cx, &child_expr);
++    }
++
++    if_chain! {
++        if let ExprKind::MethodCall(ref method_name, _, ref expressions) = expr.kind;
++        if sym!(signum) == method_name.ident.name;
++        // Check that the receiver of the signum() is a float (expressions[0] is the receiver of
++        // the method call)
++        then {
++            return is_float(cx, &expressions[0]);
++        }
++    }
++    false
++}
++
++fn is_float(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++    let value = &walk_ptrs_ty(cx.tables.expr_ty(expr)).kind;
++
++    if let ty::Array(arr_ty, _) = value {
++        return matches!(arr_ty.kind, ty::Float(_));
++    };
++
++    matches!(value, ty::Float(_))
++}
++
++fn is_array(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++    matches!(&walk_ptrs_ty(cx.tables.expr_ty(expr)).kind, ty::Array(_, _))
++}
++
++fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr<'_>, other: &Expr<'_>) {
++    let (arg_ty, snip) = match expr.kind {
++        ExprKind::MethodCall(.., ref args) if args.len() == 1 => {
++            if match_trait_method(cx, expr, &paths::TO_STRING) || match_trait_method(cx, expr, &paths::TO_OWNED) {
++                (cx.tables.expr_ty_adjusted(&args[0]), snippet(cx, args[0].span, ".."))
++            } else {
++                return;
++            }
++        },
++        ExprKind::Call(ref path, ref v) if v.len() == 1 => {
++            if let ExprKind::Path(ref path) = path.kind {
++                if match_qpath(path, &["String", "from_str"]) || match_qpath(path, &["String", "from"]) {
++                    (cx.tables.expr_ty_adjusted(&v[0]), snippet(cx, v[0].span, ".."))
++                } else {
++                    return;
++                }
++            } else {
++                return;
++            }
++        },
++        _ => return,
++    };
++
++    let other_ty = cx.tables.expr_ty_adjusted(other);
++    let partial_eq_trait_id = match cx.tcx.lang_items().eq_trait() {
++        Some(id) => id,
++        None => return,
++    };
++
++    let deref_arg_impl_partial_eq_other = arg_ty.builtin_deref(true).map_or(false, |tam| {
++        implements_trait(cx, tam.ty, partial_eq_trait_id, &[other_ty.into()])
++    });
++    let arg_impl_partial_eq_deref_other = other_ty.builtin_deref(true).map_or(false, |tam| {
++        implements_trait(cx, arg_ty, partial_eq_trait_id, &[tam.ty.into()])
++    });
++    let arg_impl_partial_eq_other = implements_trait(cx, arg_ty, partial_eq_trait_id, &[other_ty.into()]);
++
++    if !deref_arg_impl_partial_eq_other && !arg_impl_partial_eq_deref_other && !arg_impl_partial_eq_other {
++        return;
++    }
++
++    let other_gets_derefed = match other.kind {
++        ExprKind::Unary(UnOp::UnDeref, _) => true,
++        _ => false,
++    };
++
++    let lint_span = if other_gets_derefed {
++        expr.span.to(other.span)
++    } else {
++        expr.span
++    };
++
++    span_lint_and_then(
++        cx,
++        CMP_OWNED,
++        lint_span,
++        "this creates an owned instance just for comparison",
++        |diag| {
++            // This also catches `PartialEq` implementations that call `to_owned`.
++            if other_gets_derefed {
++                diag.span_label(lint_span, "try implementing the comparison without allocating");
++                return;
++            }
++
++            let try_hint = if deref_arg_impl_partial_eq_other {
++                // suggest deref on the left
++                format!("*{}", snip)
++            } else {
++                // suggest dropping the to_owned on the left
++                snip.to_string()
++            };
++
++            diag.span_suggestion(
++                lint_span,
++                "try",
++                try_hint,
++                Applicability::MachineApplicable, // snippet
++            );
++        },
++    );
++}
++
++/// Heuristic to see if an expression is used. Should be compatible with
++/// `unused_variables`'s idea
++/// of what it means for an expression to be "used".
++fn is_used(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++    if let Some(parent) = get_parent_expr(cx, expr) {
++        match parent.kind {
++            ExprKind::Assign(_, ref rhs, _) | ExprKind::AssignOp(_, _, ref rhs) => {
++                SpanlessEq::new(cx).eq_expr(rhs, expr)
++            },
++            _ => is_used(cx, parent),
++        }
++    } else {
++        true
++    }
++}
++
++/// Tests whether an expression is in a macro expansion (e.g., something
++/// generated by `#[derive(...)]` or the like).
++fn in_attributes_expansion(expr: &Expr<'_>) -> bool {
++    use rustc_span::hygiene::MacroKind;
++    if expr.span.from_expansion() {
++        let data = expr.span.ctxt().outer_expn_data();
++
++        if let ExpnKind::Macro(MacroKind::Attr, _) = data.kind {
++            true
++        } else {
++            false
++        }
++    } else {
++        false
++    }
++}
++
++/// Tests whether `res` is a variable defined outside a macro.
++fn non_macro_local(cx: &LateContext<'_, '_>, res: def::Res) -> bool {
++    if let def::Res::Local(id) = res {
++        !cx.tcx.hir().span(id).from_expansion()
++    } else {
++        false
++    }
++}
++
++fn check_cast(cx: &LateContext<'_, '_>, span: Span, e: &Expr<'_>, ty: &Ty<'_>) {
++    if_chain! {
++        if let TyKind::Ptr(ref mut_ty) = ty.kind;
++        if let ExprKind::Lit(ref lit) = e.kind;
++        if let LitKind::Int(0, _) = lit.node;
++        if !in_constant(cx, e.hir_id);
++        then {
++            let (msg, sugg_fn) = match mut_ty.mutbl {
++                Mutability::Mut => ("`0 as *mut _` detected", "std::ptr::null_mut"),
++                Mutability::Not => ("`0 as *const _` detected", "std::ptr::null"),
++            };
++
++            let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind {
++                (format!("{}()", sugg_fn), Applicability::MachineApplicable)
++            } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) {
++                (format!("{}::<{}>()", sugg_fn, mut_ty_snip), Applicability::MachineApplicable)
++            } else {
++                // `MaybeIncorrect` as type inference may not work with the suggested code
++                (format!("{}()", sugg_fn), Applicability::MaybeIncorrect)
++            };
++            span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl);
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..adfd8dfb1c18af5abcdb6d4eca6414e9803ed86f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,638 @@@
++use crate::utils::{
++    constants, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg,
++    span_lint_and_then,
++};
++use if_chain::if_chain;
++use rustc_ast::ast::{
++    BindingMode, Block, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability,
++    NodeId, Pat, PatKind, StmtKind, UnOp,
++};
++use rustc_ast::visit::{walk_expr, FnKind, Visitor};
++use rustc_data_structures::fx::FxHashMap;
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for structure field patterns bound to wildcards.
++    ///
++    /// **Why is this bad?** Using `..` instead is shorter and leaves the focus on
++    /// the fields that are actually bound.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// let { a: _, b: ref b, c: _ } = ..
++    /// ```
++    pub UNNEEDED_FIELD_PATTERN,
++    restriction,
++    "struct fields bound to a wildcard instead of using `..`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for function arguments having the similar names
++    /// differing by an underscore.
++    ///
++    /// **Why is this bad?** It affects code readability.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// fn foo(a: i32, _a: i32) {}
++    /// ```
++    pub DUPLICATE_UNDERSCORE_ARGUMENT,
++    style,
++    "function arguments having names which only differ by an underscore"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Detects closures called in the same expression where they
++    /// are defined.
++    ///
++    /// **Why is this bad?** It is unnecessarily adding to the expression's
++    /// complexity.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// (|| 42)()
++    /// ```
++    pub REDUNDANT_CLOSURE_CALL,
++    complexity,
++    "throwaway closures called in the expression they are defined"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Detects expressions of the form `--x`.
++    ///
++    /// **Why is this bad?** It can mislead C/C++ programmers to think `x` was
++    /// decremented.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let mut x = 3;
++    /// --x;
++    /// ```
++    pub DOUBLE_NEG,
++    style,
++    "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Warns on hexadecimal literals with mixed-case letter
++    /// digits.
++    ///
++    /// **Why is this bad?** It looks confusing.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let y = 0x1a9BAcD;
++    /// ```
++    pub MIXED_CASE_HEX_LITERALS,
++    style,
++    "hex literals whose letter digits are not consistently upper- or lowercased"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Warns if literal suffixes are not separated by an
++    /// underscore.
++    ///
++    /// **Why is this bad?** It is much less readable.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let y = 123832i32;
++    /// ```
++    pub UNSEPARATED_LITERAL_SUFFIX,
++    pedantic,
++    "literals whose suffix is not separated by an underscore"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Warns if an integral constant literal starts with `0`.
++    ///
++    /// **Why is this bad?** In some languages (including the infamous C language
++    /// and most of its
++    /// family), this marks an octal constant. In Rust however, this is a decimal
++    /// constant. This could
++    /// be confusing for both the writer and a reader of the constant.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// In Rust:
++    /// ```rust
++    /// fn main() {
++    ///     let a = 0123;
++    ///     println!("{}", a);
++    /// }
++    /// ```
++    ///
++    /// prints `123`, while in C:
++    ///
++    /// ```c
++    /// #include <stdio.h>
++    ///
++    /// int main() {
++    ///     int a = 0123;
++    ///     printf("%d\n", a);
++    /// }
++    /// ```
++    ///
++    /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
++    pub ZERO_PREFIXED_LITERAL,
++    complexity,
++    "integer literals starting with `0`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Warns if a generic shadows a built-in type.
++    ///
++    /// **Why is this bad?** This gives surprising type errors.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```ignore
++    /// impl<u32> Foo<u32> {
++    ///     fn impl_func(&self) -> u32 {
++    ///         42
++    ///     }
++    /// }
++    /// ```
++    pub BUILTIN_TYPE_SHADOW,
++    style,
++    "shadowing a builtin type"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for patterns in the form `name @ _`.
++    ///
++    /// **Why is this bad?** It's almost always more readable to just use direct
++    /// bindings.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let v = Some("abc");
++    ///
++    /// match v {
++    ///     Some(x) => (),
++    ///     y @ _ => (), // easier written as `y`,
++    /// }
++    /// ```
++    pub REDUNDANT_PATTERN,
++    style,
++    "using `name @ _` in a pattern"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for tuple patterns with a wildcard
++    /// pattern (`_`) is next to a rest pattern (`..`).
++    ///
++    /// _NOTE_: While `_, ..` means there is at least one element left, `..`
++    /// means there are 0 or more elements left. This can make a difference
++    /// when refactoring, but shouldn't result in errors in the refactored code,
++    /// since the wildcard pattern isn't used anyway.
++    /// **Why is this bad?** The wildcard pattern is unneeded as the rest pattern
++    /// can match that element as well.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # struct TupleStruct(u32, u32, u32);
++    /// # let t = TupleStruct(1, 2, 3);
++    ///
++    /// match t {
++    ///     TupleStruct(0, .., _) => (),
++    ///     _ => (),
++    /// }
++    /// ```
++    /// can be written as
++    /// ```rust
++    /// # struct TupleStruct(u32, u32, u32);
++    /// # let t = TupleStruct(1, 2, 3);
++    ///
++    /// match t {
++    ///     TupleStruct(0, ..) => (),
++    ///     _ => (),
++    /// }
++    /// ```
++    pub UNNEEDED_WILDCARD_PATTERN,
++    complexity,
++    "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
++}
++
++declare_lint_pass!(MiscEarlyLints => [
++    UNNEEDED_FIELD_PATTERN,
++    DUPLICATE_UNDERSCORE_ARGUMENT,
++    REDUNDANT_CLOSURE_CALL,
++    DOUBLE_NEG,
++    MIXED_CASE_HEX_LITERALS,
++    UNSEPARATED_LITERAL_SUFFIX,
++    ZERO_PREFIXED_LITERAL,
++    BUILTIN_TYPE_SHADOW,
++    REDUNDANT_PATTERN,
++    UNNEEDED_WILDCARD_PATTERN,
++]);
++
++// Used to find `return` statements or equivalents e.g., `?`
++struct ReturnVisitor {
++    found_return: bool,
++}
++
++impl ReturnVisitor {
++    #[must_use]
++    fn new() -> Self {
++        Self { found_return: false }
++    }
++}
++
++impl<'ast> Visitor<'ast> for ReturnVisitor {
++    fn visit_expr(&mut self, ex: &'ast Expr) {
++        if let ExprKind::Ret(_) = ex.kind {
++            self.found_return = true;
++        } else if let ExprKind::Try(_) = ex.kind {
++            self.found_return = true;
++        }
++
++        walk_expr(self, ex)
++    }
++}
++
++impl EarlyLintPass for MiscEarlyLints {
++    fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
++        for param in &gen.params {
++            if let GenericParamKind::Type { .. } = param.kind {
++                let name = param.ident.as_str();
++                if constants::BUILTIN_TYPES.contains(&&*name) {
++                    span_lint(
++                        cx,
++                        BUILTIN_TYPE_SHADOW,
++                        param.ident.span,
++                        &format!("This generic shadows the built-in type `{}`", name),
++                    );
++                }
++            }
++        }
++    }
++
++    fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
++        if let PatKind::Struct(ref npat, ref pfields, _) = pat.kind {
++            let mut wilds = 0;
++            let type_name = npat
++                .segments
++                .last()
++                .expect("A path must have at least one segment")
++                .ident
++                .name;
++
++            for field in pfields {
++                if let PatKind::Wild = field.pat.kind {
++                    wilds += 1;
++                }
++            }
++            if !pfields.is_empty() && wilds == pfields.len() {
++                span_lint_and_help(
++                    cx,
++                    UNNEEDED_FIELD_PATTERN,
++                    pat.span,
++                    "All the struct fields are matched to a wildcard pattern, consider using `..`.",
++                    None,
++                    &format!("Try with `{} {{ .. }}` instead", type_name),
++                );
++                return;
++            }
++            if wilds > 0 {
++                for field in pfields {
++                    if let PatKind::Wild = field.pat.kind {
++                        wilds -= 1;
++                        if wilds > 0 {
++                            span_lint(
++                                cx,
++                                UNNEEDED_FIELD_PATTERN,
++                                field.span,
++                                "You matched a field with a wildcard pattern. Consider using `..` instead",
++                            );
++                        } else {
++                            let mut normal = vec![];
++
++                            for field in pfields {
++                                match field.pat.kind {
++                                    PatKind::Wild => {},
++                                    _ => {
++                                        if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) {
++                                            normal.push(n);
++                                        }
++                                    },
++                                }
++                            }
++
++                            span_lint_and_help(
++                                cx,
++                                UNNEEDED_FIELD_PATTERN,
++                                field.span,
++                                "You matched a field with a wildcard pattern. Consider using `..` \
++                                 instead",
++                                None,
++                                &format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
++                            );
++                        }
++                    }
++                }
++            }
++        }
++
++        if let PatKind::Ident(left, ident, Some(ref right)) = pat.kind {
++            let left_binding = match left {
++                BindingMode::ByRef(Mutability::Mut) => "ref mut ",
++                BindingMode::ByRef(Mutability::Not) => "ref ",
++                _ => "",
++            };
++
++            if let PatKind::Wild = right.kind {
++                span_lint_and_sugg(
++                    cx,
++                    REDUNDANT_PATTERN,
++                    pat.span,
++                    &format!(
++                        "the `{} @ _` pattern can be written as just `{}`",
++                        ident.name, ident.name,
++                    ),
++                    "try",
++                    format!("{}{}", left_binding, ident.name),
++                    Applicability::MachineApplicable,
++                );
++            }
++        }
++
++        check_unneeded_wildcard_pattern(cx, pat);
++    }
++
++    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
++        let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();
++
++        for arg in &fn_kind.decl().inputs {
++            if let PatKind::Ident(_, ident, None) = arg.pat.kind {
++                let arg_name = ident.to_string();
++
++                if arg_name.starts_with('_') {
++                    if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
++                        span_lint(
++                            cx,
++                            DUPLICATE_UNDERSCORE_ARGUMENT,
++                            *correspondence,
++                            &format!(
++                                "`{}` already exists, having another argument having almost the same \
++                                 name makes code comprehension and documentation more difficult",
++                                arg_name[1..].to_owned()
++                            ),
++                        );
++                    }
++                } else {
++                    registered_names.insert(arg_name, arg.pat.span);
++                }
++            }
++        }
++    }
++
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++        if in_external_macro(cx.sess(), expr.span) {
++            return;
++        }
++        match expr.kind {
++            ExprKind::Call(ref paren, _) => {
++                if let ExprKind::Paren(ref closure) = paren.kind {
++                    if let ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.kind {
++                        let mut visitor = ReturnVisitor::new();
++                        visitor.visit_expr(block);
++                        if !visitor.found_return {
++                            span_lint_and_then(
++                                cx,
++                                REDUNDANT_CLOSURE_CALL,
++                                expr.span,
++                                "Try not to call a closure in the expression where it is declared.",
++                                |diag| {
++                                    if decl.inputs.is_empty() {
++                                        let mut app = Applicability::MachineApplicable;
++                                        let hint =
++                                            snippet_with_applicability(cx, block.span, "..", &mut app).into_owned();
++                                        diag.span_suggestion(expr.span, "Try doing something like: ", hint, app);
++                                    }
++                                },
++                            );
++                        }
++                    }
++                }
++            },
++            ExprKind::Unary(UnOp::Neg, ref inner) => {
++                if let ExprKind::Unary(UnOp::Neg, _) = inner.kind {
++                    span_lint(
++                        cx,
++                        DOUBLE_NEG,
++                        expr.span,
++                        "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
++                    );
++                }
++            },
++            ExprKind::Lit(ref lit) => Self::check_lit(cx, lit),
++            _ => (),
++        }
++    }
++
++    fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
++        for w in block.stmts.windows(2) {
++            if_chain! {
++                if let StmtKind::Local(ref local) = w[0].kind;
++                if let Option::Some(ref t) = local.init;
++                if let ExprKind::Closure(..) = t.kind;
++                if let PatKind::Ident(_, ident, _) = local.pat.kind;
++                if let StmtKind::Semi(ref second) = w[1].kind;
++                if let ExprKind::Assign(_, ref call, _) = second.kind;
++                if let ExprKind::Call(ref closure, _) = call.kind;
++                if let ExprKind::Path(_, ref path) = closure.kind;
++                then {
++                    if ident == path.segments[0].ident {
++                        span_lint(
++                            cx,
++                            REDUNDANT_CLOSURE_CALL,
++                            second.span,
++                            "Closure called just once immediately after it was declared",
++                        );
++                    }
++                }
++            }
++        }
++    }
++}
++
++impl MiscEarlyLints {
++    fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
++        // We test if first character in snippet is a number, because the snippet could be an expansion
++        // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`.
++        // Note that this check also covers special case that `line!()` is eagerly expanded by compiler.
++        // See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
++        // FIXME: Find a better way to detect those cases.
++        let lit_snip = match snippet_opt(cx, lit.span) {
++            Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip,
++            _ => return,
++        };
++
++        if let LitKind::Int(value, lit_int_type) = lit.kind {
++            let suffix = match lit_int_type {
++                LitIntType::Signed(ty) => ty.name_str(),
++                LitIntType::Unsigned(ty) => ty.name_str(),
++                LitIntType::Unsuffixed => "",
++            };
++
++            let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
++                val
++            } else {
++                return; // It's useless so shouldn't lint.
++            };
++            // Do not lint when literal is unsuffixed.
++            if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
++                span_lint_and_sugg(
++                    cx,
++                    UNSEPARATED_LITERAL_SUFFIX,
++                    lit.span,
++                    "integer type suffix should be separated by an underscore",
++                    "add an underscore",
++                    format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
++                    Applicability::MachineApplicable,
++                );
++            }
++
++            if lit_snip.starts_with("0x") {
++                if maybe_last_sep_idx <= 2 {
++                    // It's meaningless or causes range error.
++                    return;
++                }
++                let mut seen = (false, false);
++                for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() {
++                    match ch {
++                        b'a'..=b'f' => seen.0 = true,
++                        b'A'..=b'F' => seen.1 = true,
++                        _ => {},
++                    }
++                    if seen.0 && seen.1 {
++                        span_lint(
++                            cx,
++                            MIXED_CASE_HEX_LITERALS,
++                            lit.span,
++                            "inconsistent casing in hexadecimal literal",
++                        );
++                        break;
++                    }
++                }
++            } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") {
++                /* nothing to do */
++            } else if value != 0 && lit_snip.starts_with('0') {
++                span_lint_and_then(
++                    cx,
++                    ZERO_PREFIXED_LITERAL,
++                    lit.span,
++                    "this is a decimal constant",
++                    |diag| {
++                        diag.span_suggestion(
++                            lit.span,
++                            "if you mean to use a decimal constant, remove the `0` to avoid confusion",
++                            lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(),
++                            Applicability::MaybeIncorrect,
++                        );
++                        diag.span_suggestion(
++                            lit.span,
++                            "if you mean to use an octal constant, use `0o`",
++                            format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')),
++                            Applicability::MaybeIncorrect,
++                        );
++                    },
++                );
++            }
++        } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind {
++            let suffix = float_ty.name_str();
++            let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
++                val
++            } else {
++                return; // It's useless so shouldn't lint.
++            };
++            if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
++                span_lint_and_sugg(
++                    cx,
++                    UNSEPARATED_LITERAL_SUFFIX,
++                    lit.span,
++                    "float type suffix should be separated by an underscore",
++                    "add an underscore",
++                    format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
++                    Applicability::MachineApplicable,
++                );
++            }
++        }
++    }
++}
++
++fn check_unneeded_wildcard_pattern(cx: &EarlyContext<'_>, pat: &Pat) {
++    if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind {
++        fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) {
++            span_lint_and_sugg(
++                cx,
++                UNNEEDED_WILDCARD_PATTERN,
++                span,
++                if only_one {
++                    "this pattern is unneeded as the `..` pattern can match that element"
++                } else {
++                    "these patterns are unneeded as the `..` pattern can match those elements"
++                },
++                if only_one { "remove it" } else { "remove them" },
++                "".to_string(),
++                Applicability::MachineApplicable,
++            );
++        }
++
++        #[allow(clippy::trivially_copy_pass_by_ref)]
++        fn is_wild<P: std::ops::Deref<Target = Pat>>(pat: &&P) -> bool {
++            if let PatKind::Wild = pat.kind {
++                true
++            } else {
++                false
++            }
++        }
++
++        if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) {
++            if let Some((left_index, left_pat)) = patterns[..rest_index]
++                .iter()
++                .rev()
++                .take_while(is_wild)
++                .enumerate()
++                .last()
++            {
++                span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
++            }
++
++            if let Some((right_index, right_pat)) =
++                patterns[rest_index + 1..].iter().take_while(is_wild).enumerate().last()
++            {
++                span_lint(
++                    cx,
++                    patterns[rest_index].span.shrink_to_hi().to(right_pat.span),
++                    right_index == 0,
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4301157e16440ccbea1f95732b57d81335514ca5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,145 @@@
++use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method};
++use rustc_hir as hir;
++use rustc_hir::intravisit::FnKind;
++use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::lint::in_external_macro;
++use rustc_mir::transform::qualify_min_const_fn::is_min_const_fn;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::Span;
++use rustc_typeck::hir_ty_to_ty;
++
++declare_clippy_lint! {
++    /// **What it does:**
++    ///
++    /// Suggests the use of `const` in functions and methods where possible.
++    ///
++    /// **Why is this bad?**
++    ///
++    /// Not having the function const prevents callers of the function from being const as well.
++    ///
++    /// **Known problems:**
++    ///
++    /// Const functions are currently still being worked on, with some features only being available
++    /// on nightly. This lint does not consider all edge cases currently and the suggestions may be
++    /// incorrect if you are using this lint on stable.
++    ///
++    /// Also, the lint only runs one pass over the code. Consider these two non-const functions:
++    ///
++    /// ```rust
++    /// fn a() -> i32 {
++    ///     0
++    /// }
++    /// fn b() -> i32 {
++    ///     a()
++    /// }
++    /// ```
++    ///
++    /// When running Clippy, the lint will only suggest to make `a` const, because `b` at this time
++    /// can't be const as it calls a non-const function. Making `a` const and running Clippy again,
++    /// will suggest to make `b` const, too.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// # struct Foo {
++    /// #     random_number: usize,
++    /// # }
++    /// # impl Foo {
++    /// fn new() -> Self {
++    ///     Self { random_number: 42 }
++    /// }
++    /// # }
++    /// ```
++    ///
++    /// Could be a const fn:
++    ///
++    /// ```rust
++    /// # struct Foo {
++    /// #     random_number: usize,
++    /// # }
++    /// # impl Foo {
++    /// const fn new() -> Self {
++    ///     Self { random_number: 42 }
++    /// }
++    /// # }
++    /// ```
++    pub MISSING_CONST_FOR_FN,
++    nursery,
++    "Lint functions definitions that could be made `const fn`"
++}
++
++declare_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingConstForFn {
++    fn check_fn(
++        &mut self,
++        cx: &LateContext<'_, '_>,
++        kind: FnKind<'_>,
++        _: &FnDecl<'_>,
++        _: &Body<'_>,
++        span: Span,
++        hir_id: HirId,
++    ) {
++        let def_id = cx.tcx.hir().local_def_id(hir_id);
++
++        if in_external_macro(cx.tcx.sess, span) || is_entrypoint_fn(cx, def_id.to_def_id()) {
++            return;
++        }
++
++        // Building MIR for `fn`s with unsatisfiable preds results in ICE.
++        if fn_has_unsatisfiable_preds(cx, def_id.to_def_id()) {
++            return;
++        }
++
++        // Perform some preliminary checks that rule out constness on the Clippy side. This way we
++        // can skip the actual const check and return early.
++        match kind {
++            FnKind::ItemFn(_, generics, header, ..) => {
++                let has_const_generic_params = generics
++                    .params
++                    .iter()
++                    .any(|param| matches!(param.kind, GenericParamKind::Const{ .. }));
++
++                if already_const(header) || has_const_generic_params {
++                    return;
++                }
++            },
++            FnKind::Method(_, sig, ..) => {
++                if trait_ref_of_method(cx, hir_id).is_some()
++                    || already_const(sig.header)
++                    || method_accepts_dropable(cx, sig.decl.inputs)
++                {
++                    return;
++                }
++            },
++            _ => return,
++        }
++
++        let mir = cx.tcx.optimized_mir(def_id);
++
++        if let Err((span, err)) = is_min_const_fn(cx.tcx, def_id.to_def_id(), &mir) {
++            if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) {
++                cx.tcx.sess.span_err(span, &err);
++            }
++        } else {
++            span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`");
++        }
++    }
++}
++
++/// Returns true if any of the method parameters is a type that implements `Drop`. The method
++/// can't be made const then, because `drop` can't be const-evaluated.
++fn method_accepts_dropable(cx: &LateContext<'_, '_>, param_tys: &[hir::Ty<'_>]) -> bool {
++    // If any of the params are dropable, return true
++    param_tys.iter().any(|hir_ty| {
++        let ty_ty = hir_ty_to_ty(cx.tcx, hir_ty);
++        has_drop(cx, ty_ty)
++    })
++}
++
++// We don't have to lint on something that's already `const`
++#[must_use]
++fn already_const(header: hir::FnHeader) -> bool {
++    header.constness == Constness::Const
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2eefb6bbaf4245b2ec30d66a2f4ff53738a24f47
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,204 @@@
++// Note: More specifically this lint is largely inspired (aka copied) from
++// *rustc*'s
++// [`missing_doc`].
++//
++// [`missing_doc`]: https://github.com/rust-lang/rust/blob/d6d05904697d89099b55da3331155392f1db9c00/src/librustc_lint/builtin.rs#L246
++//
++
++use crate::utils::span_lint;
++use if_chain::if_chain;
++use rustc_ast::ast::{self, MetaItem, MetaItemKind};
++use rustc_ast::attr;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::ty;
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Warns if there is missing doc for any documentable item
++    /// (public or private).
++    ///
++    /// **Why is this bad?** Doc is good. *rustc* has a `MISSING_DOCS`
++    /// allowed-by-default lint for
++    /// public members, but has no way to enforce documentation of private items.
++    /// This lint fixes that.
++    ///
++    /// **Known problems:** None.
++    pub MISSING_DOCS_IN_PRIVATE_ITEMS,
++    restriction,
++    "detects missing documentation for public and private members"
++}
++
++pub struct MissingDoc {
++    /// Stack of whether #[doc(hidden)] is set
++    /// at each level which has lint attributes.
++    doc_hidden_stack: Vec<bool>,
++}
++
++impl Default for MissingDoc {
++    #[must_use]
++    fn default() -> Self {
++        Self::new()
++    }
++}
++
++impl MissingDoc {
++    #[must_use]
++    pub fn new() -> Self {
++        Self {
++            doc_hidden_stack: vec![false],
++        }
++    }
++
++    fn doc_hidden(&self) -> bool {
++        *self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
++    }
++
++    fn has_include(meta: Option<MetaItem>) -> bool {
++        if_chain! {
++            if let Some(meta) = meta;
++            if let MetaItemKind::List(list) = meta.kind;
++            if let Some(meta) = list.get(0);
++            if let Some(name) = meta.ident();
++            then {
++                name.as_str() == "include"
++            } else {
++                false
++            }
++        }
++    }
++
++    fn check_missing_docs_attrs(
++        &self,
++        cx: &LateContext<'_, '_>,
++        attrs: &[ast::Attribute],
++        sp: Span,
++        desc: &'static str,
++    ) {
++        // If we're building a test harness, then warning about
++        // documentation is probably not really relevant right now.
++        if cx.sess().opts.test {
++            return;
++        }
++
++        // `#[doc(hidden)]` disables missing_docs check.
++        if self.doc_hidden() {
++            return;
++        }
++
++        if sp.from_expansion() {
++            return;
++        }
++
++        let has_doc = attrs
++            .iter()
++            .any(|a| a.is_doc_comment() || a.doc_str().is_some() || a.is_value_str() || Self::has_include(a.meta()));
++        if !has_doc {
++            span_lint(
++                cx,
++                MISSING_DOCS_IN_PRIVATE_ITEMS,
++                sp,
++                &format!("missing documentation for {}", desc),
++            );
++        }
++    }
++}
++
++impl_lint_pass!(MissingDoc => [MISSING_DOCS_IN_PRIVATE_ITEMS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
++    fn enter_lint_attrs(&mut self, _: &LateContext<'a, 'tcx>, attrs: &'tcx [ast::Attribute]) {
++        let doc_hidden = self.doc_hidden()
++            || attrs.iter().any(|attr| {
++                attr.check_name(sym!(doc))
++                    && match attr.meta_item_list() {
++                        None => false,
++                        Some(l) => attr::list_contains_name(&l[..], sym!(hidden)),
++                    }
++            });
++        self.doc_hidden_stack.push(doc_hidden);
++    }
++
++    fn exit_lint_attrs(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx [ast::Attribute]) {
++        self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
++    }
++
++    fn check_crate(&mut self, cx: &LateContext<'a, 'tcx>, krate: &'tcx hir::Crate<'_>) {
++        self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "crate");
++    }
++
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx hir::Item<'_>) {
++        let desc = match it.kind {
++            hir::ItemKind::Const(..) => "a constant",
++            hir::ItemKind::Enum(..) => "an enum",
++            hir::ItemKind::Fn(..) => {
++                // ignore main()
++                if it.ident.name == sym!(main) {
++                    let def_id = it.hir_id.owner;
++                    let def_key = cx.tcx.hir().def_key(def_id);
++                    if def_key.parent == Some(hir::def_id::CRATE_DEF_INDEX) {
++                        return;
++                    }
++                }
++                "a function"
++            },
++            hir::ItemKind::Mod(..) => "a module",
++            hir::ItemKind::Static(..) => "a static",
++            hir::ItemKind::Struct(..) => "a struct",
++            hir::ItemKind::Trait(..) => "a trait",
++            hir::ItemKind::TraitAlias(..) => "a trait alias",
++            hir::ItemKind::TyAlias(..) => "a type alias",
++            hir::ItemKind::Union(..) => "a union",
++            hir::ItemKind::OpaqueTy(..) => "an existential type",
++            hir::ItemKind::ExternCrate(..)
++            | hir::ItemKind::ForeignMod(..)
++            | hir::ItemKind::GlobalAsm(..)
++            | hir::ItemKind::Impl { .. }
++            | hir::ItemKind::Use(..) => return,
++        };
++
++        self.check_missing_docs_attrs(cx, &it.attrs, it.span, desc);
++    }
++
++    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, trait_item: &'tcx hir::TraitItem<'_>) {
++        let desc = match trait_item.kind {
++            hir::TraitItemKind::Const(..) => "an associated constant",
++            hir::TraitItemKind::Fn(..) => "a trait method",
++            hir::TraitItemKind::Type(..) => "an associated type",
++        };
++
++        self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, desc);
++    }
++
++    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
++        // If the method is an impl for a trait, don't doc.
++        let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
++        match cx.tcx.associated_item(def_id).container {
++            ty::TraitContainer(_) => return,
++            ty::ImplContainer(cid) => {
++                if cx.tcx.impl_trait_ref(cid).is_some() {
++                    return;
++                }
++            },
++        }
++
++        let desc = match impl_item.kind {
++            hir::ImplItemKind::Const(..) => "an associated constant",
++            hir::ImplItemKind::Fn(..) => "a method",
++            hir::ImplItemKind::TyAlias(_) => "an associated type",
++            hir::ImplItemKind::OpaqueTy(_) => "an existential type",
++        };
++        self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, desc);
++    }
++
++    fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, sf: &'tcx hir::StructField<'_>) {
++        if !sf.is_positional() {
++            self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a struct field");
++        }
++    }
++
++    fn check_variant(&mut self, cx: &LateContext<'a, 'tcx>, v: &'tcx hir::Variant<'_>) {
++        self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a variant");
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5300fd2215b391a7c2c40a52e30d34484893de12
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,164 @@@
++use crate::utils::span_lint;
++use rustc_ast::ast;
++use rustc_hir as hir;
++use rustc_lint::{self, LateContext, LateLintPass, LintContext};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** it lints if an exported function, method, trait method with default impl,
++    /// or trait method impl is not `#[inline]`.
++    ///
++    /// **Why is this bad?** In general, it is not. Functions can be inlined across
++    /// crates when that's profitable as long as any form of LTO is used. When LTO is disabled,
++    /// functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates
++    /// might intend for most of the methods in their public API to be able to be inlined across
++    /// crates even when LTO is disabled. For these types of crates, enabling this lint might make
++    /// sense. It allows the crate to require all exported methods to be `#[inline]` by default, and
++    /// then opt out for specific methods where this might not make sense.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// pub fn foo() {} // missing #[inline]
++    /// fn ok() {} // ok
++    /// #[inline] pub fn bar() {} // ok
++    /// #[inline(always)] pub fn baz() {} // ok
++    ///
++    /// pub trait Bar {
++    ///   fn bar(); // ok
++    ///   fn def_bar() {} // missing #[inline]
++    /// }
++    ///
++    /// struct Baz;
++    /// impl Baz {
++    ///    fn private() {} // ok
++    /// }
++    ///
++    /// impl Bar for Baz {
++    ///   fn bar() {} // ok - Baz is not exported
++    /// }
++    ///
++    /// pub struct PubBaz;
++    /// impl PubBaz {
++    ///    fn private() {} // ok
++    ///    pub fn not_ptrivate() {} // missing #[inline]
++    /// }
++    ///
++    /// impl Bar for PubBaz {
++    ///    fn bar() {} // missing #[inline]
++    ///    fn def_bar() {} // missing #[inline]
++    /// }
++    /// ```
++    pub MISSING_INLINE_IN_PUBLIC_ITEMS,
++    restriction,
++    "detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)"
++}
++
++fn check_missing_inline_attrs(cx: &LateContext<'_, '_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) {
++    let has_inline = attrs.iter().any(|a| a.check_name(sym!(inline)));
++    if !has_inline {
++        span_lint(
++            cx,
++            MISSING_INLINE_IN_PUBLIC_ITEMS,
++            sp,
++            &format!("missing `#[inline]` for {}", desc),
++        );
++    }
++}
++
++fn is_executable(cx: &LateContext<'_, '_>) -> bool {
++    use rustc_session::config::CrateType;
++
++    cx.tcx.sess.crate_types.get().iter().any(|t: &CrateType| match t {
++        CrateType::Executable => true,
++        _ => false,
++    })
++}
++
++declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline {
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx hir::Item<'_>) {
++        if rustc_middle::lint::in_external_macro(cx.sess(), it.span) || is_executable(cx) {
++            return;
++        }
++
++        if !cx.access_levels.is_exported(it.hir_id) {
++            return;
++        }
++        match it.kind {
++            hir::ItemKind::Fn(..) => {
++                let desc = "a function";
++                check_missing_inline_attrs(cx, &it.attrs, it.span, desc);
++            },
++            hir::ItemKind::Trait(ref _is_auto, ref _unsafe, ref _generics, ref _bounds, trait_items) => {
++                // note: we need to check if the trait is exported so we can't use
++                // `LateLintPass::check_trait_item` here.
++                for tit in trait_items {
++                    let tit_ = cx.tcx.hir().trait_item(tit.id);
++                    match tit_.kind {
++                        hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => {},
++                        hir::TraitItemKind::Fn(..) => {
++                            if tit.defaultness.has_value() {
++                                // trait method with default body needs inline in case
++                                // an impl is not provided
++                                let desc = "a default trait method";
++                                let item = cx.tcx.hir().expect_trait_item(tit.id.hir_id);
++                                check_missing_inline_attrs(cx, &item.attrs, item.span, desc);
++                            }
++                        },
++                    }
++                }
++            },
++            hir::ItemKind::Const(..)
++            | hir::ItemKind::Enum(..)
++            | hir::ItemKind::Mod(..)
++            | hir::ItemKind::Static(..)
++            | hir::ItemKind::Struct(..)
++            | hir::ItemKind::TraitAlias(..)
++            | hir::ItemKind::GlobalAsm(..)
++            | hir::ItemKind::TyAlias(..)
++            | hir::ItemKind::Union(..)
++            | hir::ItemKind::OpaqueTy(..)
++            | hir::ItemKind::ExternCrate(..)
++            | hir::ItemKind::ForeignMod(..)
++            | hir::ItemKind::Impl { .. }
++            | hir::ItemKind::Use(..) => {},
++        };
++    }
++
++    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
++        use rustc_middle::ty::{ImplContainer, TraitContainer};
++        if rustc_middle::lint::in_external_macro(cx.sess(), impl_item.span) || is_executable(cx) {
++            return;
++        }
++
++        // If the item being implemented is not exported, then we don't need #[inline]
++        if !cx.access_levels.is_exported(impl_item.hir_id) {
++            return;
++        }
++
++        let desc = match impl_item.kind {
++            hir::ImplItemKind::Fn(..) => "a method",
++            hir::ImplItemKind::Const(..) | hir::ImplItemKind::TyAlias(_) | hir::ImplItemKind::OpaqueTy(_) => return,
++        };
++
++        let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
++        let trait_def_id = match cx.tcx.associated_item(def_id).container {
++            TraitContainer(cid) => Some(cid),
++            ImplContainer(cid) => cx.tcx.impl_trait_ref(cid).map(|t| t.def_id),
++        };
++
++        if let Some(trait_def_id) = trait_def_id {
++            if trait_def_id.is_local() && !cx.access_levels.is_exported(impl_item.hir_id) {
++                // If a trait is being implemented for an item, and the
++                // trait is not exported, we don't need #[inline]
++                return;
++            }
++        }
++
++        check_missing_inline_attrs(cx, &impl_item.attrs, impl_item.span, desc);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4ca90455bc4d1e81b44bbd1c8fdc51e61a99561a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,148 @@@
++use crate::consts::{constant, Constant};
++use crate::utils::{sext, span_lint_and_then};
++use if_chain::if_chain;
++use rustc_hir::{BinOpKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::{self};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use std::fmt::Display;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for modulo arithemtic.
++    ///
++    /// **Why is this bad?** The results of modulo (%) operation might differ
++    /// depending on the language, when negative numbers are involved.
++    /// If you interop with different languages it might be beneficial
++    /// to double check all places that use modulo arithmetic.
++    ///
++    /// For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x = -17 % 3;
++    /// ```
++    pub MODULO_ARITHMETIC,
++    restriction,
++    "any modulo arithmetic statement"
++}
++
++declare_lint_pass!(ModuloArithmetic => [MODULO_ARITHMETIC]);
++
++struct OperandInfo {
++    string_representation: Option<String>,
++    is_negative: bool,
++    is_integral: bool,
++}
++
++fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option<OperandInfo> {
++    match constant(cx, cx.tables, operand) {
++        Some((Constant::Int(v), _)) => match cx.tables.expr_ty(expr).kind {
++            ty::Int(ity) => {
++                let value = sext(cx.tcx, v, ity);
++                return Some(OperandInfo {
++                    string_representation: Some(value.to_string()),
++                    is_negative: value < 0,
++                    is_integral: true,
++                });
++            },
++            ty::Uint(_) => {
++                return Some(OperandInfo {
++                    string_representation: None,
++                    is_negative: false,
++                    is_integral: true,
++                });
++            },
++            _ => {},
++        },
++        Some((Constant::F32(f), _)) => {
++            return Some(floating_point_operand_info(&f));
++        },
++        Some((Constant::F64(f), _)) => {
++            return Some(floating_point_operand_info(&f));
++        },
++        _ => {},
++    }
++    None
++}
++
++fn floating_point_operand_info<T: Display + PartialOrd + From<f32>>(f: &T) -> OperandInfo {
++    OperandInfo {
++        string_representation: Some(format!("{:.3}", *f)),
++        is_negative: *f < 0.0.into(),
++        is_integral: false,
++    }
++}
++
++fn might_have_negative_value(t: &ty::TyS<'_>) -> bool {
++    t.is_signed() || t.is_floating_point()
++}
++
++fn check_const_operands<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &'tcx Expr<'_>,
++    lhs_operand: &OperandInfo,
++    rhs_operand: &OperandInfo,
++) {
++    if lhs_operand.is_negative ^ rhs_operand.is_negative {
++        span_lint_and_then(
++            cx,
++            MODULO_ARITHMETIC,
++            expr.span,
++            &format!(
++                "you are using modulo operator on constants with different signs: `{} % {}`",
++                lhs_operand.string_representation.as_ref().unwrap(),
++                rhs_operand.string_representation.as_ref().unwrap()
++            ),
++            |diag| {
++                diag.note("double check for expected result especially when interoperating with different languages");
++                if lhs_operand.is_integral {
++                    diag.note("or consider using `rem_euclid` or similar function");
++                }
++            },
++        );
++    }
++}
++
++fn check_non_const_operands<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, operand: &Expr<'_>) {
++    let operand_type = cx.tables.expr_ty(operand);
++    if might_have_negative_value(operand_type) {
++        span_lint_and_then(
++            cx,
++            MODULO_ARITHMETIC,
++            expr.span,
++            "you are using modulo operator on types that might have different signs",
++            |diag| {
++                diag.note("double check for expected result especially when interoperating with different languages");
++                if operand_type.is_integral() {
++                    diag.note("or consider using `rem_euclid` or similar function");
++                }
++            },
++        );
++    }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ModuloArithmetic {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        match &expr.kind {
++            ExprKind::Binary(op, lhs, rhs) | ExprKind::AssignOp(op, lhs, rhs) => {
++                if let BinOpKind::Rem = op.node {
++                    let lhs_operand = analyze_operand(lhs, cx, expr);
++                    let rhs_operand = analyze_operand(rhs, cx, expr);
++                    if_chain! {
++                        if let Some(lhs_operand) = lhs_operand;
++                        if let Some(rhs_operand) = rhs_operand;
++                        then {
++                            check_const_operands(cx, expr, &lhs_operand, &rhs_operand);
++                        }
++                        else {
++                            check_non_const_operands(cx, expr, lhs);
++                        }
++                    }
++                };
++            },
++            _ => {},
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ed85d0315bd25d7d232de53e96c9ef97bf53ca0a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,68 @@@
++//! lint on multiple versions of a crate being used
++
++use crate::utils::{run_lints, span_lint};
++use rustc_hir::{Crate, CRATE_HIR_ID};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::DUMMY_SP;
++
++use itertools::Itertools;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks to see if multiple versions of a crate are being
++    /// used.
++    ///
++    /// **Why is this bad?** This bloats the size of targets, and can lead to
++    /// confusing error messages when structs or traits are used interchangeably
++    /// between different versions of a crate.
++    ///
++    /// **Known problems:** Because this can be caused purely by the dependencies
++    /// themselves, it's not always possible to fix this issue.
++    ///
++    /// **Example:**
++    /// ```toml
++    /// # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning.
++    /// [dependencies]
++    /// ctrlc = "=3.1.0"
++    /// ansi_term = "=0.11.0"
++    /// ```
++    pub MULTIPLE_CRATE_VERSIONS,
++    cargo,
++    "multiple versions of the same crate being used"
++}
++
++declare_lint_pass!(MultipleCrateVersions => [MULTIPLE_CRATE_VERSIONS]);
++
++impl LateLintPass<'_, '_> for MultipleCrateVersions {
++    fn check_crate(&mut self, cx: &LateContext<'_, '_>, _: &Crate<'_>) {
++        if !run_lints(cx, &[MULTIPLE_CRATE_VERSIONS], CRATE_HIR_ID) {
++            return;
++        }
++
++        let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().exec() {
++            metadata
++        } else {
++            span_lint(cx, MULTIPLE_CRATE_VERSIONS, DUMMY_SP, "could not read cargo metadata");
++
++            return;
++        };
++
++        let mut packages = metadata.packages;
++        packages.sort_by(|a, b| a.name.cmp(&b.name));
++
++        for (name, group) in &packages.into_iter().group_by(|p| p.name.clone()) {
++            let group: Vec<cargo_metadata::Package> = group.collect();
++
++            if group.len() > 1 {
++                let versions = group.into_iter().map(|p| p.version).join(", ");
++
++                span_lint(
++                    cx,
++                    MULTIPLE_CRATE_VERSIONS,
++                    DUMMY_SP,
++                    &format!("multiple versions for dependency `{}`: {}", name, versions),
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0b9b7e1b8cc1b3e8e951d2f97dc43ff080390638
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,124 @@@
++use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method, walk_ptrs_ty};
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::{Adt, Array, RawPtr, Ref, Slice, Tuple, Ty, TypeAndMut};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for sets/maps with mutable key types.
++    ///
++    /// **Why is this bad?** All of `HashMap`, `HashSet`, `BTreeMap` and
++    /// `BtreeSet` rely on either the hash or the order of keys be unchanging,
++    /// so having types with interior mutability is a bad idea.
++    ///
++    /// **Known problems:** We don't currently account for `Rc` or `Arc`, so
++    /// this may yield false positives.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// use std::cmp::{PartialEq, Eq};
++    /// use std::collections::HashSet;
++    /// use std::hash::{Hash, Hasher};
++    /// use std::sync::atomic::AtomicUsize;
++    ///# #[allow(unused)]
++    ///
++    /// struct Bad(AtomicUsize);
++    /// impl PartialEq for Bad {
++    ///     fn eq(&self, rhs: &Self) -> bool {
++    ///          ..
++    /// ; unimplemented!();
++    ///     }
++    /// }
++    ///
++    /// impl Eq for Bad {}
++    ///
++    /// impl Hash for Bad {
++    ///     fn hash<H: Hasher>(&self, h: &mut H) {
++    ///         ..
++    /// ; unimplemented!();
++    ///     }
++    /// }
++    ///
++    /// fn main() {
++    ///     let _: HashSet<Bad> = HashSet::new();
++    /// }
++    /// ```
++    pub MUTABLE_KEY_TYPE,
++    correctness,
++    "Check for mutable `Map`/`Set` key type"
++}
++
++declare_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutableKeyType {
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'tcx>) {
++        if let hir::ItemKind::Fn(ref sig, ..) = item.kind {
++            check_sig(cx, item.hir_id, &sig.decl);
++        }
++    }
++
++    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem<'tcx>) {
++        if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind {
++            if trait_ref_of_method(cx, item.hir_id).is_none() {
++                check_sig(cx, item.hir_id, &sig.decl);
++            }
++        }
++    }
++
++    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem<'tcx>) {
++        if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
++            check_sig(cx, item.hir_id, &sig.decl);
++        }
++    }
++
++    fn check_local(&mut self, cx: &LateContext<'_, '_>, local: &hir::Local<'_>) {
++        if let hir::PatKind::Wild = local.pat.kind {
++            return;
++        }
++        check_ty(cx, local.span, cx.tables.pat_ty(&*local.pat));
++    }
++}
++
++fn check_sig<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item_hir_id: hir::HirId, decl: &hir::FnDecl<'_>) {
++    let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id);
++    let fn_sig = cx.tcx.fn_sig(fn_def_id);
++    for (hir_ty, ty) in decl.inputs.iter().zip(fn_sig.inputs().skip_binder().iter()) {
++        check_ty(cx, hir_ty.span, ty);
++    }
++    check_ty(
++        cx,
++        decl.output.span(),
++        cx.tcx.erase_late_bound_regions(&fn_sig.output()),
++    );
++}
++
++// We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased
++// generics (because the compiler cannot ensure immutability for unknown types).
++fn check_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, span: Span, ty: Ty<'tcx>) {
++    let ty = walk_ptrs_ty(ty);
++    if let Adt(def, substs) = ty.kind {
++        if [&paths::HASHMAP, &paths::BTREEMAP, &paths::HASHSET, &paths::BTREESET]
++            .iter()
++            .any(|path| match_def_path(cx, def.did, &**path))
++            && is_mutable_type(cx, substs.type_at(0), span)
++        {
++            span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
++        }
++    }
++}
++
++fn is_mutable_type<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>, span: Span) -> bool {
++    match ty.kind {
++        RawPtr(TypeAndMut { ty: inner_ty, mutbl }) | Ref(_, inner_ty, mutbl) => {
++            mutbl == hir::Mutability::Mut || is_mutable_type(cx, inner_ty, span)
++        },
++        Slice(inner_ty) => is_mutable_type(cx, inner_ty, span),
++        Array(inner_ty, size) => {
++            size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) && is_mutable_type(cx, inner_ty, span)
++        },
++        Tuple(..) => ty.tuple_fields().any(|ty| is_mutable_type(cx, ty, span)),
++        Adt(..) => cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx, cx.param_env, span),
++        _ => false,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f7a20a74b85e21d2e04ddb2399c24dff18a233f8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,114 @@@
++use crate::utils::{higher, span_lint};
++use rustc_hir as hir;
++use rustc_hir::intravisit;
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for instances of `mut mut` references.
++    ///
++    /// **Why is this bad?** Multiple `mut`s don't add anything meaningful to the
++    /// source. This is either a copy'n'paste error, or it shows a fundamental
++    /// misunderstanding of references.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let mut y = 1;
++    /// let x = &mut &mut y;
++    /// ```
++    pub MUT_MUT,
++    pedantic,
++    "usage of double-mut refs, e.g., `&mut &mut ...`"
++}
++
++declare_lint_pass!(MutMut => [MUT_MUT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutMut {
++    fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx hir::Block<'_>) {
++        intravisit::walk_block(&mut MutVisitor { cx }, block);
++    }
++
++    fn check_ty(&mut self, cx: &LateContext<'a, 'tcx>, ty: &'tcx hir::Ty<'_>) {
++        use rustc_hir::intravisit::Visitor;
++
++        MutVisitor { cx }.visit_ty(ty);
++    }
++}
++
++pub struct MutVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++}
++
++impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
++        if in_external_macro(self.cx.sess(), expr.span) {
++            return;
++        }
++
++        if let Some((_, arg, body)) = higher::for_loop(expr) {
++            // A `for` loop lowers to:
++            // ```rust
++            // match ::std::iter::Iterator::next(&mut iter) {
++            // //                                ^^^^
++            // ```
++            // Let's ignore the generated code.
++            intravisit::walk_expr(self, arg);
++            intravisit::walk_expr(self, body);
++        } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, ref e) = expr.kind {
++            if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind {
++                span_lint(
++                    self.cx,
++                    MUT_MUT,
++                    expr.span,
++                    "generally you want to avoid `&mut &mut _` if possible",
++                );
++            } else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.tables.expr_ty(e).kind {
++                span_lint(
++                    self.cx,
++                    MUT_MUT,
++                    expr.span,
++                    "this expression mutably borrows a mutable reference. Consider reborrowing",
++                );
++            }
++        }
++    }
++
++    fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) {
++        if let hir::TyKind::Rptr(
++            _,
++            hir::MutTy {
++                ty: ref pty,
++                mutbl: hir::Mutability::Mut,
++            },
++        ) = ty.kind
++        {
++            if let hir::TyKind::Rptr(
++                _,
++                hir::MutTy {
++                    mutbl: hir::Mutability::Mut,
++                    ..
++                },
++            ) = pty.kind
++            {
++                span_lint(
++                    self.cx,
++                    MUT_MUT,
++                    ty.span,
++                    "generally you want to avoid `&mut &mut _` if possible",
++                );
++            }
++        }
++
++        intravisit::walk_ty(self, ty);
++    }
++    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
++        intravisit::NestedVisitorMap::None
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e5680482e5bfba5251f8151c5c869f83b2163670
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,82 @@@
++use crate::utils::span_lint;
++use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::subst::Subst;
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Detects giving a mutable reference to a function that only
++    /// requires an immutable reference.
++    ///
++    /// **Why is this bad?** The immutable reference rules out all other references
++    /// to the value. Also the code misleads about the intent of the call site.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// my_vec.push(&mut value)
++    /// ```
++    pub UNNECESSARY_MUT_PASSED,
++    style,
++    "an argument passed as a mutable reference although the callee only demands an immutable reference"
++}
++
++declare_lint_pass!(UnnecessaryMutPassed => [UNNECESSARY_MUT_PASSED]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnecessaryMutPassed {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        match e.kind {
++            ExprKind::Call(ref fn_expr, ref arguments) => {
++                if let ExprKind::Path(ref path) = fn_expr.kind {
++                    check_arguments(
++                        cx,
++                        arguments,
++                        cx.tables.expr_ty(fn_expr),
++                        &rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)),
++                    );
++                }
++            },
++            ExprKind::MethodCall(ref path, _, ref arguments) => {
++                let def_id = cx.tables.type_dependent_def_id(e.hir_id).unwrap();
++                let substs = cx.tables.node_substs(e.hir_id);
++                let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs);
++                check_arguments(cx, arguments, method_type, &path.ident.as_str())
++            },
++            _ => (),
++        }
++    }
++}
++
++fn check_arguments<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    arguments: &[Expr<'_>],
++    type_definition: Ty<'tcx>,
++    name: &str,
++) {
++    match type_definition.kind {
++        ty::FnDef(..) | ty::FnPtr(_) => {
++            let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs();
++            for (argument, parameter) in arguments.iter().zip(parameters.iter()) {
++                match parameter.kind {
++                    ty::Ref(_, _, Mutability::Not)
++                    | ty::RawPtr(ty::TypeAndMut {
++                        mutbl: Mutability::Not, ..
++                    }) => {
++                        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = argument.kind {
++                            span_lint(
++                                cx,
++                                UNNECESSARY_MUT_PASSED,
++                                argument.span,
++                                &format!("The function/method `{}` doesn't need a mutable reference", name),
++                            );
++                        }
++                    },
++                    _ => (),
++                }
++            }
++        },
++        _ => (),
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..119e0905ff442f32a5ecec979165c7951cf1681d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,159 @@@
++use crate::utils::{is_direct_expn_of, span_lint};
++use if_chain::if_chain;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability, StmtKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for function/method calls with a mutable
++    /// parameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros.
++    ///
++    /// **Why is this bad?** In release builds `debug_assert!` macros are optimized out by the
++    /// compiler.
++    /// Therefore mutating something in a `debug_assert!` macro results in different behaviour
++    /// between a release and debug build.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// debug_assert_eq!(vec![3].pop(), Some(3));
++    /// // or
++    /// fn take_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() }
++    /// debug_assert!(take_a_mut_parameter(&mut 5));
++    /// ```
++    pub DEBUG_ASSERT_WITH_MUT_CALL,
++    nursery,
++    "mutable arguments in `debug_assert{,_ne,_eq}!`"
++}
++
++declare_lint_pass!(DebugAssertWithMutCall => [DEBUG_ASSERT_WITH_MUT_CALL]);
++
++const DEBUG_MACRO_NAMES: [&str; 3] = ["debug_assert", "debug_assert_eq", "debug_assert_ne"];
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DebugAssertWithMutCall {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        for dmn in &DEBUG_MACRO_NAMES {
++            if is_direct_expn_of(e.span, dmn).is_some() {
++                if let Some(span) = extract_call(cx, e) {
++                    span_lint(
++                        cx,
++                        DEBUG_ASSERT_WITH_MUT_CALL,
++                        span,
++                        &format!("do not call a function with mutable arguments inside of `{}!`", dmn),
++                    );
++                }
++            }
++        }
++    }
++}
++
++//HACK(hellow554): remove this when #4694 is implemented
++fn extract_call<'a, 'tcx>(cx: &'a LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) -> Option<Span> {
++    if_chain! {
++        if let ExprKind::Block(ref block, _) = e.kind;
++        if block.stmts.len() == 1;
++        if let StmtKind::Semi(ref matchexpr) = block.stmts[0].kind;
++        then {
++            // debug_assert
++            if_chain! {
++                if let ExprKind::Match(ref ifclause, _, _) = matchexpr.kind;
++                if let ExprKind::DropTemps(ref droptmp) = ifclause.kind;
++                if let ExprKind::Unary(UnOp::UnNot, ref condition) = droptmp.kind;
++                then {
++                    let mut visitor = MutArgVisitor::new(cx);
++                    visitor.visit_expr(condition);
++                    return visitor.expr_span();
++                }
++            }
++
++            // debug_assert_{eq,ne}
++            if_chain! {
++                if let ExprKind::Block(ref matchblock, _) = matchexpr.kind;
++                if let Some(ref matchheader) = matchblock.expr;
++                if let ExprKind::Match(ref headerexpr, _, _) = matchheader.kind;
++                if let ExprKind::Tup(ref conditions) = headerexpr.kind;
++                if conditions.len() == 2;
++                then {
++                    if let ExprKind::AddrOf(BorrowKind::Ref, _, ref lhs) = conditions[0].kind {
++                        let mut visitor = MutArgVisitor::new(cx);
++                        visitor.visit_expr(lhs);
++                        if let Some(span) = visitor.expr_span() {
++                            return Some(span);
++                        }
++                    }
++                    if let ExprKind::AddrOf(BorrowKind::Ref, _, ref rhs) = conditions[1].kind {
++                        let mut visitor = MutArgVisitor::new(cx);
++                        visitor.visit_expr(rhs);
++                        if let Some(span) = visitor.expr_span() {
++                            return Some(span);
++                        }
++                    }
++                }
++            }
++        }
++    }
++
++    None
++}
++
++struct MutArgVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    expr_span: Option<Span>,
++    found: bool,
++}
++
++impl<'a, 'tcx> MutArgVisitor<'a, 'tcx> {
++    fn new(cx: &'a LateContext<'a, 'tcx>) -> Self {
++        Self {
++            cx,
++            expr_span: None,
++            found: false,
++        }
++    }
++
++    fn expr_span(&self) -> Option<Span> {
++        if self.found {
++            self.expr_span
++        } else {
++            None
++        }
++    }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        match expr.kind {
++            ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) => {
++                self.found = true;
++                return;
++            },
++            ExprKind::Path(_) => {
++                if let Some(adj) = self.cx.tables.adjustments().get(expr.hir_id) {
++                    if adj
++                        .iter()
++                        .any(|a| matches!(a.target.kind, ty::Ref(_, _, Mutability::Mut)))
++                    {
++                        self.found = true;
++                        return;
++                    }
++                }
++            },
++            // Don't check await desugars
++            ExprKind::Match(_, _, MatchSource::AwaitDesugar) => return,
++            _ if !self.found => self.expr_span = Some(expr.span),
++            _ => return,
++        }
++        walk_expr(self, expr)
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4e1a8be4892e6b841a9c95a7774422dc76daf15a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,88 @@@
++//! Checks for uses of mutex where an atomic value could be used
++//!
++//! This lint is **warn** by default
++
++use crate::utils::{is_type_diagnostic_item, span_lint};
++use rustc_ast::ast;
++use rustc_hir::Expr;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usages of `Mutex<X>` where an atomic will do.
++    ///
++    /// **Why is this bad?** Using a mutex just to make access to a plain bool or
++    /// reference sequential is shooting flies with cannons.
++    /// `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and
++    /// faster.
++    ///
++    /// **Known problems:** This lint cannot detect if the mutex is actually used
++    /// for waiting before a critical section.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # use std::sync::Mutex;
++    /// # let y = 1;
++    /// let x = Mutex::new(&y);
++    /// ```
++    pub MUTEX_ATOMIC,
++    perf,
++    "using a mutex where an atomic value could be used instead"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usages of `Mutex<X>` where `X` is an integral
++    /// type.
++    ///
++    /// **Why is this bad?** Using a mutex just to make access to a plain integer
++    /// sequential is
++    /// shooting flies with cannons. `std::sync::atomic::AtomicUsize` is leaner and faster.
++    ///
++    /// **Known problems:** This lint cannot detect if the mutex is actually used
++    /// for waiting before a critical section.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # use std::sync::Mutex;
++    /// let x = Mutex::new(0usize);
++    /// ```
++    pub MUTEX_INTEGER,
++    nursery,
++    "using a mutex for an integer type"
++}
++
++declare_lint_pass!(Mutex => [MUTEX_ATOMIC, MUTEX_INTEGER]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Mutex {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        let ty = cx.tables.expr_ty(expr);
++        if let ty::Adt(_, subst) = ty.kind {
++            if is_type_diagnostic_item(cx, ty, sym!(mutex_type)) {
++                let mutex_param = subst.type_at(0);
++                if let Some(atomic_name) = get_atomic_name(mutex_param) {
++                    let msg = format!(
++                        "Consider using an `{}` instead of a `Mutex` here. If you just want the locking \
++                         behavior and not the internal type, consider using `Mutex<()>`.",
++                        atomic_name
++                    );
++                    match mutex_param.kind {
++                        ty::Uint(t) if t != ast::UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, &msg),
++                        ty::Int(t) if t != ast::IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, &msg),
++                        _ => span_lint(cx, MUTEX_ATOMIC, expr.span, &msg),
++                    };
++                }
++            }
++        }
++    }
++}
++
++fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> {
++    match ty.kind {
++        ty::Bool => Some("AtomicBool"),
++        ty::Uint(_) => Some("AtomicUsize"),
++        ty::Int(_) => Some("AtomicIsize"),
++        ty::RawPtr(_) => Some("AtomicPtr"),
++        _ => None,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..efa77db822dd039215796cfd100dec22baf9160b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,348 @@@
++//! Checks for needless boolean results of if-else expressions
++//!
++//! This lint is **warn** by default
++
++use crate::utils::sugg::Sugg;
++use crate::utils::{higher, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Spanned;
++use rustc_span::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for expressions of the form `if c { true } else {
++    /// false }`
++    /// (or vice versa) and suggest using the condition directly.
++    ///
++    /// **Why is this bad?** Redundant code.
++    ///
++    /// **Known problems:** Maybe false positives: Sometimes, the two branches are
++    /// painstakingly documented (which we, of course, do not detect), so they *may*
++    /// have some value. Even then, the documentation can be rewritten to match the
++    /// shorter code.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// if x {
++    ///     false
++    /// } else {
++    ///     true
++    /// }
++    /// ```
++    /// Could be written as
++    /// ```rust,ignore
++    /// !x
++    /// ```
++    pub NEEDLESS_BOOL,
++    complexity,
++    "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for expressions of the form `x == true`,
++    /// `x != true` and order comparisons such as `x < true` (or vice versa) and
++    /// suggest using the variable directly.
++    ///
++    /// **Why is this bad?** Unnecessary code.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// if x == true {}
++    /// if y == false {}
++    /// ```
++    /// use `x` directly:
++    /// ```rust,ignore
++    /// if x {}
++    /// if !y {}
++    /// ```
++    pub BOOL_COMPARISON,
++    complexity,
++    "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
++}
++
++declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBool {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        use self::Expression::{Bool, RetBool};
++        if let Some((ref pred, ref then_block, Some(ref else_expr))) = higher::if_block(&e) {
++            let reduce = |ret, not| {
++                let mut applicability = Applicability::MachineApplicable;
++                let snip = Sugg::hir_with_applicability(cx, pred, "<predicate>", &mut applicability);
++                let mut snip = if not { !snip } else { snip };
++
++                if ret {
++                    snip = snip.make_return();
++                }
++
++                if parent_node_is_if_expr(&e, &cx) {
++                    snip = snip.blockify()
++                }
++
++                span_lint_and_sugg(
++                    cx,
++                    NEEDLESS_BOOL,
++                    e.span,
++                    "this if-then-else expression returns a bool literal",
++                    "you can reduce it to",
++                    snip.to_string(),
++                    applicability,
++                );
++            };
++            if let ExprKind::Block(ref then_block, _) = then_block.kind {
++                match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) {
++                    (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => {
++                        span_lint(
++                            cx,
++                            NEEDLESS_BOOL,
++                            e.span,
++                            "this if-then-else expression will always return true",
++                        );
++                    },
++                    (RetBool(false), RetBool(false)) | (Bool(false), Bool(false)) => {
++                        span_lint(
++                            cx,
++                            NEEDLESS_BOOL,
++                            e.span,
++                            "this if-then-else expression will always return false",
++                        );
++                    },
++                    (RetBool(true), RetBool(false)) => reduce(true, false),
++                    (Bool(true), Bool(false)) => reduce(false, false),
++                    (RetBool(false), RetBool(true)) => reduce(true, true),
++                    (Bool(false), Bool(true)) => reduce(false, true),
++                    _ => (),
++                }
++            } else {
++                panic!("IfExpr `then` node is not an `ExprKind::Block`");
++            }
++        }
++    }
++}
++
++declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoolComparison {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        if e.span.from_expansion() {
++            return;
++        }
++
++        if let ExprKind::Binary(Spanned { node, .. }, ..) = e.kind {
++            let ignore_case = None::<(fn(_) -> _, &str)>;
++            let ignore_no_literal = None::<(fn(_, _) -> _, &str)>;
++            match node {
++                BinOpKind::Eq => {
++                    let true_case = Some((|h| h, "equality checks against true are unnecessary"));
++                    let false_case = Some((
++                        |h: Sugg<'_>| !h,
++                        "equality checks against false can be replaced by a negation",
++                    ));
++                    check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal)
++                },
++                BinOpKind::Ne => {
++                    let true_case = Some((
++                        |h: Sugg<'_>| !h,
++                        "inequality checks against true can be replaced by a negation",
++                    ));
++                    let false_case = Some((|h| h, "inequality checks against false are unnecessary"));
++                    check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal)
++                },
++                BinOpKind::Lt => check_comparison(
++                    cx,
++                    e,
++                    ignore_case,
++                    Some((|h| h, "greater than checks against false are unnecessary")),
++                    Some((
++                        |h: Sugg<'_>| !h,
++                        "less than comparison against true can be replaced by a negation",
++                    )),
++                    ignore_case,
++                    Some((
++                        |l: Sugg<'_>, r: Sugg<'_>| (!l).bit_and(&r),
++                        "order comparisons between booleans can be simplified",
++                    )),
++                ),
++                BinOpKind::Gt => check_comparison(
++                    cx,
++                    e,
++                    Some((
++                        |h: Sugg<'_>| !h,
++                        "less than comparison against true can be replaced by a negation",
++                    )),
++                    ignore_case,
++                    ignore_case,
++                    Some((|h| h, "greater than checks against false are unnecessary")),
++                    Some((
++                        |l: Sugg<'_>, r: Sugg<'_>| l.bit_and(&(!r)),
++                        "order comparisons between booleans can be simplified",
++                    )),
++                ),
++                _ => (),
++            }
++        }
++    }
++}
++
++struct ExpressionInfoWithSpan {
++    one_side_is_unary_not: bool,
++    left_span: Span,
++    right_span: Span,
++}
++
++fn is_unary_not(e: &Expr<'_>) -> (bool, Span) {
++    if_chain! {
++        if let ExprKind::Unary(unop, operand) = e.kind;
++        if let UnOp::UnNot = unop;
++        then {
++            return (true, operand.span);
++        }
++    };
++    (false, e.span)
++}
++
++fn one_side_is_unary_not<'tcx>(left_side: &'tcx Expr<'_>, right_side: &'tcx Expr<'_>) -> ExpressionInfoWithSpan {
++    let left = is_unary_not(left_side);
++    let right = is_unary_not(right_side);
++
++    ExpressionInfoWithSpan {
++        one_side_is_unary_not: left.0 != right.0,
++        left_span: left.1,
++        right_span: right.1,
++    }
++}
++
++fn check_comparison<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    e: &'tcx Expr<'_>,
++    left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
++    left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
++    right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
++    right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
++    no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &str)>,
++) {
++    use self::Expression::{Bool, Other};
++
++    if let ExprKind::Binary(op, ref left_side, ref right_side) = e.kind {
++        let (l_ty, r_ty) = (cx.tables.expr_ty(left_side), cx.tables.expr_ty(right_side));
++        if l_ty.is_bool() && r_ty.is_bool() {
++            let mut applicability = Applicability::MachineApplicable;
++
++            if let BinOpKind::Eq = op.node {
++                let expression_info = one_side_is_unary_not(&left_side, &right_side);
++                if expression_info.one_side_is_unary_not {
++                    span_lint_and_sugg(
++                        cx,
++                        BOOL_COMPARISON,
++                        e.span,
++                        "This comparison might be written more concisely",
++                        "try simplifying it as shown",
++                        format!(
++                            "{} != {}",
++                            snippet_with_applicability(cx, expression_info.left_span, "..", &mut applicability),
++                            snippet_with_applicability(cx, expression_info.right_span, "..", &mut applicability)
++                        ),
++                        applicability,
++                    )
++                }
++            }
++
++            match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
++                (Bool(true), Other) => left_true.map_or((), |(h, m)| {
++                    suggest_bool_comparison(cx, e, right_side, applicability, m, h)
++                }),
++                (Other, Bool(true)) => right_true.map_or((), |(h, m)| {
++                    suggest_bool_comparison(cx, e, left_side, applicability, m, h)
++                }),
++                (Bool(false), Other) => left_false.map_or((), |(h, m)| {
++                    suggest_bool_comparison(cx, e, right_side, applicability, m, h)
++                }),
++                (Other, Bool(false)) => right_false.map_or((), |(h, m)| {
++                    suggest_bool_comparison(cx, e, left_side, applicability, m, h)
++                }),
++                (Other, Other) => no_literal.map_or((), |(h, m)| {
++                    let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability);
++                    let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability);
++                    span_lint_and_sugg(
++                        cx,
++                        BOOL_COMPARISON,
++                        e.span,
++                        m,
++                        "try simplifying it as shown",
++                        h(left_side, right_side).to_string(),
++                        applicability,
++                    )
++                }),
++                _ => (),
++            }
++        }
++    }
++}
++
++fn suggest_bool_comparison<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    e: &'tcx Expr<'_>,
++    expr: &Expr<'_>,
++    mut applicability: Applicability,
++    message: &str,
++    conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
++) {
++    let hint = Sugg::hir_with_applicability(cx, expr, "..", &mut applicability);
++    span_lint_and_sugg(
++        cx,
++        BOOL_COMPARISON,
++        e.span,
++        message,
++        "try simplifying it as shown",
++        conv_hint(hint).to_string(),
++        applicability,
++    );
++}
++
++enum Expression {
++    Bool(bool),
++    RetBool(bool),
++    Other,
++}
++
++fn fetch_bool_block(block: &Block<'_>) -> Expression {
++    match (&*block.stmts, block.expr.as_ref()) {
++        (&[], Some(e)) => fetch_bool_expr(&**e),
++        (&[ref e], None) => {
++            if let StmtKind::Semi(ref e) = e.kind {
++                if let ExprKind::Ret(_) = e.kind {
++                    fetch_bool_expr(&**e)
++                } else {
++                    Expression::Other
++                }
++            } else {
++                Expression::Other
++            }
++        },
++        _ => Expression::Other,
++    }
++}
++
++fn fetch_bool_expr(expr: &Expr<'_>) -> Expression {
++    match expr.kind {
++        ExprKind::Block(ref block, _) => fetch_bool_block(block),
++        ExprKind::Lit(ref lit_ptr) => {
++            if let LitKind::Bool(value) = lit_ptr.node {
++                Expression::Bool(value)
++            } else {
++                Expression::Other
++            }
++        },
++        ExprKind::Ret(Some(ref expr)) => match fetch_bool_expr(expr) {
++            Expression::Bool(value) => Expression::RetBool(value),
++            _ => Expression::Other,
++        },
++        _ => Expression::Other,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9ee875d7516eba3d29e70f4a372f8797f3ed85ce
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,124 @@@
++//! Checks for needless address of operations (`&`)
++//!
++//! This lint is **warn** by default
++
++use crate::utils::{snippet_opt, span_lint_and_then};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, HirId, Item, Mutability, Pat, PatKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_middle::ty::adjustment::{Adjust, Adjustment};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for address of operations (`&`) that are going to
++    /// be dereferenced immediately by the compiler.
++    ///
++    /// **Why is this bad?** Suggests that the receiver of the expression borrows
++    /// the expression.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x: &i32 = &&&&&&5;
++    /// ```
++    ///
++    /// **Known problems:** None.
++    pub NEEDLESS_BORROW,
++    nursery,
++    "taking a reference that is going to be automatically dereferenced"
++}
++
++#[derive(Default)]
++pub struct NeedlessBorrow {
++    derived_item: Option<HirId>,
++}
++
++impl_lint_pass!(NeedlessBorrow => [NEEDLESS_BORROW]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBorrow {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        if e.span.from_expansion() || self.derived_item.is_some() {
++            return;
++        }
++        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind {
++            if let ty::Ref(..) = cx.tables.expr_ty(inner).kind {
++                for adj3 in cx.tables.expr_adjustments(e).windows(3) {
++                    if let [Adjustment {
++                        kind: Adjust::Deref(_), ..
++                    }, Adjustment {
++                        kind: Adjust::Deref(_), ..
++                    }, Adjustment {
++                        kind: Adjust::Borrow(_),
++                        ..
++                    }] = *adj3
++                    {
++                        span_lint_and_then(
++                            cx,
++                            NEEDLESS_BORROW,
++                            e.span,
++                            "this expression borrows a reference that is immediately dereferenced \
++                             by the compiler",
++                            |diag| {
++                                if let Some(snippet) = snippet_opt(cx, inner.span) {
++                                    diag.span_suggestion(
++                                        e.span,
++                                        "change this to",
++                                        snippet,
++                                        Applicability::MachineApplicable,
++                                    );
++                                }
++                            },
++                        );
++                    }
++                }
++            }
++        }
++    }
++    fn check_pat(&mut self, cx: &LateContext<'a, 'tcx>, pat: &'tcx Pat<'_>) {
++        if pat.span.from_expansion() || self.derived_item.is_some() {
++            return;
++        }
++        if_chain! {
++            if let PatKind::Binding(BindingAnnotation::Ref, .., name, _) = pat.kind;
++            if let ty::Ref(_, tam, mutbl) = cx.tables.pat_ty(pat).kind;
++            if mutbl == Mutability::Not;
++            if let ty::Ref(_, _, mutbl) = tam.kind;
++            // only lint immutable refs, because borrowed `&mut T` cannot be moved out
++            if mutbl == Mutability::Not;
++            then {
++                span_lint_and_then(
++                    cx,
++                    NEEDLESS_BORROW,
++                    pat.span,
++                    "this pattern creates a reference to a reference",
++                    |diag| {
++                        if let Some(snippet) = snippet_opt(cx, name.span) {
++                            diag.span_suggestion(
++                                pat.span,
++                                "change this to",
++                                snippet,
++                                Applicability::MachineApplicable,
++                            );
++                        }
++                    }
++                )
++            }
++        }
++    }
++
++    fn check_item(&mut self, _: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++        if item.attrs.iter().any(|a| a.check_name(sym!(automatically_derived))) {
++            debug_assert!(self.derived_item.is_none());
++            self.derived_item = Some(item.hir_id);
++        }
++    }
++
++    fn check_item_post(&mut self, _: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++        if let Some(id) = self.derived_item {
++            if item.hir_id == id {
++                self.derived_item = None;
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e56489c6d434d609829f2d8676417d5c922fd98d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,92 @@@
++//! Checks for useless borrowed references.
++//!
++//! This lint is **warn** by default
++
++use crate::utils::{snippet_with_applicability, span_lint_and_then};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for useless borrowed references.
++    ///
++    /// **Why is this bad?** It is mostly useless and make the code look more
++    /// complex than it
++    /// actually is.
++    ///
++    /// **Known problems:** It seems that the `&ref` pattern is sometimes useful.
++    /// For instance in the following snippet:
++    /// ```rust,ignore
++    /// enum Animal {
++    ///     Cat(u64),
++    ///     Dog(u64),
++    /// }
++    ///
++    /// fn foo(a: &Animal, b: &Animal) {
++    ///     match (a, b) {
++    ///         (&Animal::Cat(v), k) | (k, &Animal::Cat(v)) => (), // lifetime mismatch error
++    ///         (&Animal::Dog(ref c), &Animal::Dog(_)) => ()
++    ///     }
++    /// }
++    /// ```
++    /// There is a lifetime mismatch error for `k` (indeed a and b have distinct
++    /// lifetime).
++    /// This can be fixed by using the `&ref` pattern.
++    /// However, the code can also be fixed by much cleaner ways
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let mut v = Vec::<String>::new();
++    /// let _ = v.iter_mut().filter(|&ref a| a.is_empty());
++    /// ```
++    /// This closure takes a reference on something that has been matched as a
++    /// reference and
++    /// de-referenced.
++    /// As such, it could just be |a| a.is_empty()
++    pub NEEDLESS_BORROWED_REFERENCE,
++    complexity,
++    "taking a needless borrowed reference"
++}
++
++declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBorrowedRef {
++    fn check_pat(&mut self, cx: &LateContext<'a, 'tcx>, pat: &'tcx Pat<'_>) {
++        if pat.span.from_expansion() {
++            // OK, simple enough, lints doesn't check in macro.
++            return;
++        }
++
++        if_chain! {
++            // Only lint immutable refs, because `&mut ref T` may be useful.
++            if let PatKind::Ref(ref sub_pat, Mutability::Not) = pat.kind;
++
++            // Check sub_pat got a `ref` keyword (excluding `ref mut`).
++            if let PatKind::Binding(BindingAnnotation::Ref, .., spanned_name, _) = sub_pat.kind;
++            let parent_id = cx.tcx.hir().get_parent_node(pat.hir_id);
++            if let Some(parent_node) = cx.tcx.hir().find(parent_id);
++            then {
++                // do not recurse within patterns, as they may have other references
++                // XXXManishearth we can relax this constraint if we only check patterns
++                // with a single ref pattern inside them
++                if let Node::Pat(_) = parent_node {
++                    return;
++                }
++                let mut applicability = Applicability::MachineApplicable;
++                span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, pat.span,
++                                   "this pattern takes a reference on something that is being de-referenced",
++                                   |diag| {
++                                       let hint = snippet_with_applicability(cx, spanned_name.span, "..", &mut applicability).into_owned();
++                                       diag.span_suggestion(
++                                           pat.span,
++                                           "try removing the `&ref` part and just keep",
++                                           hint,
++                                           applicability,
++                                       );
++                                   });
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..28183810df48977e3c776f568b58ab92633b6479
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,463 @@@
++//! Checks for continue statements in loops that are redundant.
++//!
++//! For example, the lint would catch
++//!
++//! ```rust
++//! let mut a = 1;
++//! let x = true;
++//!
++//! while a < 5 {
++//!     a = 6;
++//!     if x {
++//!         // ...
++//!     } else {
++//!         continue;
++//!     }
++//!     println!("Hello, world");
++//! }
++//! ```
++//!
++//! And suggest something like this:
++//!
++//! ```rust
++//! let mut a = 1;
++//! let x = true;
++//!
++//! while a < 5 {
++//!     a = 6;
++//!     if x {
++//!         // ...
++//!         println!("Hello, world");
++//!     }
++//! }
++//! ```
++//!
++//! This lint is **warn** by default.
++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
++    /// `else`-blocks, when omitting the `else`-block possibly with some
++    /// rearrangement of code can make the code easier to understand.
++    ///
++    /// **Why is this bad?** Having explicit `else` blocks for `if` statements
++    /// containing `continue` in their THEN branch adds unnecessary branching and
++    /// nesting to the code. Having an else block containing just `continue` can
++    /// also be better written by grouping the statements following the whole `if`
++    /// statement within the THEN block and omitting the else block completely.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # fn condition() -> bool { false }
++    /// # fn update_condition() {}
++    /// # let x = false;
++    /// while condition() {
++    ///     update_condition();
++    ///     if x {
++    ///         // ...
++    ///     } else {
++    ///         continue;
++    ///     }
++    ///     println!("Hello, world");
++    /// }
++    /// ```
++    ///
++    /// Could be rewritten as
++    ///
++    /// ```rust
++    /// # fn condition() -> bool { false }
++    /// # fn update_condition() {}
++    /// # let x = false;
++    /// while condition() {
++    ///     update_condition();
++    ///     if x {
++    ///         // ...
++    ///         println!("Hello, world");
++    ///     }
++    /// }
++    /// ```
++    ///
++    /// As another example, the following code
++    ///
++    /// ```rust
++    /// # fn waiting() -> bool { false }
++    /// loop {
++    ///     if waiting() {
++    ///         continue;
++    ///     } else {
++    ///         // Do something useful
++    ///     }
++    ///     # break;
++    /// }
++    /// ```
++    /// Could be rewritten as
++    ///
++    /// ```rust
++    /// # fn waiting() -> bool { false }
++    /// loop {
++    ///     if waiting() {
++    ///         continue;
++    ///     }
++    ///     // Do something useful
++    ///     # break;
++    /// }
++    /// ```
++    pub NEEDLESS_CONTINUE,
++    pedantic,
++    "`continue` statements that can be replaced by a rearrangement of code"
++}
++
++declare_lint_pass!(NeedlessContinue => [NEEDLESS_CONTINUE]);
++
++impl EarlyLintPass for NeedlessContinue {
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
++        if !expr.span.from_expansion() {
++            check_and_warn(cx, expr);
++        }
++    }
++}
++
++/* This lint has to mainly deal with two cases of needless continue
++ * statements. */
++// Case 1 [Continue inside else block]:
++//
++//     loop {
++//         // region A
++//         if cond {
++//             // region B
++//         } else {
++//             continue;
++//         }
++//         // region C
++//     }
++//
++// This code can better be written as follows:
++//
++//     loop {
++//         // region A
++//         if cond {
++//             // region B
++//             // region C
++//         }
++//     }
++//
++// Case 2 [Continue inside then block]:
++//
++//     loop {
++//       // region A
++//       if cond {
++//           continue;
++//           // potentially more code here.
++//       } else {
++//           // region B
++//       }
++//       // region C
++//     }
++//
++//
++// This snippet can be refactored to:
++//
++//     loop {
++//       // region A
++//       if !cond {
++//           // region B
++//           // region C
++//       }
++//     }
++//
++
++/// Given an expression, returns true if either of the following is true
++///
++/// - The expression is a `continue` node.
++/// - The expression node is a block with the first statement being a
++/// `continue`.
++fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool {
++    match else_expr.kind {
++        ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label),
++        ast::ExprKind::Continue(l) => compare_labels(label, l.as_ref()),
++        _ => false,
++    }
++}
++
++fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) -> bool {
++    block.stmts.get(0).map_or(false, |stmt| match stmt.kind {
++        ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
++            if let ast::ExprKind::Continue(ref l) = e.kind {
++                compare_labels(label, l.as_ref())
++            } else {
++                false
++            }
++        },
++        _ => false,
++    })
++}
++
++/// If the `continue` has a label, check it matches the label of the loop.
++fn compare_labels(loop_label: Option<&ast::Label>, continue_label: Option<&ast::Label>) -> bool {
++    match (loop_label, continue_label) {
++        // `loop { continue; }` or `'a loop { continue; }`
++        (_, None) => true,
++        // `loop { continue 'a; }`
++        (None, _) => false,
++        // `'a loop { continue 'a; }` or `'a loop { continue 'b; }`
++        (Some(x), Some(y)) => x.ident == y.ident,
++    }
++}
++
++/// If `expr` is a loop expression (while/while let/for/loop), calls `func` with
++/// the AST object representing the loop block of `expr`.
++fn with_loop_block<F>(expr: &ast::Expr, mut func: F)
++where
++    F: FnMut(&ast::Block, Option<&ast::Label>),
++{
++    if let ast::ExprKind::While(_, loop_block, label)
++    | ast::ExprKind::ForLoop(_, _, loop_block, label)
++    | ast::ExprKind::Loop(loop_block, label) = &expr.kind
++    {
++        func(loop_block, label.as_ref());
++    }
++}
++
++/// If `stmt` is an if expression node with an `else` branch, calls func with
++/// the
++/// following:
++///
++/// - The `if` expression itself,
++/// - The `if` condition expression,
++/// - The `then` block, and
++/// - The `else` expression.
++fn with_if_expr<F>(stmt: &ast::Stmt, mut func: F)
++where
++    F: FnMut(&ast::Expr, &ast::Expr, &ast::Block, &ast::Expr),
++{
++    match stmt.kind {
++        ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
++            if let ast::ExprKind::If(ref cond, ref if_block, Some(ref else_expr)) = e.kind {
++                func(e, cond, if_block, else_expr);
++            }
++        },
++        _ => {},
++    }
++}
++
++/// A type to distinguish between the two distinct cases this lint handles.
++#[derive(Copy, Clone, Debug)]
++enum LintType {
++    ContinueInsideElseBlock,
++    ContinueInsideThenBlock,
++}
++
++/// Data we pass around for construction of help messages.
++struct LintData<'a> {
++    /// The `if` expression encountered in the above loop.
++    if_expr: &'a ast::Expr,
++    /// The condition expression for the above `if`.
++    if_cond: &'a ast::Expr,
++    /// The `then` block of the `if` statement.
++    if_block: &'a ast::Block,
++    /// The `else` block of the `if` statement.
++    /// Note that we only work with `if` exprs that have an `else` branch.
++    else_expr: &'a ast::Expr,
++    /// The 0-based index of the `if` statement in the containing loop block.
++    stmt_idx: usize,
++    /// The statements of the loop block.
++    block_stmts: &'a [ast::Stmt],
++}
++
++const MSG_REDUNDANT_ELSE_BLOCK: &str = "this `else` block is redundant";
++
++const MSG_ELSE_BLOCK_NOT_NEEDED: &str = "there is no need for an explicit `else` block for this `if` \
++                                         expression";
++
++const DROP_ELSE_BLOCK_AND_MERGE_MSG: &str = "consider dropping the `else` clause and merging the code that \
++                                             follows (in the loop) with the `if` block";
++
++const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause";
++
++fn emit_warning<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, typ: LintType) {
++    // snip    is the whole *help* message that appears after the warning.
++    // message is the warning message.
++    // expr    is the expression which the lint warning message refers to.
++    let (snip, message, expr) = match typ {
++        LintType::ContinueInsideElseBlock => (
++            suggestion_snippet_for_continue_inside_else(cx, data),
++            MSG_REDUNDANT_ELSE_BLOCK,
++            data.else_expr,
++        ),
++        LintType::ContinueInsideThenBlock => (
++            suggestion_snippet_for_continue_inside_if(cx, data),
++            MSG_ELSE_BLOCK_NOT_NEEDED,
++            data.if_expr,
++        ),
++    };
++    span_lint_and_help(
++        cx,
++        NEEDLESS_CONTINUE,
++        expr.span,
++        message,
++        None,
++        &format!("{}\n{}", header, snip),
++    );
++}
++
++fn suggestion_snippet_for_continue_inside_if<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String {
++    let cond_code = snippet(cx, data.if_cond.span, "..");
++
++    let continue_code = snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span));
++
++    let else_code = snippet_block(cx, data.else_expr.span, "..", Some(data.if_expr.span));
++
++    let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0);
++    format!(
++        "{indent}if {} {}\n{indent}{}",
++        cond_code,
++        continue_code,
++        else_code,
++        indent = " ".repeat(indent_if),
++    )
++}
++
++fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String {
++    let cond_code = snippet(cx, data.if_cond.span, "..");
++
++    // Region B
++    let block_code = erode_from_back(&snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span)));
++
++    // Region C
++    // These is the code in the loop block that follows the if/else construction
++    // we are complaining about. We want to pull all of this code into the
++    // `then` block of the `if` statement.
++    let indent = span_of_first_expr_in_block(data.if_block)
++        .and_then(|span| indent_of(cx, span))
++        .unwrap_or(0);
++    let to_annex = data.block_stmts[data.stmt_idx + 1..]
++        .iter()
++        .map(|stmt| original_sp(stmt.span, DUMMY_SP))
++        .map(|span| {
++            let snip = snippet_block(cx, span, "..", None).into_owned();
++            snip.lines()
++                .map(|line| format!("{}{}", " ".repeat(indent), line))
++                .collect::<Vec<_>>()
++                .join("\n")
++        })
++        .collect::<Vec<_>>()
++        .join("\n");
++
++    let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0);
++    format!(
++        "{indent_if}if {} {}\n{indent}// merged code follows:\n{}\n{indent_if}}}",
++        cond_code,
++        block_code,
++        to_annex,
++        indent = " ".repeat(indent),
++        indent_if = " ".repeat(indent_if),
++    )
++}
++
++fn check_and_warn<'a>(cx: &EarlyContext<'_>, expr: &'a ast::Expr) {
++    with_loop_block(expr, |loop_block, label| {
++        for (i, stmt) in loop_block.stmts.iter().enumerate() {
++            with_if_expr(stmt, |if_expr, cond, then_block, else_expr| {
++                let data = &LintData {
++                    stmt_idx: i,
++                    if_expr,
++                    if_cond: cond,
++                    if_block: then_block,
++                    else_expr,
++                    block_stmts: &loop_block.stmts,
++                };
++                if needless_continue_in_else(else_expr, label) {
++                    emit_warning(
++                        cx,
++                        data,
++                        DROP_ELSE_BLOCK_AND_MERGE_MSG,
++                        LintType::ContinueInsideElseBlock,
++                    );
++                } else if is_first_block_stmt_continue(then_block, label) {
++                    emit_warning(cx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock);
++                }
++            });
++        }
++    });
++}
++
++/// Eats at `s` from the end till a closing brace `}` is encountered, and then continues eating
++/// till a non-whitespace character is found.  e.g., the string. If no closing `}` is present, the
++/// string will be preserved.
++///
++/// ```rust
++/// {
++///     let x = 5;
++/// }
++/// ```
++///
++/// is transformed to
++///
++/// ```ignore
++///     {
++///         let x = 5;
++/// ```
++#[must_use]
++fn erode_from_back(s: &str) -> String {
++    let mut ret = s.to_string();
++    while ret.pop().map_or(false, |c| c != '}') {}
++    while let Some(c) = ret.pop() {
++        if !c.is_whitespace() {
++            ret.push(c);
++            break;
++        }
++    }
++    if ret.is_empty() {
++        s.to_string()
++    } else {
++        ret
++    }
++}
++
++fn span_of_first_expr_in_block(block: &ast::Block) -> Option<Span> {
++    block.stmts.iter().next().map(|stmt| stmt.span)
++}
++
++#[cfg(test)]
++mod test {
++    use super::erode_from_back;
++
++    #[test]
++    #[rustfmt::skip]
++    fn test_erode_from_back() {
++        let input = "\
++{
++    let x = 5;
++    let y = format!(\"{}\", 42);
++}";
++
++        let expected = "\
++{
++    let x = 5;
++    let y = format!(\"{}\", 42);";
++
++        let got = erode_from_back(input);
++        assert_eq!(expected, got);
++    }
++
++    #[test]
++    #[rustfmt::skip]
++    fn test_erode_from_back_no_brace() {
++        let input = "\
++let x = 5;
++let y = something();
++";
++        let expected = input;
++        let got = erode_from_back(input);
++        assert_eq!(expected, got);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a21818701dacc2fd059bb7387a3dc3f572adc4ea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,347 @@@
++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 if_chain::if_chain;
++use rustc_ast::ast::Attribute;
++use rustc_data_structures::fx::{FxHashMap, FxHashSet};
++use rustc_errors::{Applicability, DiagnosticBuilder};
++use rustc_hir::intravisit::FnKind;
++use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, ItemKind, Node, PatKind, QPath, TyKind};
++use rustc_infer::infer::TyCtxtInferExt;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::{self, TypeFoldable};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::Span;
++use rustc_target::spec::abi::Abi;
++use rustc_trait_selection::traits;
++use rustc_trait_selection::traits::misc::can_type_implement_copy;
++use rustc_typeck::expr_use_visitor as euv;
++use std::borrow::Cow;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for functions taking arguments by value, but not
++    /// consuming them in its
++    /// body.
++    ///
++    /// **Why is this bad?** Taking arguments by reference is more flexible and can
++    /// sometimes avoid
++    /// unnecessary allocations.
++    ///
++    /// **Known problems:**
++    /// * This lint suggests taking an argument by reference,
++    /// however sometimes it is better to let users decide the argument type
++    /// (by using `Borrow` trait, for example), depending on how the function is used.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// fn foo(v: Vec<i32>) {
++    ///     assert_eq!(v.len(), 42);
++    /// }
++    /// ```
++    ///
++    /// ```rust
++    /// // should be
++    /// fn foo(v: &[i32]) {
++    ///     assert_eq!(v.len(), 42);
++    /// }
++    /// ```
++    pub NEEDLESS_PASS_BY_VALUE,
++    pedantic,
++    "functions taking arguments by value, but not consuming them in its body"
++}
++
++declare_lint_pass!(NeedlessPassByValue => [NEEDLESS_PASS_BY_VALUE]);
++
++macro_rules! need {
++    ($e: expr) => {
++        if let Some(x) = $e {
++            x
++        } else {
++            return;
++        }
++    };
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
++    #[allow(clippy::too_many_lines)]
++    fn check_fn(
++        &mut self,
++        cx: &LateContext<'a, 'tcx>,
++        kind: FnKind<'tcx>,
++        decl: &'tcx FnDecl<'_>,
++        body: &'tcx Body<'_>,
++        span: Span,
++        hir_id: HirId,
++    ) {
++        if span.from_expansion() {
++            return;
++        }
++
++        match kind {
++            FnKind::ItemFn(.., header, _, attrs) => {
++                if header.abi != Abi::Rust || requires_exact_signature(attrs) {
++                    return;
++                }
++            },
++            FnKind::Method(..) => (),
++            _ => return,
++        }
++
++        // Exclude non-inherent impls
++        if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
++            if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } |
++                ItemKind::Trait(..))
++            {
++                return;
++            }
++        }
++
++        // Allow `Borrow` or functions to be taken by value
++        let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT));
++        let whitelisted_traits = [
++            need!(cx.tcx.lang_items().fn_trait()),
++            need!(cx.tcx.lang_items().fn_once_trait()),
++            need!(cx.tcx.lang_items().fn_mut_trait()),
++            need!(get_trait_def_id(cx, &paths::RANGE_ARGUMENT_TRAIT)),
++        ];
++
++        let sized_trait = need!(cx.tcx.lang_items().sized_trait());
++
++        let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
++
++        let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds.iter().copied())
++            .filter(|p| !p.is_global())
++            .filter_map(|obligation| {
++                if let ty::Predicate::Trait(poly_trait_ref, _) = obligation.predicate {
++                    if poly_trait_ref.def_id() == sized_trait || poly_trait_ref.skip_binder().has_escaping_bound_vars()
++                    {
++                        return None;
++                    }
++                    Some(poly_trait_ref)
++                } else {
++                    None
++                }
++            })
++            .collect::<Vec<_>>();
++
++        // Collect moved variables and spans which will need dereferencings from the
++        // function body.
++        let MovedVariablesCtxt {
++            moved_vars,
++            spans_need_deref,
++            ..
++        } = {
++            let mut ctx = MovedVariablesCtxt::default();
++            cx.tcx.infer_ctxt().enter(|infcx| {
++                euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.tables).consume_body(body);
++            });
++            ctx
++        };
++
++        let fn_sig = cx.tcx.fn_sig(fn_def_id);
++        let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);
++
++        for (idx, ((input, &ty), arg)) in decl.inputs.iter().zip(fn_sig.inputs()).zip(body.params).enumerate() {
++            // All spans generated from a proc-macro invocation are the same...
++            if span == input.span {
++                return;
++            }
++
++            // Ignore `self`s.
++            if idx == 0 {
++                if let PatKind::Binding(.., ident, _) = arg.pat.kind {
++                    if ident.as_str() == "self" {
++                        continue;
++                    }
++                }
++            }
++
++            //
++            // * Exclude a type that is specifically bounded by `Borrow`.
++            // * Exclude a type whose reference also fulfills its bound. (e.g., `std::convert::AsRef`,
++            //   `serde::Serialize`)
++            let (implements_borrow_trait, all_borrowable_trait) = {
++                let preds = preds
++                    .iter()
++                    .filter(|t| t.skip_binder().self_ty() == ty)
++                    .collect::<Vec<_>>();
++
++                (
++                    preds.iter().any(|t| t.def_id() == borrow_trait),
++                    !preds.is_empty() && {
++                        let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_root_empty, ty);
++                        preds.iter().all(|t| {
++                            let ty_params = &t
++                                .skip_binder()
++                                .trait_ref
++                                .substs
++                                .iter()
++                                .skip(1)
++                                .cloned()
++                                .collect::<Vec<_>>();
++                            implements_trait(cx, ty_empty_region, t.def_id(), ty_params)
++                        })
++                    },
++                )
++            };
++
++            if_chain! {
++                if !is_self(arg);
++                if !ty.is_mutable_ptr();
++                if !is_copy(cx, ty);
++                if !whitelisted_traits.iter().any(|&t| implements_trait(cx, ty, t, &[]));
++                if !implements_borrow_trait;
++                if !all_borrowable_trait;
++
++                if let PatKind::Binding(mode, canonical_id, ..) = arg.pat.kind;
++                if !moved_vars.contains(&canonical_id);
++                then {
++                    if mode == BindingAnnotation::Mutable || mode == BindingAnnotation::RefMut {
++                        continue;
++                    }
++
++                    // Dereference suggestion
++                    let sugg = |diag: &mut DiagnosticBuilder<'_>| {
++                        if let ty::Adt(def, ..) = ty.kind {
++                            if let Some(span) = cx.tcx.hir().span_if_local(def.did) {
++                                if can_type_implement_copy(cx.tcx, cx.param_env, ty).is_ok() {
++                                    diag.span_help(span, "consider marking this type as `Copy`");
++                                }
++                            }
++                        }
++
++                        let deref_span = spans_need_deref.get(&canonical_id);
++                        if_chain! {
++                            if is_type_diagnostic_item(cx, ty, sym!(vec_type));
++                            if let Some(clone_spans) =
++                                get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]);
++                            if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind;
++                            if let Some(elem_ty) = path.segments.iter()
++                                .find(|seg| seg.ident.name == sym!(Vec))
++                                .and_then(|ps| ps.args.as_ref())
++                                .map(|params| params.args.iter().find_map(|arg| match arg {
++                                    GenericArg::Type(ty) => Some(ty),
++                                    _ => None,
++                                }).unwrap());
++                            then {
++                                let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
++                                diag.span_suggestion(
++                                    input.span,
++                                    "consider changing the type to",
++                                    slice_ty,
++                                    Applicability::Unspecified,
++                                );
++
++                                for (span, suggestion) in clone_spans {
++                                    diag.span_suggestion(
++                                        span,
++                                        &snippet_opt(cx, span)
++                                            .map_or(
++                                                "change the call to".into(),
++                                                |x| Cow::from(format!("change `{}` to", x)),
++                                            ),
++                                        suggestion.into(),
++                                        Applicability::Unspecified,
++                                    );
++                                }
++
++                                // cannot be destructured, no need for `*` suggestion
++                                assert!(deref_span.is_none());
++                                return;
++                            }
++                        }
++
++                        if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
++                            if let Some(clone_spans) =
++                                get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
++                                diag.span_suggestion(
++                                    input.span,
++                                    "consider changing the type to",
++                                    "&str".to_string(),
++                                    Applicability::Unspecified,
++                                );
++
++                                for (span, suggestion) in clone_spans {
++                                    diag.span_suggestion(
++                                        span,
++                                        &snippet_opt(cx, span)
++                                            .map_or(
++                                                "change the call to".into(),
++                                                |x| Cow::from(format!("change `{}` to", x))
++                                            ),
++                                        suggestion.into(),
++                                        Applicability::Unspecified,
++                                    );
++                                }
++
++                                assert!(deref_span.is_none());
++                                return;
++                            }
++                        }
++
++                        let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];
++
++                        // Suggests adding `*` to dereference the added reference.
++                        if let Some(deref_span) = deref_span {
++                            spans.extend(
++                                deref_span
++                                    .iter()
++                                    .cloned()
++                                    .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
++                            );
++                            spans.sort_by_key(|&(span, _)| span);
++                        }
++                        multispan_sugg(diag, "consider taking a reference instead".to_string(), spans);
++                    };
++
++                    span_lint_and_then(
++                        cx,
++                        NEEDLESS_PASS_BY_VALUE,
++                        input.span,
++                        "this argument is passed by value, but not consumed in the function body",
++                        sugg,
++                    );
++                }
++            }
++        }
++    }
++}
++
++/// Functions marked with these attributes must have the exact signature.
++fn requires_exact_signature(attrs: &[Attribute]) -> bool {
++    attrs.iter().any(|attr| {
++        [sym!(proc_macro), sym!(proc_macro_attribute), sym!(proc_macro_derive)]
++            .iter()
++            .any(|&allow| attr.check_name(allow))
++    })
++}
++
++#[derive(Default)]
++struct MovedVariablesCtxt {
++    moved_vars: FxHashSet<HirId>,
++    /// Spans which need to be prefixed with `*` for dereferencing the
++    /// suggested additional reference.
++    spans_need_deref: FxHashMap<HirId, FxHashSet<Span>>,
++}
++
++impl MovedVariablesCtxt {
++    fn move_common(&mut self, cmt: &euv::Place<'_>) {
++        if let euv::PlaceBase::Local(vid) = cmt.base {
++            self.moved_vars.insert(vid);
++        }
++    }
++}
++
++impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt {
++    fn consume(&mut self, cmt: &euv::Place<'tcx>, mode: euv::ConsumeMode) {
++        if let euv::ConsumeMode::Move = mode {
++            self.move_common(cmt);
++        }
++    }
++
++    fn borrow(&mut self, _: &euv::Place<'tcx>, _: ty::BorrowKind) {}
++
++    fn mutate(&mut self, _: &euv::Place<'tcx>) {}
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4b2586877e562a0f84b6a88308b22ddeeb4d7bf6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++use crate::utils::span_lint;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for needlessly including a base struct on update
++    /// when all fields are changed anyway.
++    ///
++    /// **Why is this bad?** This will cost resources (because the base has to be
++    /// somewhere), and make the code less readable.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # struct Point {
++    /// #     x: i32,
++    /// #     y: i32,
++    /// #     z: i32,
++    /// # }
++    /// # let zero_point = Point { x: 0, y: 0, z: 0 };
++    /// Point {
++    ///     x: 1,
++    ///     y: 1,
++    ///     ..zero_point
++    /// };
++    /// ```
++    pub NEEDLESS_UPDATE,
++    complexity,
++    "using `Foo { ..base }` when there are no missing fields"
++}
++
++declare_lint_pass!(NeedlessUpdate => [NEEDLESS_UPDATE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessUpdate {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if let ExprKind::Struct(_, ref fields, Some(ref base)) = expr.kind {
++            let ty = cx.tables.expr_ty(expr);
++            if let ty::Adt(def, _) = ty.kind {
++                if fields.len() == def.non_enum_variant().fields.len() {
++                    span_lint(
++                        cx,
++                        NEEDLESS_UPDATE,
++                        base.span,
++                        "struct update has no effect, all the fields in the struct have already been specified",
++                    );
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..54536ed57d3e9ba9753a7e4152b70fdf94eb797e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,91 @@@
++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
++    /// `PartialOrd` (e.g., `f64`).
++    ///
++    /// **Why is this bad?**
++    /// These operators make it easy to forget that the underlying types actually allow not only three
++    /// potential Orderings (Less, Equal, Greater) but also a fourth one (Uncomparable). This is
++    /// especially easy to miss if the operator based comparison result is negated.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// use std::cmp::Ordering;
++    ///
++    /// // Bad
++    /// let a = 1.0;
++    /// let b = f64::NAN;
++    ///
++    /// let _not_less_or_equal = !(a <= b);
++    ///
++    /// // Good
++    /// let a = 1.0;
++    /// let b = f64::NAN;
++    ///
++    /// let _not_less_or_equal = match a.partial_cmp(&b) {
++    ///     None | Some(Ordering::Greater) => true,
++    ///     _ => false,
++    /// };
++    /// ```
++    pub NEG_CMP_OP_ON_PARTIAL_ORD,
++    complexity,
++    "The use of negated comparison operators on partially ordered types may produce confusing code."
++}
++
++declare_lint_pass!(NoNegCompOpForPartialOrd => [NEG_CMP_OP_ON_PARTIAL_ORD]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NoNegCompOpForPartialOrd {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if_chain! {
++
++            if !in_external_macro(cx.sess(), expr.span);
++            if let ExprKind::Unary(UnOp::UnNot, ref inner) = expr.kind;
++            if let ExprKind::Binary(ref op, ref left, _) = inner.kind;
++            if let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node;
++
++            then {
++
++                let ty = cx.tables.expr_ty(left);
++
++                let implements_ord = {
++                    if let Some(id) = utils::get_trait_def_id(cx, &paths::ORD) {
++                        utils::implements_trait(cx, ty, id, &[])
++                    } else {
++                        return;
++                    }
++                };
++
++                let implements_partial_ord = {
++                    if let Some(id) = cx.tcx.lang_items().partial_ord_trait() {
++                        utils::implements_trait(cx, ty, id, &[])
++                    } else {
++                        return;
++                    }
++                };
++
++                if implements_partial_ord && !implements_ord {
++                    span_lint(
++                        cx,
++                        NEG_CMP_OP_ON_PARTIAL_ORD,
++                        expr.span,
++                        "The use of negated comparison operators on partially ordered \
++                        types produces code that is hard to read and refactor. Please \
++                        consider using the `partial_cmp` method instead, to make it \
++                        clear that the two values could be incomparable."
++                    )
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4681e990df88a1880efa4484c8aceb1ffb2ca5f5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++use if_chain::if_chain;
++use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++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.
++    ///
++    /// **Why is this bad?** It's more readable to just negate.
++    ///
++    /// **Known problems:** This only catches integers (for now).
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// x * -1
++    /// ```
++    pub NEG_MULTIPLY,
++    style,
++    "multiplying integers with `-1`"
++}
++
++declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]);
++
++#[allow(clippy::match_same_arms)]
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NegMultiply {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        if let ExprKind::Binary(ref op, ref left, ref right) = e.kind {
++            if BinOpKind::Mul == op.node {
++                match (&left.kind, &right.kind) {
++                    (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {},
++                    (&ExprKind::Unary(UnOp::UnNeg, ref lit), _) => check_mul(cx, e.span, lit, right),
++                    (_, &ExprKind::Unary(UnOp::UnNeg, ref lit)) => check_mul(cx, e.span, lit, left),
++                    _ => {},
++                }
++            }
++        }
++    }
++}
++
++fn check_mul(cx: &LateContext<'_, '_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
++    if_chain! {
++        if let ExprKind::Lit(ref l) = lit.kind;
++        if let Constant::Int(1) = consts::lit_to_constant(&l.node, cx.tables.expr_ty_opt(lit));
++        if cx.tables.expr_ty(exp).is_integral();
++        then {
++            span_lint(cx, NEG_MULTIPLY, span, "Negation by multiplying with `-1`");
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a599667b8d8a8a9f7214c0774f24b3658abe817d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,234 @@@
++use crate::utils::paths;
++use crate::utils::sugg::DiagnosticBuilderExt;
++use crate::utils::{get_trait_def_id, implements_trait, return_ty, same_tys, span_lint_hir_and_then};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::def_id::DefId;
++use rustc_hir::HirIdSet;
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for types with a `fn new() -> Self` method and no
++    /// implementation of
++    /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html).
++    ///
++    /// It detects both the case when a manual
++    /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
++    /// implementation is required and also when it can be created with
++    /// `#[derive(Default)]`
++    ///
++    /// **Why is this bad?** The user might expect to be able to use
++    /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the
++    /// type can be constructed without arguments.
++    ///
++    /// **Known problems:** Hopefully none.
++    ///
++    /// **Example:**
++    ///
++    /// ```ignore
++    /// struct Foo(Bar);
++    ///
++    /// impl Foo {
++    ///     fn new() -> Self {
++    ///         Foo(Bar::new())
++    ///     }
++    /// }
++    /// ```
++    ///
++    /// Instead, use:
++    ///
++    /// ```ignore
++    /// struct Foo(Bar);
++    ///
++    /// impl Default for Foo {
++    ///     fn default() -> Self {
++    ///         Foo(Bar::new())
++    ///     }
++    /// }
++    /// ```
++    ///
++    /// Or, if
++    /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
++    /// can be derived by `#[derive(Default)]`:
++    ///
++    /// ```rust
++    /// struct Foo;
++    ///
++    /// impl Foo {
++    ///     fn new() -> Self {
++    ///         Foo
++    ///     }
++    /// }
++    /// ```
++    ///
++    /// Instead, use:
++    ///
++    /// ```rust
++    /// #[derive(Default)]
++    /// struct Foo;
++    ///
++    /// impl Foo {
++    ///     fn new() -> Self {
++    ///         Foo
++    ///     }
++    /// }
++    /// ```
++    ///
++    /// You can also have `new()` call `Default::default()`.
++    pub NEW_WITHOUT_DEFAULT,
++    style,
++    "`fn new() -> Self` method without `Default` implementation"
++}
++
++#[derive(Clone, Default)]
++pub struct NewWithoutDefault {
++    impling_types: Option<HirIdSet>,
++}
++
++impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault {
++    #[allow(clippy::too_many_lines)]
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) {
++        if let hir::ItemKind::Impl {
++            of_trait: None, items, ..
++        } = item.kind
++        {
++            for assoc_item in items {
++                if let hir::AssocItemKind::Fn { has_self: false } = assoc_item.kind {
++                    let impl_item = cx.tcx.hir().impl_item(assoc_item.id);
++                    if in_external_macro(cx.sess(), impl_item.span) {
++                        return;
++                    }
++                    if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind {
++                        let name = impl_item.ident.name;
++                        let id = impl_item.hir_id;
++                        if sig.header.constness == hir::Constness::Const {
++                            // can't be implemented by default
++                            return;
++                        }
++                        if sig.header.unsafety == hir::Unsafety::Unsafe {
++                            // can't be implemented for unsafe new
++                            return;
++                        }
++                        if impl_item.generics.params.iter().any(|gen| match gen.kind {
++                            hir::GenericParamKind::Type { .. } => true,
++                            _ => false,
++                        }) {
++                            // when the result of `new()` depends on a type parameter we should not require
++                            // an
++                            // impl of `Default`
++                            return;
++                        }
++                        if sig.decl.inputs.is_empty() && name == sym!(new) && cx.access_levels.is_reachable(id) {
++                            let self_did = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id));
++                            let self_ty = cx.tcx.type_of(self_did);
++                            if_chain! {
++                                if same_tys(cx, self_ty, return_ty(cx, id));
++                                if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT);
++                                then {
++                                    if self.impling_types.is_none() {
++                                        let mut impls = HirIdSet::default();
++                                        cx.tcx.for_each_impl(default_trait_id, |d| {
++                                            if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() {
++                                                if let Some(local_def_id) = ty_def.did.as_local() {
++                                                    impls.insert(cx.tcx.hir().as_local_hir_id(local_def_id));
++                                                }
++                                            }
++                                        });
++                                        self.impling_types = Some(impls);
++                                    }
++
++                                    // Check if a Default implementation exists for the Self type, regardless of
++                                    // generics
++                                    if_chain! {
++                                        if let Some(ref impling_types) = self.impling_types;
++                                        if let Some(self_def) = cx.tcx.type_of(self_did).ty_adt_def();
++                                        if let Some(self_def_id) = self_def.did.as_local();
++                                        then {
++                                            let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_def_id);
++                                            if impling_types.contains(&self_id) {
++                                                return;
++                                            }
++                                        }
++                                    }
++
++                                    if let Some(sp) = can_derive_default(self_ty, cx, default_trait_id) {
++                                        span_lint_hir_and_then(
++                                            cx,
++                                            NEW_WITHOUT_DEFAULT,
++                                            id,
++                                            impl_item.span,
++                                            &format!(
++                                                "you should consider deriving a `Default` implementation for `{}`",
++                                                self_ty
++                                            ),
++                                            |diag| {
++                                                diag.suggest_item_with_attr(
++                                                    cx,
++                                                    sp,
++                                                    "try this",
++                                                    "#[derive(Default)]",
++                                                    Applicability::MaybeIncorrect,
++                                                );
++                                            });
++                                    } else {
++                                        span_lint_hir_and_then(
++                                            cx,
++                                            NEW_WITHOUT_DEFAULT,
++                                            id,
++                                            impl_item.span,
++                                            &format!(
++                                                "you should consider adding a `Default` implementation for `{}`",
++                                                self_ty
++                                            ),
++                                            |diag| {
++                                                diag.suggest_prepend_item(
++                                                    cx,
++                                                    item.span,
++                                                    "try this",
++                                                    &create_new_without_default_suggest_msg(self_ty),
++                                                    Applicability::MaybeIncorrect,
++                                                );
++                                            },
++                                        );
++                                    }
++                                }
++                            }
++                        }
++                    }
++                }
++            }
++        }
++    }
++}
++
++fn create_new_without_default_suggest_msg(ty: Ty<'_>) -> String {
++    #[rustfmt::skip]
++    format!(
++"impl Default for {} {{
++    fn default() -> Self {{
++        Self::new()
++    }}
++}}", ty)
++}
++
++fn can_derive_default<'t, 'c>(ty: Ty<'t>, cx: &LateContext<'c, 't>, default_trait_id: DefId) -> Option<Span> {
++    match ty.kind {
++        ty::Adt(adt_def, substs) if adt_def.is_struct() => {
++            for field in adt_def.all_fields() {
++                let f_ty = field.ty(cx.tcx, substs);
++                if !implements_trait(cx, f_ty, default_trait_id, &[]) {
++                    return None;
++                }
++            }
++            Some(cx.tcx.def_span(adt_def.did))
++        },
++        _ => None,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b8bfa676a16088cd6b412626308b033b833ddb74
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,177 @@@
++use crate::utils::{has_drop, qpath_res, snippet_opt, span_lint, span_lint_and_sugg};
++use rustc_errors::Applicability;
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::{BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use std::ops::Deref;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for statements which have no effect.
++    ///
++    /// **Why is this bad?** Similar to dead code, these statements are actually
++    /// executed. However, as they have no effect, all they do is make the code less
++    /// readable.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// 0;
++    /// ```
++    pub NO_EFFECT,
++    complexity,
++    "statements with no effect"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for expression statements that can be reduced to a
++    /// sub-expression.
++    ///
++    /// **Why is this bad?** Expressions by themselves often have no side-effects.
++    /// Having such expressions reduces readability.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// compute_array()[0];
++    /// ```
++    pub UNNECESSARY_OPERATION,
++    complexity,
++    "outer expressions with no effect"
++}
++
++fn has_no_effect(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++    if expr.span.from_expansion() {
++        return false;
++    }
++    match expr.kind {
++        ExprKind::Lit(..) | ExprKind::Closure(..) => true,
++        ExprKind::Path(..) => !has_drop(cx, cx.tables.expr_ty(expr)),
++        ExprKind::Index(ref a, ref b) | ExprKind::Binary(_, ref a, ref b) => {
++            has_no_effect(cx, a) && has_no_effect(cx, b)
++        },
++        ExprKind::Array(ref v) | ExprKind::Tup(ref v) => v.iter().all(|val| has_no_effect(cx, val)),
++        ExprKind::Repeat(ref inner, _)
++        | ExprKind::Cast(ref inner, _)
++        | ExprKind::Type(ref inner, _)
++        | ExprKind::Unary(_, ref inner)
++        | ExprKind::Field(ref inner, _)
++        | ExprKind::AddrOf(_, _, ref inner)
++        | ExprKind::Box(ref inner) => has_no_effect(cx, inner),
++        ExprKind::Struct(_, ref fields, ref base) => {
++            !has_drop(cx, cx.tables.expr_ty(expr))
++                && fields.iter().all(|field| has_no_effect(cx, &field.expr))
++                && base.as_ref().map_or(true, |base| has_no_effect(cx, base))
++        },
++        ExprKind::Call(ref callee, ref args) => {
++            if let ExprKind::Path(ref qpath) = callee.kind {
++                let res = qpath_res(cx, qpath, callee.hir_id);
++                match res {
++                    Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..) => {
++                        !has_drop(cx, cx.tables.expr_ty(expr)) && args.iter().all(|arg| has_no_effect(cx, arg))
++                    },
++                    _ => false,
++                }
++            } else {
++                false
++            }
++        },
++        ExprKind::Block(ref block, _) => {
++            block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| has_no_effect(cx, expr))
++        },
++        _ => false,
++    }
++}
++
++declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NoEffect {
++    fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) {
++        if let StmtKind::Semi(ref expr) = stmt.kind {
++            if has_no_effect(cx, expr) {
++                span_lint(cx, NO_EFFECT, stmt.span, "statement with no effect");
++            } else if let Some(reduced) = reduce_expression(cx, expr) {
++                let mut snippet = String::new();
++                for e in reduced {
++                    if e.span.from_expansion() {
++                        return;
++                    }
++                    if let Some(snip) = snippet_opt(cx, e.span) {
++                        snippet.push_str(&snip);
++                        snippet.push(';');
++                    } else {
++                        return;
++                    }
++                }
++                span_lint_and_sugg(
++                    cx,
++                    UNNECESSARY_OPERATION,
++                    stmt.span,
++                    "statement can be reduced",
++                    "replace it with",
++                    snippet,
++                    Applicability::MachineApplicable,
++                );
++            }
++        }
++    }
++}
++
++fn reduce_expression<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'a>) -> Option<Vec<&'a Expr<'a>>> {
++    if expr.span.from_expansion() {
++        return None;
++    }
++    match expr.kind {
++        ExprKind::Index(ref a, ref b) => Some(vec![&**a, &**b]),
++        ExprKind::Binary(ref binop, ref a, ref b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => {
++            Some(vec![&**a, &**b])
++        },
++        ExprKind::Array(ref v) | ExprKind::Tup(ref v) => Some(v.iter().collect()),
++        ExprKind::Repeat(ref inner, _)
++        | ExprKind::Cast(ref inner, _)
++        | ExprKind::Type(ref inner, _)
++        | ExprKind::Unary(_, ref inner)
++        | ExprKind::Field(ref inner, _)
++        | ExprKind::AddrOf(_, _, ref inner)
++        | ExprKind::Box(ref inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
++        ExprKind::Struct(_, ref fields, ref base) => {
++            if has_drop(cx, cx.tables.expr_ty(expr)) {
++                None
++            } else {
++                Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
++            }
++        },
++        ExprKind::Call(ref callee, ref args) => {
++            if let ExprKind::Path(ref qpath) = callee.kind {
++                let res = qpath_res(cx, qpath, callee.hir_id);
++                match res {
++                    Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(..), _)
++                        if !has_drop(cx, cx.tables.expr_ty(expr)) =>
++                    {
++                        Some(args.iter().collect())
++                    },
++                    _ => None,
++                }
++            } else {
++                None
++            }
++        },
++        ExprKind::Block(ref block, _) => {
++            if block.stmts.is_empty() {
++                block.expr.as_ref().and_then(|e| {
++                    match block.rules {
++                        BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => None,
++                        BlockCheckMode::DefaultBlock => Some(vec![&**e]),
++                        // in case of compiler-inserted signaling blocks
++                        _ => reduce_expression(cx, e),
++                    }
++                })
++            } else {
++                None
++            }
++        },
++        _ => None,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bb257e5a542d98632f9f25c31bac6342c72504b7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,261 @@@
++//! Checks for uses of const which the type is not `Freeze` (`Cell`-free).
++//!
++//! This lint is **deny** by default.
++
++use std::ptr;
++
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass, Lint};
++use rustc_middle::ty::adjustment::Adjust;
++use rustc_middle::ty::{Ty, TypeFlags};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::{InnerSpan, Span, DUMMY_SP};
++use rustc_typeck::hir_ty_to_ty;
++
++use crate::utils::{in_constant, is_copy, qpath_res, span_lint_and_then};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for declaration of `const` items which is interior
++    /// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.).
++    ///
++    /// **Why is this bad?** Consts are copied everywhere they are referenced, i.e.,
++    /// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
++    /// or `AtomicXxxx` will be created, which defeats the whole purpose of using
++    /// these types in the first place.
++    ///
++    /// The `const` should better be replaced by a `static` item if a global
++    /// variable is wanted, or replaced by a `const fn` if a constructor is wanted.
++    ///
++    /// **Known problems:** A "non-constant" const item is a legacy way to supply an
++    /// initialized value to downstream `static` items (e.g., the
++    /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,
++    /// and this lint should be suppressed.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
++    ///
++    /// // Bad.
++    /// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);
++    /// CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged
++    /// assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct
++    ///
++    /// // Good.
++    /// static STATIC_ATOM: AtomicUsize = AtomicUsize::new(15);
++    /// STATIC_ATOM.store(9, SeqCst);
++    /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
++    /// ```
++    pub DECLARE_INTERIOR_MUTABLE_CONST,
++    correctness,
++    "declaring `const` with interior mutability"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks if `const` items which is interior mutable (e.g.,
++    /// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly.
++    ///
++    /// **Why is this bad?** Consts are copied everywhere they are referenced, i.e.,
++    /// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
++    /// or `AtomicXxxx` will be created, which defeats the whole purpose of using
++    /// these types in the first place.
++    ///
++    /// The `const` value should be stored inside a `static` item.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// ```rust
++    /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
++    /// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);
++    ///
++    /// // Bad.
++    /// CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged
++    /// assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct
++    ///
++    /// // Good.
++    /// static STATIC_ATOM: AtomicUsize = CONST_ATOM;
++    /// STATIC_ATOM.store(9, SeqCst);
++    /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
++    /// ```
++    pub BORROW_INTERIOR_MUTABLE_CONST,
++    correctness,
++    "referencing `const` with interior mutability"
++}
++
++#[allow(dead_code)]
++#[derive(Copy, Clone)]
++enum Source {
++    Item { item: Span },
++    Assoc { item: Span, ty: Span },
++    Expr { expr: Span },
++}
++
++impl Source {
++    #[must_use]
++    fn lint(&self) -> (&'static Lint, &'static str, Span) {
++        match self {
++            Self::Item { item } | Self::Assoc { item, .. } => (
++                DECLARE_INTERIOR_MUTABLE_CONST,
++                "a `const` item should never be interior mutable",
++                *item,
++            ),
++            Self::Expr { expr } => (
++                BORROW_INTERIOR_MUTABLE_CONST,
++                "a `const` item with interior mutability should not be borrowed",
++                *expr,
++            ),
++        }
++    }
++}
++
++fn verify_ty_bound<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>, source: Source) {
++    if ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP) || is_copy(cx, ty) {
++        // An `UnsafeCell` is `!Copy`, and an `UnsafeCell` is also the only type which
++        // is `!Freeze`, thus if our type is `Copy` we can be sure it must be `Freeze`
++        // as well.
++        return;
++    }
++
++    let (lint, msg, span) = source.lint();
++    span_lint_and_then(cx, lint, span, msg, |diag| {
++        if span.from_expansion() {
++            return; // Don't give suggestions into macros.
++        }
++        match source {
++            Source::Item { .. } => {
++                let const_kw_span = span.from_inner(InnerSpan::new(0, 5));
++                diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)");
++            },
++            Source::Assoc { ty: ty_span, .. } => {
++                if ty.flags.intersects(TypeFlags::HAS_FREE_LOCAL_NAMES) {
++                    diag.span_label(ty_span, &format!("consider requiring `{}` to be `Copy`", ty));
++                }
++            },
++            Source::Expr { .. } => {
++                diag.help("assign this const to a local or static variable, and use the variable here");
++            },
++        }
++    });
++}
++
++declare_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonCopyConst {
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx Item<'_>) {
++        if let ItemKind::Const(hir_ty, ..) = &it.kind {
++            let ty = hir_ty_to_ty(cx.tcx, hir_ty);
++            verify_ty_bound(cx, ty, Source::Item { item: it.span });
++        }
++    }
++
++    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, trait_item: &'tcx TraitItem<'_>) {
++        if let TraitItemKind::Const(hir_ty, ..) = &trait_item.kind {
++            let ty = hir_ty_to_ty(cx.tcx, hir_ty);
++            verify_ty_bound(
++                cx,
++                ty,
++                Source::Assoc {
++                    ty: hir_ty.span,
++                    item: trait_item.span,
++                },
++            );
++        }
++    }
++
++    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx ImplItem<'_>) {
++        if let ImplItemKind::Const(hir_ty, ..) = &impl_item.kind {
++            let item_hir_id = cx.tcx.hir().get_parent_node(impl_item.hir_id);
++            let item = cx.tcx.hir().expect_item(item_hir_id);
++            // Ensure the impl is an inherent impl.
++            if let ItemKind::Impl { of_trait: None, .. } = item.kind {
++                let ty = hir_ty_to_ty(cx.tcx, hir_ty);
++                verify_ty_bound(
++                    cx,
++                    ty,
++                    Source::Assoc {
++                        ty: hir_ty.span,
++                        item: impl_item.span,
++                    },
++                );
++            }
++        }
++    }
++
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if let ExprKind::Path(qpath) = &expr.kind {
++            // Only lint if we use the const item inside a function.
++            if in_constant(cx, expr.hir_id) {
++                return;
++            }
++
++            // Make sure it is a const item.
++            match qpath_res(cx, qpath, expr.hir_id) {
++                Res::Def(DefKind::Const | DefKind::AssocConst, _) => {},
++                _ => return,
++            };
++
++            // Climb up to resolve any field access and explicit referencing.
++            let mut cur_expr = expr;
++            let mut dereferenced_expr = expr;
++            let mut needs_check_adjustment = true;
++            loop {
++                let parent_id = cx.tcx.hir().get_parent_node(cur_expr.hir_id);
++                if parent_id == cur_expr.hir_id {
++                    break;
++                }
++                if let Some(Node::Expr(parent_expr)) = cx.tcx.hir().find(parent_id) {
++                    match &parent_expr.kind {
++                        ExprKind::AddrOf(..) => {
++                            // `&e` => `e` must be referenced.
++                            needs_check_adjustment = false;
++                        },
++                        ExprKind::Field(..) => {
++                            dereferenced_expr = parent_expr;
++                            needs_check_adjustment = true;
++                        },
++                        ExprKind::Index(e, _) if ptr::eq(&**e, cur_expr) => {
++                            // `e[i]` => desugared to `*Index::index(&e, i)`,
++                            // meaning `e` must be referenced.
++                            // no need to go further up since a method call is involved now.
++                            needs_check_adjustment = false;
++                            break;
++                        },
++                        ExprKind::Unary(UnOp::UnDeref, _) => {
++                            // `*e` => desugared to `*Deref::deref(&e)`,
++                            // meaning `e` must be referenced.
++                            // no need to go further up since a method call is involved now.
++                            needs_check_adjustment = false;
++                            break;
++                        },
++                        _ => break,
++                    }
++                    cur_expr = parent_expr;
++                } else {
++                    break;
++                }
++            }
++
++            let ty = if needs_check_adjustment {
++                let adjustments = cx.tables.expr_adjustments(dereferenced_expr);
++                if let Some(i) = adjustments.iter().position(|adj| match adj.kind {
++                    Adjust::Borrow(_) | Adjust::Deref(_) => true,
++                    _ => false,
++                }) {
++                    if i == 0 {
++                        cx.tables.expr_ty(dereferenced_expr)
++                    } else {
++                        adjustments[i - 1].target
++                    }
++                } else {
++                    // No borrow adjustments means the entire const is moved.
++                    return;
++                }
++            } else {
++                cx.tables.expr_ty(dereferenced_expr)
++            };
++
++            verify_ty_bound(cx, ty, Source::Expr { expr: expr.span });
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..45809b359866168f5c5cde87a15d1c4cc749b846
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,410 @@@
++use crate::utils::{span_lint, span_lint_and_then};
++use rustc_ast::ast::{
++    Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Ident, Item, ItemKind, Local, MacCall, Pat, PatKind,
++};
++use rustc_ast::attr;
++use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::Span;
++use rustc_span::symbol::SymbolStr;
++use std::cmp::Ordering;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for names that are very similar and thus confusing.
++    ///
++    /// **Why is this bad?** It's hard to distinguish between names that differ only
++    /// by a single character.
++    ///
++    /// **Known problems:** None?
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// let checked_exp = something;
++    /// let checked_expr = something_else;
++    /// ```
++    pub SIMILAR_NAMES,
++    pedantic,
++    "similarly named items and bindings"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for too many variables whose name consists of a
++    /// single character.
++    ///
++    /// **Why is this bad?** It's hard to memorize what a variable means without a
++    /// descriptive name.
++    ///
++    /// **Known problems:** None?
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// let (a, b, c, d, e, f, g) = (...);
++    /// ```
++    pub MANY_SINGLE_CHAR_NAMES,
++    style,
++    "too many single character bindings"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks if you have variables whose name consists of just
++    /// underscores and digits.
++    ///
++    /// **Why is this bad?** It's hard to memorize what a variable means without a
++    /// descriptive name.
++    ///
++    /// **Known problems:** None?
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let _1 = 1;
++    /// let ___1 = 1;
++    /// let __1___2 = 11;
++    /// ```
++    pub JUST_UNDERSCORES_AND_DIGITS,
++    style,
++    "unclear name"
++}
++
++#[derive(Copy, Clone)]
++pub struct NonExpressiveNames {
++    pub single_char_binding_names_threshold: u64,
++}
++
++impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]);
++
++struct ExistingName {
++    interned: SymbolStr,
++    span: Span,
++    len: usize,
++    whitelist: &'static [&'static str],
++}
++
++struct SimilarNamesLocalVisitor<'a, 'tcx> {
++    names: Vec<ExistingName>,
++    cx: &'a EarlyContext<'tcx>,
++    lint: &'a NonExpressiveNames,
++
++    /// A stack of scopes containing the single-character bindings in each scope.
++    single_char_names: Vec<Vec<Ident>>,
++}
++
++impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
++    fn check_single_char_names(&self) {
++        let num_single_char_names = self.single_char_names.iter().flatten().count();
++        let threshold = self.lint.single_char_binding_names_threshold;
++        if num_single_char_names as u64 > threshold {
++            let span = self
++                .single_char_names
++                .iter()
++                .flatten()
++                .map(|ident| ident.span)
++                .collect::<Vec<_>>();
++            span_lint(
++                self.cx,
++                MANY_SINGLE_CHAR_NAMES,
++                span,
++                &format!(
++                    "{} bindings with single-character names in scope",
++                    num_single_char_names
++                ),
++            );
++        }
++    }
++}
++
++// this list contains lists of names that are allowed to be similar
++// the assumption is that no name is ever contained in multiple lists.
++#[rustfmt::skip]
++const WHITELIST: &[&[&str]] = &[
++    &["parsed", "parser"],
++    &["lhs", "rhs"],
++    &["tx", "rx"],
++    &["set", "get"],
++    &["args", "arms"],
++    &["qpath", "path"],
++    &["lit", "lint"],
++];
++
++struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);
++
++impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
++    fn visit_pat(&mut self, pat: &'tcx Pat) {
++        match pat.kind {
++            PatKind::Ident(_, ident, _) => self.check_ident(ident),
++            PatKind::Struct(_, ref fields, _) => {
++                for field in fields {
++                    if !field.is_shorthand {
++                        self.visit_pat(&field.pat);
++                    }
++                }
++            },
++            // just go through the first pattern, as either all patterns
++            // bind the same bindings or rustc would have errored much earlier
++            PatKind::Or(ref pats) => self.visit_pat(&pats[0]),
++            _ => walk_pat(self, pat),
++        }
++    }
++    fn visit_mac(&mut self, _mac: &MacCall) {
++        // do not check macs
++    }
++}
++
++#[must_use]
++fn get_whitelist(interned_name: &str) -> Option<&'static [&'static str]> {
++    for &allow in WHITELIST {
++        if whitelisted(interned_name, allow) {
++            return Some(allow);
++        }
++    }
++    None
++}
++
++#[must_use]
++fn whitelisted(interned_name: &str, list: &[&str]) -> bool {
++    list.iter()
++        .any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name))
++}
++
++impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
++    fn check_short_ident(&mut self, ident: Ident) {
++        // Ignore shadowing
++        if self
++            .0
++            .single_char_names
++            .iter()
++            .flatten()
++            .any(|id| id.name == ident.name)
++        {
++            return;
++        }
++
++        if let Some(scope) = &mut self.0.single_char_names.last_mut() {
++            scope.push(ident);
++        }
++    }
++
++    #[allow(clippy::too_many_lines)]
++    fn check_ident(&mut self, ident: Ident) {
++        let interned_name = ident.name.as_str();
++        if interned_name.chars().any(char::is_uppercase) {
++            return;
++        }
++        if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
++            span_lint(
++                self.0.cx,
++                JUST_UNDERSCORES_AND_DIGITS,
++                ident.span,
++                "consider choosing a more descriptive name",
++            );
++            return;
++        }
++        let count = interned_name.chars().count();
++        if count < 3 {
++            if count == 1 {
++                self.check_short_ident(ident);
++            }
++            return;
++        }
++        for existing_name in &self.0.names {
++            if whitelisted(&interned_name, existing_name.whitelist) {
++                continue;
++            }
++            let mut split_at = None;
++            match existing_name.len.cmp(&count) {
++                Ordering::Greater => {
++                    if existing_name.len - count != 1 || levenstein_not_1(&interned_name, &existing_name.interned) {
++                        continue;
++                    }
++                },
++                Ordering::Less => {
++                    if count - existing_name.len != 1 || levenstein_not_1(&existing_name.interned, &interned_name) {
++                        continue;
++                    }
++                },
++                Ordering::Equal => {
++                    let mut interned_chars = interned_name.chars();
++                    let mut existing_chars = existing_name.interned.chars();
++                    let first_i = interned_chars.next().expect("we know we have at least one char");
++                    let first_e = existing_chars.next().expect("we know we have at least one char");
++                    let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();
++
++                    if eq_or_numeric((first_i, first_e)) {
++                        let last_i = interned_chars.next_back().expect("we know we have at least two chars");
++                        let last_e = existing_chars.next_back().expect("we know we have at least two chars");
++                        if eq_or_numeric((last_i, last_e)) {
++                            if interned_chars
++                                .zip(existing_chars)
++                                .filter(|&ie| !eq_or_numeric(ie))
++                                .count()
++                                != 1
++                            {
++                                continue;
++                            }
++                        } else {
++                            let second_last_i = interned_chars
++                                .next_back()
++                                .expect("we know we have at least three chars");
++                            let second_last_e = existing_chars
++                                .next_back()
++                                .expect("we know we have at least three chars");
++                            if !eq_or_numeric((second_last_i, second_last_e))
++                                || second_last_i == '_'
++                                || !interned_chars.zip(existing_chars).all(eq_or_numeric)
++                            {
++                                // allowed similarity foo_x, foo_y
++                                // or too many chars differ (foo_x, boo_y) or (foox, booy)
++                                continue;
++                            }
++                            split_at = interned_name.char_indices().rev().next().map(|(i, _)| i);
++                        }
++                    } else {
++                        let second_i = interned_chars.next().expect("we know we have at least two chars");
++                        let second_e = existing_chars.next().expect("we know we have at least two chars");
++                        if !eq_or_numeric((second_i, second_e))
++                            || second_i == '_'
++                            || !interned_chars.zip(existing_chars).all(eq_or_numeric)
++                        {
++                            // allowed similarity x_foo, y_foo
++                            // or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
++                            continue;
++                        }
++                        split_at = interned_name.chars().next().map(char::len_utf8);
++                    }
++                },
++            }
++            span_lint_and_then(
++                self.0.cx,
++                SIMILAR_NAMES,
++                ident.span,
++                "binding's name is too similar to existing binding",
++                |diag| {
++                    diag.span_note(existing_name.span, "existing binding defined here");
++                    if let Some(split) = split_at {
++                        diag.span_help(
++                            ident.span,
++                            &format!(
++                                "separate the discriminating character by an \
++                                 underscore like: `{}_{}`",
++                                &interned_name[..split],
++                                &interned_name[split..]
++                            ),
++                        );
++                    }
++                },
++            );
++            return;
++        }
++        self.0.names.push(ExistingName {
++            whitelist: get_whitelist(&interned_name).unwrap_or(&[]),
++            interned: interned_name,
++            span: ident.span,
++            len: count,
++        });
++    }
++}
++
++impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
++    /// ensure scoping rules work
++    fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) {
++        let n = self.names.len();
++        let single_char_count = self.single_char_names.len();
++        f(self);
++        self.names.truncate(n);
++        self.single_char_names.truncate(single_char_count);
++    }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
++    fn visit_local(&mut self, local: &'tcx Local) {
++        if let Some(ref init) = local.init {
++            self.apply(|this| walk_expr(this, &**init));
++        }
++        // add the pattern after the expression because the bindings aren't available
++        // yet in the init
++        // expression
++        SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
++    }
++    fn visit_block(&mut self, blk: &'tcx Block) {
++        self.single_char_names.push(vec![]);
++
++        self.apply(|this| walk_block(this, blk));
++
++        self.check_single_char_names();
++        self.single_char_names.pop();
++    }
++    fn visit_arm(&mut self, arm: &'tcx Arm) {
++        self.single_char_names.push(vec![]);
++
++        self.apply(|this| {
++            SimilarNamesNameVisitor(this).visit_pat(&arm.pat);
++            this.apply(|this| walk_expr(this, &arm.body));
++        });
++
++        self.check_single_char_names();
++        self.single_char_names.pop();
++    }
++    fn visit_item(&mut self, _: &Item) {
++        // do not recurse into inner items
++    }
++    fn visit_mac(&mut self, _mac: &MacCall) {
++        // do not check macs
++    }
++}
++
++impl EarlyLintPass for NonExpressiveNames {
++    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
++        if let ItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
++            do_check(self, cx, &item.attrs, &sig.decl, blk);
++        }
++    }
++
++    fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) {
++        if let AssocItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
++            do_check(self, cx, &item.attrs, &sig.decl, blk);
++        }
++    }
++}
++
++fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
++    if !attr::contains_name(attrs, sym!(test)) {
++        let mut visitor = SimilarNamesLocalVisitor {
++            names: Vec::new(),
++            cx,
++            lint,
++            single_char_names: vec![vec![]],
++        };
++
++        // initialize with function arguments
++        for arg in &decl.inputs {
++            SimilarNamesNameVisitor(&mut visitor).visit_pat(&arg.pat);
++        }
++        // walk all other bindings
++        walk_block(&mut visitor, blk);
++
++        visitor.check_single_char_names();
++    }
++}
++
++/// Precondition: `a_name.chars().count() < b_name.chars().count()`.
++#[must_use]
++fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
++    debug_assert!(a_name.chars().count() < b_name.chars().count());
++    let mut a_chars = a_name.chars();
++    let mut b_chars = b_name.chars();
++    while let (Some(a), Some(b)) = (a_chars.next(), b_chars.next()) {
++        if a == b {
++            continue;
++        }
++        if let Some(b2) = b_chars.next() {
++            // check if there's just one character inserted
++            return a != b2 || a_chars.ne(b_chars);
++        } else {
++            // tuple
++            // ntuple
++            return true;
++        }
++    }
++    // for item in items
++    true
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9d3b67988dbb55d35612886ba7f8bd207b6e3292
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,203 @@@
++use crate::utils::{match_type, paths, span_lint, walk_ptrs_ty};
++use rustc_ast::ast::LitKind;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::{Span, Spanned};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for duplicate open options as well as combinations
++    /// that make no sense.
++    ///
++    /// **Why is this bad?** In the best case, the code will be harder to read than
++    /// necessary. I don't know the worst case.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// use std::fs::OpenOptions;
++    ///
++    /// OpenOptions::new().read(true).truncate(true);
++    /// ```
++    pub NONSENSICAL_OPEN_OPTIONS,
++    correctness,
++    "nonsensical combination of options for opening a file"
++}
++
++declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OpenOptions {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        if let ExprKind::MethodCall(ref path, _, ref arguments) = e.kind {
++            let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&arguments[0]));
++            if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) {
++                let mut options = Vec::new();
++                get_open_options(cx, &arguments[0], &mut options);
++                check_open_options(cx, &options, e.span);
++            }
++        }
++    }
++}
++
++#[derive(Debug, PartialEq, Eq, Clone, Copy)]
++enum Argument {
++    True,
++    False,
++    Unknown,
++}
++
++#[derive(Debug)]
++enum OpenOption {
++    Write,
++    Read,
++    Truncate,
++    Create,
++    Append,
++}
++
++fn get_open_options(cx: &LateContext<'_, '_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) {
++    if let ExprKind::MethodCall(ref path, _, ref arguments) = argument.kind {
++        let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&arguments[0]));
++
++        // Only proceed if this is a call on some object of type std::fs::OpenOptions
++        if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {
++            let argument_option = match arguments[1].kind {
++                ExprKind::Lit(ref span) => {
++                    if let Spanned {
++                        node: LitKind::Bool(lit),
++                        ..
++                    } = *span
++                    {
++                        if lit {
++                            Argument::True
++                        } else {
++                            Argument::False
++                        }
++                    } else {
++                        return; // The function is called with a literal
++                                // which is not a boolean literal. This is theoretically
++                                // possible, but not very likely.
++                    }
++                },
++                _ => Argument::Unknown,
++            };
++
++            match &*path.ident.as_str() {
++                "create" => {
++                    options.push((OpenOption::Create, argument_option));
++                },
++                "append" => {
++                    options.push((OpenOption::Append, argument_option));
++                },
++                "truncate" => {
++                    options.push((OpenOption::Truncate, argument_option));
++                },
++                "read" => {
++                    options.push((OpenOption::Read, argument_option));
++                },
++                "write" => {
++                    options.push((OpenOption::Write, argument_option));
++                },
++                _ => (),
++            }
++
++            get_open_options(cx, &arguments[0], options);
++        }
++    }
++}
++
++fn check_open_options(cx: &LateContext<'_, '_>, options: &[(OpenOption, Argument)], span: Span) {
++    let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false);
++    let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) =
++        (false, false, false, false, false);
++    // This code is almost duplicated (oh, the irony), but I haven't found a way to
++    // unify it.
++
++    for option in options {
++        match *option {
++            (OpenOption::Create, arg) => {
++                if create {
++                    span_lint(
++                        cx,
++                        NONSENSICAL_OPEN_OPTIONS,
++                        span,
++                        "the method `create` is called more than once",
++                    );
++                } else {
++                    create = true
++                }
++                create_arg = create_arg || (arg == Argument::True);
++            },
++            (OpenOption::Append, arg) => {
++                if append {
++                    span_lint(
++                        cx,
++                        NONSENSICAL_OPEN_OPTIONS,
++                        span,
++                        "the method `append` is called more than once",
++                    );
++                } else {
++                    append = true
++                }
++                append_arg = append_arg || (arg == Argument::True);
++            },
++            (OpenOption::Truncate, arg) => {
++                if truncate {
++                    span_lint(
++                        cx,
++                        NONSENSICAL_OPEN_OPTIONS,
++                        span,
++                        "the method `truncate` is called more than once",
++                    );
++                } else {
++                    truncate = true
++                }
++                truncate_arg = truncate_arg || (arg == Argument::True);
++            },
++            (OpenOption::Read, arg) => {
++                if read {
++                    span_lint(
++                        cx,
++                        NONSENSICAL_OPEN_OPTIONS,
++                        span,
++                        "the method `read` is called more than once",
++                    );
++                } else {
++                    read = true
++                }
++                read_arg = read_arg || (arg == Argument::True);
++            },
++            (OpenOption::Write, arg) => {
++                if write {
++                    span_lint(
++                        cx,
++                        NONSENSICAL_OPEN_OPTIONS,
++                        span,
++                        "the method `write` is called more than once",
++                    );
++                } else {
++                    write = true
++                }
++                write_arg = write_arg || (arg == Argument::True);
++            },
++        }
++    }
++
++    if read && truncate && read_arg && truncate_arg && !(write && write_arg) {
++        span_lint(
++            cx,
++            NONSENSICAL_OPEN_OPTIONS,
++            span,
++            "file opened with `truncate` and `read`",
++        );
++    }
++    if append && truncate && append_arg && truncate_arg {
++        span_lint(
++            cx,
++            NONSENSICAL_OPEN_OPTIONS,
++            span,
++            "file opened with `append` and `truncate`",
++        );
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..66dfa20edb5e724890f43177332bfd720d941d44
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++use crate::utils::{is_direct_expn_of, span_lint_and_help};
++use if_chain::if_chain;
++use rustc_ast::ast::{Expr, ExprKind};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `option_env!(...).unwrap()` and
++    /// suggests usage of the `env!` macro.
++    ///
++    /// **Why is this bad?** Unwrapping the result of `option_env!` will panic
++    /// at run-time if the environment variable doesn't exist, whereas `env!`
++    /// catches it at compile-time.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust,no_run
++    /// let _ = option_env!("HOME").unwrap();
++    /// ```
++    ///
++    /// Is better expressed as:
++    ///
++    /// ```rust,no_run
++    /// let _ = env!("HOME");
++    /// ```
++    pub OPTION_ENV_UNWRAP,
++    correctness,
++    "using `option_env!(...).unwrap()` to get environment variable"
++}
++
++declare_lint_pass!(OptionEnvUnwrap => [OPTION_ENV_UNWRAP]);
++
++impl EarlyLintPass for OptionEnvUnwrap {
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++        if_chain! {
++            if let ExprKind::MethodCall(path_segment, args) = &expr.kind;
++            let method_name = path_segment.ident.as_str();
++            if method_name == "expect" || method_name == "unwrap";
++            if let ExprKind::Call(caller, _) = &args[0].kind;
++            if is_direct_expn_of(caller.span, "option_env").is_some();
++            then {
++                span_lint_and_help(
++                    cx,
++                    OPTION_ENV_UNWRAP,
++                    expr.span,
++                    "this will panic at run-time if the environment variable doesn't exist at compile-time",
++                    None,
++                    "consider using the `env!` macro instead"
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b90fdc232e72c825d406fce6df2035820e3db488
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,82 @@@
++use crate::utils::{span_lint, SpanlessEq};
++use if_chain::if_chain;
++use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Detects classic underflow/overflow checks.
++    ///
++    /// **Why is this bad?** Most classic C underflow/overflow checks will fail in
++    /// Rust. Users can use functions like `overflowing_*` and `wrapping_*` instead.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let a = 1;
++    /// # let b = 2;
++    /// a + b < a;
++    /// ```
++    pub OVERFLOW_CHECK_CONDITIONAL,
++    complexity,
++    "overflow checks inspired by C which are likely to panic"
++}
++
++declare_lint_pass!(OverflowCheckConditional => [OVERFLOW_CHECK_CONDITIONAL]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OverflowCheckConditional {
++    // a + b < a, a > a + b, a < a - b, a - b > a
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        let eq = |l, r| SpanlessEq::new(cx).eq_path_segment(l, r);
++        if_chain! {
++            if let ExprKind::Binary(ref op, ref first, ref second) = expr.kind;
++            if let ExprKind::Binary(ref op2, ref ident1, ref ident2) = first.kind;
++            if let ExprKind::Path(QPath::Resolved(_, ref path1)) = ident1.kind;
++            if let ExprKind::Path(QPath::Resolved(_, ref path2)) = ident2.kind;
++            if let ExprKind::Path(QPath::Resolved(_, ref path3)) = second.kind;
++            if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]);
++            if cx.tables.expr_ty(ident1).is_integral();
++            if cx.tables.expr_ty(ident2).is_integral();
++            then {
++                if let BinOpKind::Lt = op.node {
++                    if let BinOpKind::Add = op2.node {
++                        span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
++                            "You are trying to use classic C overflow conditions that will fail in Rust.");
++                    }
++                }
++                if let BinOpKind::Gt = op.node {
++                    if let BinOpKind::Sub = op2.node {
++                        span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
++                            "You are trying to use classic C underflow conditions that will fail in Rust.");
++                    }
++                }
++            }
++        }
++
++        if_chain! {
++            if let ExprKind::Binary(ref op, ref first, ref second) = expr.kind;
++            if let ExprKind::Binary(ref op2, ref ident1, ref ident2) = second.kind;
++            if let ExprKind::Path(QPath::Resolved(_, ref path1)) = ident1.kind;
++            if let ExprKind::Path(QPath::Resolved(_, ref path2)) = ident2.kind;
++            if let ExprKind::Path(QPath::Resolved(_, ref path3)) = first.kind;
++            if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]);
++            if cx.tables.expr_ty(ident1).is_integral();
++            if cx.tables.expr_ty(ident2).is_integral();
++            then {
++                if let BinOpKind::Gt = op.node {
++                    if let BinOpKind::Add = op2.node {
++                        span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
++                            "You are trying to use classic C overflow conditions that will fail in Rust.");
++                    }
++                }
++                if let BinOpKind::Lt = op.node {
++                    if let BinOpKind::Sub = op2.node {
++                        span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
++                            "You are trying to use classic C underflow conditions that will fail in Rust.");
++                    }
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2cd9200ddb252d291fd818853a152484e9d82a6b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,154 @@@
++use crate::utils::{is_direct_expn_of, is_expn_of, match_function_call, paths, span_lint};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for missing parameters in `panic!`.
++    ///
++    /// **Why is this bad?** Contrary to the `format!` family of macros, there are
++    /// two forms of `panic!`: if there are no parameters given, the first argument
++    /// is not a format string and used literally. So while `format!("{}")` will
++    /// fail to compile, `panic!("{}")` will not.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```no_run
++    /// panic!("This `panic!` is probably missing a parameter there: {}");
++    /// ```
++    pub PANIC_PARAMS,
++    style,
++    "missing parameters in `panic!` calls"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `panic!`.
++    ///
++    /// **Why is this bad?** `panic!` will stop the execution of the executable
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```no_run
++    /// panic!("even with a good reason");
++    /// ```
++    pub PANIC,
++    restriction,
++    "usage of the `panic!` macro"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `unimplemented!`.
++    ///
++    /// **Why is this bad?** This macro should not be present in production code
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```no_run
++    /// unimplemented!();
++    /// ```
++    pub UNIMPLEMENTED,
++    restriction,
++    "`unimplemented!` should not be present in production code"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `todo!`.
++    ///
++    /// **Why is this bad?** This macro should not be present in production code
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```no_run
++    /// todo!();
++    /// ```
++    pub TODO,
++    restriction,
++    "`todo!` should not be present in production code"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `unreachable!`.
++    ///
++    /// **Why is this bad?** This macro can cause code to panic
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```no_run
++    /// unreachable!();
++    /// ```
++    pub UNREACHABLE,
++    restriction,
++    "`unreachable!` should not be present in production code"
++}
++
++declare_lint_pass!(PanicUnimplemented => [PANIC_PARAMS, UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PanicUnimplemented {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            if let ExprKind::Block(ref block, _) = expr.kind;
++            if let Some(ref ex) = block.expr;
++            if let Some(params) = match_function_call(cx, ex, &paths::BEGIN_PANIC);
++            if params.len() == 1;
++            then {
++                if is_expn_of(expr.span, "unimplemented").is_some() {
++                    let span = get_outer_span(expr);
++                    span_lint(cx, UNIMPLEMENTED, span,
++                              "`unimplemented` should not be present in production code");
++                } else if is_expn_of(expr.span, "todo").is_some() {
++                    let span = get_outer_span(expr);
++                    span_lint(cx, TODO, span,
++                              "`todo` should not be present in production code");
++                } else if is_expn_of(expr.span, "unreachable").is_some() {
++                    let span = get_outer_span(expr);
++                    span_lint(cx, UNREACHABLE, span,
++                              "`unreachable` should not be present in production code");
++                } else if is_expn_of(expr.span, "panic").is_some() {
++                    let span = get_outer_span(expr);
++                    span_lint(cx, PANIC, span,
++                              "`panic` should not be present in production code");
++                    match_panic(params, expr, cx);
++                }
++            }
++        }
++    }
++}
++
++fn get_outer_span(expr: &Expr<'_>) -> Span {
++    if_chain! {
++        if expr.span.from_expansion();
++        let first = expr.span.ctxt().outer_expn_data();
++        if first.call_site.from_expansion();
++        let second = first.call_site.ctxt().outer_expn_data();
++        then {
++            second.call_site
++        } else {
++            expr.span
++        }
++    }
++}
++
++fn match_panic(params: &[Expr<'_>], expr: &Expr<'_>, cx: &LateContext<'_, '_>) {
++    if_chain! {
++        if let ExprKind::Lit(ref lit) = params[0].kind;
++        if is_direct_expn_of(expr.span, "panic").is_some();
++        if let LitKind::Str(ref string, _) = lit.node;
++        let string = string.as_str().replace("{{", "").replace("}}", "");
++        if let Some(par) = string.find('{');
++        if string[par..].contains('}');
++        if params[0].span.source_callee().is_none();
++        if params[0].span.lo() != params[0].span.hi();
++        then {
++            span_lint(cx, PANIC_PARAMS, params[0].span,
++                      "you probably are missing some parameter in your format string");
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1445df41c452f4aaa2856353f235c686cc08aec6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++use crate::utils::{is_automatically_derived, span_lint_hir};
++use if_chain::if_chain;
++use rustc_hir::{Item, ItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for manual re-implementations of `PartialEq::ne`.
++    ///
++    /// **Why is this bad?** `PartialEq::ne` is required to always return the
++    /// negated result of `PartialEq::eq`, which is exactly what the default
++    /// implementation does. Therefore, there should never be any need to
++    /// re-implement it.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// struct Foo;
++    ///
++    /// impl PartialEq for Foo {
++    ///    fn eq(&self, other: &Foo) -> bool { true }
++    ///    fn ne(&self, other: &Foo) -> bool { !(self == other) }
++    /// }
++    /// ```
++    pub PARTIALEQ_NE_IMPL,
++    complexity,
++    "re-implementing `PartialEq::ne`"
++}
++
++declare_lint_pass!(PartialEqNeImpl => [PARTIALEQ_NE_IMPL]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PartialEqNeImpl {
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++        if_chain! {
++            if let ItemKind::Impl{ of_trait: Some(ref trait_ref), items: impl_items, .. } = item.kind;
++            if !is_automatically_derived(&*item.attrs);
++            if let Some(eq_trait) = cx.tcx.lang_items().eq_trait();
++            if trait_ref.path.res.def_id() == eq_trait;
++            then {
++                for impl_item in impl_items {
++                    if impl_item.ident.name == sym!(ne) {
++                        span_lint_hir(
++                            cx,
++                            PARTIALEQ_NE_IMPL,
++                            impl_item.id.hir_id,
++                            impl_item.span,
++                            "re-implementing `PartialEq::ne` is unnecessary",
++                        );
++                    }
++                }
++            }
++        };
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bdbaf2695c8ef84c697cfcb0b5cb46fa1d93d9fb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,71 @@@
++use crate::utils::{match_type, paths, span_lint_and_sugg, walk_ptrs_ty};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use std::path::{Component, Path};
++
++declare_clippy_lint! {
++    /// **What it does:*** Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
++    /// calls on `PathBuf` that can cause overwrites.
++    ///
++    /// **Why is this bad?** Calling `push` with a root path at the start can overwrite the
++    /// previous defined path.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// use std::path::PathBuf;
++    ///
++    /// let mut x = PathBuf::from("/foo");
++    /// x.push("/bar");
++    /// assert_eq!(x, PathBuf::from("/bar"));
++    /// ```
++    /// Could be written:
++    ///
++    /// ```rust
++    /// use std::path::PathBuf;
++    ///
++    /// let mut x = PathBuf::from("/foo");
++    /// x.push("bar");
++    /// assert_eq!(x, PathBuf::from("/foo/bar"));
++    /// ```
++    pub PATH_BUF_PUSH_OVERWRITE,
++    nursery,
++    "calling `push` with file system root on `PathBuf` can overwrite it"
++}
++
++declare_lint_pass!(PathBufPushOverwrite => [PATH_BUF_PUSH_OVERWRITE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PathBufPushOverwrite {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind;
++            if path.ident.name == sym!(push);
++            if args.len() == 2;
++            if match_type(cx, walk_ptrs_ty(cx.tables.expr_ty(&args[0])), &paths::PATH_BUF);
++            if let Some(get_index_arg) = args.get(1);
++            if let ExprKind::Lit(ref lit) = get_index_arg.kind;
++            if let LitKind::Str(ref path_lit, _) = lit.node;
++            if let pushed_path = Path::new(&*path_lit.as_str());
++            if let Some(pushed_path_lit) = pushed_path.to_str();
++            if pushed_path.has_root();
++            if let Some(root) = pushed_path.components().next();
++            if root == Component::RootDir;
++            then {
++                span_lint_and_sugg(
++                    cx,
++                    PATH_BUF_PUSH_OVERWRITE,
++                    lit.span,
++                    "Calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
++                    "try",
++                    format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
++                    Applicability::MachineApplicable,
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cc783baa687232f7c01f6d898a8b8d3c3aa3853a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,164 @@@
++use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
++use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp};
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Spanned;
++
++const ODD_FUNCTIONS_WHITELIST: [&str; 14] = [
++    "asin",
++    "asinh",
++    "atan",
++    "atanh",
++    "cbrt",
++    "fract",
++    "round",
++    "signum",
++    "sin",
++    "sinh",
++    "tan",
++    "tanh",
++    "to_degrees",
++    "to_radians",
++];
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for operations where precedence may be unclear
++    /// and suggests to add parentheses. Currently it catches the following:
++    /// * mixed usage of arithmetic and bit shifting/combining operators without
++    /// parentheses
++    /// * a "negative" numeric literal (which is really a unary `-` followed by a
++    /// numeric literal)
++    ///   followed by a method call
++    ///
++    /// **Why is this bad?** Not everyone knows the precedence of those operators by
++    /// heart, so expressions like these may trip others trying to reason about the
++    /// code.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7
++    /// * `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1
++    pub PRECEDENCE,
++    complexity,
++    "operations where precedence may be unclear"
++}
++
++declare_lint_pass!(Precedence => [PRECEDENCE]);
++
++impl EarlyLintPass for Precedence {
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++        if expr.span.from_expansion() {
++            return;
++        }
++
++        if let ExprKind::Binary(Spanned { node: op, .. }, ref left, ref right) = expr.kind {
++            let span_sugg = |expr: &Expr, sugg, appl| {
++                span_lint_and_sugg(
++                    cx,
++                    PRECEDENCE,
++                    expr.span,
++                    "operator precedence can trip the unwary",
++                    "consider parenthesizing your expression",
++                    sugg,
++                    appl,
++                );
++            };
++
++            if !is_bit_op(op) {
++                return;
++            }
++            let mut applicability = Applicability::MachineApplicable;
++            match (is_arith_expr(left), is_arith_expr(right)) {
++                (true, true) => {
++                    let sugg = format!(
++                        "({}) {} ({})",
++                        snippet_with_applicability(cx, left.span, "..", &mut applicability),
++                        op.to_string(),
++                        snippet_with_applicability(cx, right.span, "..", &mut applicability)
++                    );
++                    span_sugg(expr, sugg, applicability);
++                },
++                (true, false) => {
++                    let sugg = format!(
++                        "({}) {} {}",
++                        snippet_with_applicability(cx, left.span, "..", &mut applicability),
++                        op.to_string(),
++                        snippet_with_applicability(cx, right.span, "..", &mut applicability)
++                    );
++                    span_sugg(expr, sugg, applicability);
++                },
++                (false, true) => {
++                    let sugg = format!(
++                        "{} {} ({})",
++                        snippet_with_applicability(cx, left.span, "..", &mut applicability),
++                        op.to_string(),
++                        snippet_with_applicability(cx, right.span, "..", &mut applicability)
++                    );
++                    span_sugg(expr, sugg, applicability);
++                },
++                (false, false) => (),
++            }
++        }
++
++        if let ExprKind::Unary(UnOp::Neg, ref rhs) = expr.kind {
++            if let ExprKind::MethodCall(ref path_segment, ref args) = rhs.kind {
++                let path_segment_str = path_segment.ident.name.as_str();
++                if let Some(slf) = args.first() {
++                    if let ExprKind::Lit(ref lit) = slf.kind {
++                        match lit.kind {
++                            LitKind::Int(..) | LitKind::Float(..) => {
++                                if ODD_FUNCTIONS_WHITELIST
++                                    .iter()
++                                    .any(|odd_function| **odd_function == *path_segment_str)
++                                {
++                                    return;
++                                }
++                                let mut applicability = Applicability::MachineApplicable;
++                                span_lint_and_sugg(
++                                    cx,
++                                    PRECEDENCE,
++                                    expr.span,
++                                    "unary minus has lower precedence than method call",
++                                    "consider adding parentheses to clarify your intent",
++                                    format!(
++                                        "-({})",
++                                        snippet_with_applicability(cx, rhs.span, "..", &mut applicability)
++                                    ),
++                                    applicability,
++                                );
++                            },
++                            _ => (),
++                        }
++                    }
++                }
++            }
++        }
++    }
++}
++
++fn is_arith_expr(expr: &Expr) -> bool {
++    match expr.kind {
++        ExprKind::Binary(Spanned { node: op, .. }, _, _) => is_arith_op(op),
++        _ => false,
++    }
++}
++
++#[must_use]
++fn is_bit_op(op: BinOpKind) -> bool {
++    use rustc_ast::ast::BinOpKind::{BitAnd, BitOr, BitXor, Shl, Shr};
++    match op {
++        BitXor | BitAnd | BitOr | Shl | Shr => true,
++        _ => false,
++    }
++}
++
++#[must_use]
++fn is_arith_op(op: BinOpKind) -> bool {
++    use rustc_ast::ast::BinOpKind::{Add, Div, Mul, Rem, Sub};
++    match op {
++        Add | Sub | Mul | Div | Rem => true,
++        _ => false,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2cdf96714195abbbec79deccf117807c16714113
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,301 @@@
++//! Checks for usage of  `&Vec[_]` and `&String`.
++
++use crate::utils::ptr::get_spans;
++use crate::utils::{
++    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 if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{
++    BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind,
++    Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind,
++};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++use rustc_span::MultiSpan;
++use std::borrow::Cow;
++
++declare_clippy_lint! {
++    /// **What it does:** This lint checks for function arguments of type `&String`
++    /// or `&Vec` unless the references are mutable. It will also suggest you
++    /// replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()`
++    /// calls.
++    ///
++    /// **Why is this bad?** Requiring the argument to be of the specific size
++    /// makes the function less useful for no benefit; slices in the form of `&[T]`
++    /// or `&str` usually suffice and can be obtained from other types, too.
++    ///
++    /// **Known problems:** The lint does not follow data. So if you have an
++    /// argument `x` and write `let y = x; y.clone()` the lint will not suggest
++    /// changing that `.clone()` to `.to_owned()`.
++    ///
++    /// Other functions called from this function taking a `&String` or `&Vec`
++    /// argument may also fail to compile if you change the argument. Applying
++    /// this lint on them will fix the problem, but they may be in other crates.
++    ///
++    /// Also there may be `fn(&Vec)`-typed references pointing to your function.
++    /// If you have them, you will get a compiler error after applying this lint's
++    /// suggestions. You then have the choice to undo your changes or change the
++    /// type of the reference.
++    ///
++    /// Note that if the function is part of your public interface, there may be
++    /// other crates referencing it you may not be aware. Carefully deprecate the
++    /// function before applying the lint suggestions in this case.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// fn foo(&Vec<u32>) { .. }
++    /// ```
++    pub PTR_ARG,
++    style,
++    "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** This lint checks for equality comparisons with `ptr::null`
++    ///
++    /// **Why is this bad?** It's easier and more readable to use the inherent
++    /// `.is_null()`
++    /// method instead
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// if x == ptr::null {
++    ///     ..
++    /// }
++    /// ```
++    pub CMP_NULL,
++    style,
++    "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead."
++}
++
++declare_clippy_lint! {
++    /// **What it does:** This lint checks for functions that take immutable
++    /// references and return
++    /// mutable ones.
++    ///
++    /// **Why is this bad?** This is trivially unsound, as one can create two
++    /// mutable references
++    /// from the same (immutable!) source. This
++    /// [error](https://github.com/rust-lang/rust/issues/39465)
++    /// actually lead to an interim Rust release 1.15.1.
++    ///
++    /// **Known problems:** To be on the conservative side, if there's at least one
++    /// mutable reference
++    /// with the output lifetime, this lint will not trigger. In practice, this
++    /// case is unlikely anyway.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// fn foo(&Foo) -> &mut Bar { .. }
++    /// ```
++    pub MUT_FROM_REF,
++    correctness,
++    "fns that create mutable refs from immutable ref args"
++}
++
++declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Ptr {
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++        if let ItemKind::Fn(ref sig, _, body_id) = item.kind {
++            check_fn(cx, &sig.decl, item.hir_id, Some(body_id));
++        }
++    }
++
++    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem<'_>) {
++        if let ImplItemKind::Fn(ref sig, body_id) = item.kind {
++            let parent_item = cx.tcx.hir().get_parent_item(item.hir_id);
++            if let Some(Node::Item(it)) = cx.tcx.hir().find(parent_item) {
++                if let ItemKind::Impl { of_trait: Some(_), .. } = it.kind {
++                    return; // ignore trait impls
++                }
++            }
++            check_fn(cx, &sig.decl, item.hir_id, Some(body_id));
++        }
++    }
++
++    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) {
++        if let TraitItemKind::Fn(ref sig, ref trait_method) = item.kind {
++            let body_id = if let TraitFn::Provided(b) = *trait_method {
++                Some(b)
++            } else {
++                None
++            };
++            check_fn(cx, &sig.decl, item.hir_id, body_id);
++        }
++    }
++
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if let ExprKind::Binary(ref op, ref l, ref r) = expr.kind {
++            if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(l) || is_null_path(r)) {
++                span_lint(
++                    cx,
++                    CMP_NULL,
++                    expr.span,
++                    "Comparing with null is better expressed by the `.is_null()` method",
++                );
++            }
++        }
++    }
++}
++
++#[allow(clippy::too_many_lines)]
++fn check_fn(cx: &LateContext<'_, '_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: Option<BodyId>) {
++    let fn_def_id = cx.tcx.hir().local_def_id(fn_id);
++    let sig = cx.tcx.fn_sig(fn_def_id);
++    let fn_ty = sig.skip_binder();
++
++    for (idx, (arg, ty)) in decl.inputs.iter().zip(fn_ty.inputs()).enumerate() {
++        if let ty::Ref(_, ty, Mutability::Not) = ty.kind {
++            if is_type_diagnostic_item(cx, ty, sym!(vec_type)) {
++                let mut ty_snippet = None;
++                if_chain! {
++                    if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind;
++                    if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last();
++                    then {
++                        let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg {
++                            GenericArg::Type(ty) => Some(ty),
++                            _ => None,
++                        }).collect();
++                        if types.len() == 1 {
++                            ty_snippet = snippet_opt(cx, types[0].span);
++                        }
++                    }
++                };
++                if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) {
++                    span_lint_and_then(
++                        cx,
++                        PTR_ARG,
++                        arg.span,
++                        "writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \
++                         with non-Vec-based slices.",
++                        |diag| {
++                            if let Some(ref snippet) = ty_snippet {
++                                diag.span_suggestion(
++                                    arg.span,
++                                    "change this to",
++                                    format!("&[{}]", snippet),
++                                    Applicability::Unspecified,
++                                );
++                            }
++                            for (clonespan, suggestion) in spans {
++                                diag.span_suggestion(
++                                    clonespan,
++                                    &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
++                                        Cow::Owned(format!("change `{}` to", x))
++                                    }),
++                                    suggestion.into(),
++                                    Applicability::Unspecified,
++                                );
++                            }
++                        },
++                    );
++                }
++            } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
++                if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) {
++                    span_lint_and_then(
++                        cx,
++                        PTR_ARG,
++                        arg.span,
++                        "writing `&String` instead of `&str` involves a new object where a slice will do.",
++                        |diag| {
++                            diag.span_suggestion(arg.span, "change this to", "&str".into(), Applicability::Unspecified);
++                            for (clonespan, suggestion) in spans {
++                                diag.span_suggestion_short(
++                                    clonespan,
++                                    &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
++                                        Cow::Owned(format!("change `{}` to", x))
++                                    }),
++                                    suggestion.into(),
++                                    Applicability::Unspecified,
++                                );
++                            }
++                        },
++                    );
++                }
++            } else if match_type(cx, ty, &paths::COW) {
++                if_chain! {
++                    if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind;
++                    if let TyKind::Path(ref path) = ty.kind;
++                    if let QPath::Resolved(None, ref pp) = *path;
++                    if let [ref bx] = *pp.segments;
++                    if let Some(ref params) = bx.args;
++                    if !params.parenthesized;
++                    if let Some(inner) = params.args.iter().find_map(|arg| match arg {
++                        GenericArg::Type(ty) => Some(ty),
++                        _ => None,
++                    });
++                    then {
++                        let replacement = snippet_opt(cx, inner.span);
++                        if let Some(r) = replacement {
++                            span_lint_and_sugg(
++                                cx,
++                                PTR_ARG,
++                                arg.span,
++                                "using a reference to `Cow` is not recommended.",
++                                "change this to",
++                                "&".to_owned() + &r,
++                                Applicability::Unspecified,
++                            );
++                        }
++                    }
++                }
++            }
++        }
++    }
++
++    if let FnRetTy::Return(ref ty) = decl.output {
++        if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) {
++            let mut immutables = vec![];
++            for (_, ref mutbl, ref argspan) in decl
++                .inputs
++                .iter()
++                .filter_map(|ty| get_rptr_lm(ty))
++                .filter(|&(lt, _, _)| lt.name == out.name)
++            {
++                if *mutbl == Mutability::Mut {
++                    return;
++                }
++                immutables.push(*argspan);
++            }
++            if immutables.is_empty() {
++                return;
++            }
++            span_lint_and_then(
++                cx,
++                MUT_FROM_REF,
++                ty.span,
++                "mutable borrow from immutable input(s)",
++                |diag| {
++                    let ms = MultiSpan::from_spans(immutables);
++                    diag.span_note(ms, "immutable borrow here");
++                },
++            );
++        }
++    }
++}
++
++fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
++    if let TyKind::Rptr(ref lt, ref m) = ty.kind {
++        Some((lt, m.mutbl, ty.span))
++    } else {
++        None
++    }
++}
++
++fn is_null_path(expr: &Expr<'_>) -> bool {
++    if let ExprKind::Call(ref pathexp, ref args) = expr.kind {
++        if args.is_empty() {
++            if let ExprKind::Path(ref path) = pathexp.kind {
++                return match_qpath(path, &paths::PTR_NULL) || match_qpath(path, &paths::PTR_NULL_MUT);
++            }
++        }
++    }
++    false
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ffc59d43750e043974eb5f6a1dfb60381d66662f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,150 @@@
++use crate::utils::{snippet_opt, span_lint, span_lint_and_sugg};
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use std::fmt;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of the `offset` pointer method with a `usize` casted to an
++    /// `isize`.
++    ///
++    /// **Why is this bad?** If we’re always increasing the pointer address, we can avoid the numeric
++    /// cast by using the `add` method instead.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let vec = vec![b'a', b'b', b'c'];
++    /// let ptr = vec.as_ptr();
++    /// let offset = 1_usize;
++    ///
++    /// unsafe {
++    ///     ptr.offset(offset as isize);
++    /// }
++    /// ```
++    ///
++    /// Could be written:
++    ///
++    /// ```rust
++    /// let vec = vec![b'a', b'b', b'c'];
++    /// let ptr = vec.as_ptr();
++    /// let offset = 1_usize;
++    ///
++    /// unsafe {
++    ///     ptr.add(offset);
++    /// }
++    /// ```
++    pub PTR_OFFSET_WITH_CAST,
++    complexity,
++    "unneeded pointer offset cast"
++}
++
++declare_lint_pass!(PtrOffsetWithCast => [PTR_OFFSET_WITH_CAST]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PtrOffsetWithCast {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call
++        let (receiver_expr, arg_expr, method) = match expr_as_ptr_offset_call(cx, expr) {
++            Some(call_arg) => call_arg,
++            None => return,
++        };
++
++        // Check if the argument to the method call is a cast from usize
++        let cast_lhs_expr = match expr_as_cast_from_usize(cx, arg_expr) {
++            Some(cast_lhs_expr) => cast_lhs_expr,
++            None => return,
++        };
++
++        let msg = format!("use of `{}` with a `usize` casted to an `isize`", method);
++        if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) {
++            span_lint_and_sugg(
++                cx,
++                PTR_OFFSET_WITH_CAST,
++                expr.span,
++                &msg,
++                "try",
++                sugg,
++                Applicability::MachineApplicable,
++            );
++        } else {
++            span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, &msg);
++        }
++    }
++}
++
++// If the given expression is a cast from a usize, return the lhs of the cast
++fn expr_as_cast_from_usize<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
++    if let ExprKind::Cast(ref cast_lhs_expr, _) = expr.kind {
++        if is_expr_ty_usize(cx, &cast_lhs_expr) {
++            return Some(cast_lhs_expr);
++        }
++    }
++    None
++}
++
++// If the given expression is a ptr::offset  or ptr::wrapping_offset method call, return the
++// receiver, the arg of the method call, and the method.
++fn expr_as_ptr_offset_call<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &'tcx Expr<'_>,
++) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> {
++    if let ExprKind::MethodCall(ref path_segment, _, ref args) = expr.kind {
++        if is_expr_ty_raw_ptr(cx, &args[0]) {
++            if path_segment.ident.name == sym!(offset) {
++                return Some((&args[0], &args[1], Method::Offset));
++            }
++            if path_segment.ident.name == sym!(wrapping_offset) {
++                return Some((&args[0], &args[1], Method::WrappingOffset));
++            }
++        }
++    }
++    None
++}
++
++// Is the type of the expression a usize?
++fn is_expr_ty_usize<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>) -> bool {
++    cx.tables.expr_ty(expr) == cx.tcx.types.usize
++}
++
++// Is the type of the expression a raw pointer?
++fn is_expr_ty_raw_ptr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>) -> bool {
++    cx.tables.expr_ty(expr).is_unsafe_ptr()
++}
++
++fn build_suggestion<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    method: Method,
++    receiver_expr: &Expr<'_>,
++    cast_lhs_expr: &Expr<'_>,
++) -> Option<String> {
++    let receiver = snippet_opt(cx, receiver_expr.span)?;
++    let cast_lhs = snippet_opt(cx, cast_lhs_expr.span)?;
++    Some(format!("{}.{}({})", receiver, method.suggestion(), cast_lhs))
++}
++
++#[derive(Copy, Clone)]
++enum Method {
++    Offset,
++    WrappingOffset,
++}
++
++impl Method {
++    #[must_use]
++    fn suggestion(self) -> &'static str {
++        match self {
++            Self::Offset => "add",
++            Self::WrappingOffset => "wrapping_add",
++        }
++    }
++}
++
++impl fmt::Display for Method {
++    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++        match self {
++            Self::Offset => write!(f, "offset"),
++            Self::WrappingOffset => write!(f, "wrapping_offset"),
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ea654467b86687cfc522ff22471572cf9bef39fb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,204 @@@
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::{def, BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use crate::utils::sugg::Sugg;
++use crate::utils::{
++    higher, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability,
++    span_lint_and_sugg, SpanlessEq,
++};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for expressions that could be replaced by the question mark operator.
++    ///
++    /// **Why is this bad?** Question mark usage is more idiomatic.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// if option.is_none() {
++    ///     return None;
++    /// }
++    /// ```
++    ///
++    /// Could be written:
++    ///
++    /// ```ignore
++    /// option?;
++    /// ```
++    pub QUESTION_MARK,
++    style,
++    "checks for expressions that could be replaced by the question mark operator"
++}
++
++declare_lint_pass!(QuestionMark => [QUESTION_MARK]);
++
++impl QuestionMark {
++    /// Checks if the given expression on the given context matches the following structure:
++    ///
++    /// ```ignore
++    /// if option.is_none() {
++    ///    return None;
++    /// }
++    /// ```
++    ///
++    /// If it matches, it will suggest to use the question mark operator instead
++    fn check_is_none_and_early_return_none(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++        if_chain! {
++            if let Some((if_expr, body, else_)) = higher::if_block(&expr);
++            if let ExprKind::MethodCall(segment, _, args) = &if_expr.kind;
++            if segment.ident.name == sym!(is_none);
++            if Self::expression_returns_none(cx, body);
++            if let Some(subject) = args.get(0);
++            if Self::is_option(cx, subject);
++
++            then {
++                let mut applicability = Applicability::MachineApplicable;
++                let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability);
++                let mut replacement: Option<String> = None;
++                if let Some(else_) = else_ {
++                    if_chain! {
++                        if let ExprKind::Block(block, None) = &else_.kind;
++                        if block.stmts.is_empty();
++                        if let Some(block_expr) = &block.expr;
++                        if SpanlessEq::new(cx).ignore_fn().eq_expr(subject, block_expr);
++                        then {
++                            replacement = Some(format!("Some({}?)", receiver_str));
++                        }
++                    }
++                } else if Self::moves_by_default(cx, subject)
++                    && !matches!(subject.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
++                {
++                    replacement = Some(format!("{}.as_ref()?;", receiver_str));
++                } else {
++                    replacement = Some(format!("{}?;", receiver_str));
++                }
++
++                if let Some(replacement_str) = replacement {
++                    span_lint_and_sugg(
++                        cx,
++                        QUESTION_MARK,
++                        expr.span,
++                        "this block may be rewritten with the `?` operator",
++                        "replace it with",
++                        replacement_str,
++                        applicability,
++                    )
++               }
++            }
++        }
++    }
++
++    fn check_if_let_some_and_early_return_none(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++        if_chain! {
++            if let ExprKind::Match(subject, arms, source) = &expr.kind;
++            if *source == MatchSource::IfLetDesugar { contains_else_clause: true };
++            if Self::is_option(cx, subject);
++
++            if let PatKind::TupleStruct(path1, fields, None) = &arms[0].pat.kind;
++            if match_qpath(path1, &["Some"]);
++            if let PatKind::Binding(annot, _, bind, _) = &fields[0].kind;
++            let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
++
++            if let ExprKind::Block(block, None) = &arms[0].body.kind;
++            if block.stmts.is_empty();
++            if let Some(trailing_expr) = &block.expr;
++            if let ExprKind::Path(path) = &trailing_expr.kind;
++            if match_qpath(path, &[&bind.as_str()]);
++
++            if let PatKind::Wild = arms[1].pat.kind;
++            if Self::expression_returns_none(cx, arms[1].body);
++            then {
++                let mut applicability = Applicability::MachineApplicable;
++                let receiver_str = snippet_with_applicability(cx, subject.span, "..", &mut applicability);
++                let replacement = format!(
++                    "{}{}?",
++                    receiver_str,
++                    if by_ref { ".as_ref()" } else { "" },
++                );
++
++                span_lint_and_sugg(
++                    cx,
++                    QUESTION_MARK,
++                    expr.span,
++                    "this if-let-else may be rewritten with the `?` operator",
++                    "replace it with",
++                    replacement,
++                    applicability,
++                )
++            }
++        }
++    }
++
++    fn moves_by_default(cx: &LateContext<'_, '_>, expression: &Expr<'_>) -> bool {
++        let expr_ty = cx.tables.expr_ty(expression);
++
++        !expr_ty.is_copy_modulo_regions(cx.tcx, cx.param_env, expression.span)
++    }
++
++    fn is_option(cx: &LateContext<'_, '_>, expression: &Expr<'_>) -> bool {
++        let expr_ty = cx.tables.expr_ty(expression);
++
++        is_type_diagnostic_item(cx, expr_ty, sym!(option_type))
++    }
++
++    fn expression_returns_none(cx: &LateContext<'_, '_>, expression: &Expr<'_>) -> bool {
++        match expression.kind {
++            ExprKind::Block(ref block, _) => {
++                if let Some(return_expression) = Self::return_expression(block) {
++                    return Self::expression_returns_none(cx, &return_expression);
++                }
++
++                false
++            },
++            ExprKind::Ret(Some(ref expr)) => Self::expression_returns_none(cx, expr),
++            ExprKind::Path(ref qp) => {
++                if let Res::Def(DefKind::Ctor(def::CtorOf::Variant, def::CtorKind::Const), def_id) =
++                    cx.tables.qpath_res(qp, expression.hir_id)
++                {
++                    return match_def_path(cx, def_id, &paths::OPTION_NONE);
++                }
++
++                false
++            },
++            _ => false,
++        }
++    }
++
++    fn return_expression<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
++        // Check if last expression is a return statement. Then, return the expression
++        if_chain! {
++            if block.stmts.len() == 1;
++            if let Some(expr) = block.stmts.iter().last();
++            if let StmtKind::Semi(ref expr) = expr.kind;
++            if let ExprKind::Ret(ret_expr) = expr.kind;
++            if let Some(ret_expr) = ret_expr;
++
++            then {
++                return Some(ret_expr);
++            }
++        }
++
++        // Check for `return` without a semicolon.
++        if_chain! {
++            if block.stmts.is_empty();
++            if let Some(ExprKind::Ret(Some(ret_expr))) = block.expr.as_ref().map(|e| &e.kind);
++            then {
++                return Some(ret_expr);
++            }
++        }
++
++        None
++    }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for QuestionMark {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        Self::check_is_none_and_early_return_none(cx, expr);
++        Self::check_if_let_some_and_early_return_none(cx, expr);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d7ce2e66d69fbb3af1fa666169e3f5c8f0b79443
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,237 @@@
++use if_chain::if_chain;
++use rustc_ast::ast::RangeLimits;
++use rustc_errors::Applicability;
++use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Spanned;
++
++use crate::utils::sugg::Sugg;
++use crate::utils::{higher, SpanlessEq};
++use crate::utils::{is_integer_const, snippet, snippet_opt, span_lint, span_lint_and_then};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for zipping a collection with the range of
++    /// `0.._.len()`.
++    ///
++    /// **Why is this bad?** The code is better expressed with `.enumerate()`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let x = vec![1];
++    /// x.iter().zip(0..x.len());
++    /// ```
++    /// Could be written as
++    /// ```rust
++    /// # let x = vec![1];
++    /// x.iter().enumerate();
++    /// ```
++    pub RANGE_ZIP_WITH_LEN,
++    complexity,
++    "zipping iterator with a range when `enumerate()` would do"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for exclusive ranges where 1 is added to the
++    /// upper bound, e.g., `x..(y+1)`.
++    ///
++    /// **Why is this bad?** The code is more readable with an inclusive range
++    /// like `x..=y`.
++    ///
++    /// **Known problems:** Will add unnecessary pair of parentheses when the
++    /// expression is not wrapped in a pair but starts with a opening parenthesis
++    /// and ends with a closing one.
++    /// I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`.
++    ///
++    /// Also in many cases, inclusive ranges are still slower to run than
++    /// exclusive ranges, because they essentially add an extra branch that
++    /// LLVM may fail to hoist out of the loop.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// for x..(y+1) { .. }
++    /// ```
++    /// Could be written as
++    /// ```rust,ignore
++    /// for x..=y { .. }
++    /// ```
++    pub RANGE_PLUS_ONE,
++    pedantic,
++    "`x..(y+1)` reads better as `x..=y`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for inclusive ranges where 1 is subtracted from
++    /// the upper bound, e.g., `x..=(y-1)`.
++    ///
++    /// **Why is this bad?** The code is more readable with an exclusive range
++    /// like `x..y`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// for x..=(y-1) { .. }
++    /// ```
++    /// Could be written as
++    /// ```rust,ignore
++    /// for x..y { .. }
++    /// ```
++    pub RANGE_MINUS_ONE,
++    complexity,
++    "`x..=(y-1)` reads better as `x..y`"
++}
++
++declare_lint_pass!(Ranges => [
++    RANGE_ZIP_WITH_LEN,
++    RANGE_PLUS_ONE,
++    RANGE_MINUS_ONE
++]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Ranges {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind {
++            let name = path.ident.as_str();
++            if name == "zip" && args.len() == 2 {
++                let iter = &args[0].kind;
++                let zip_arg = &args[1];
++                if_chain! {
++                    // `.iter()` call
++                    if let ExprKind::MethodCall(ref iter_path, _, ref iter_args ) = *iter;
++                    if iter_path.ident.name == sym!(iter);
++                    // range expression in `.zip()` call: `0..x.len()`
++                    if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(cx, zip_arg);
++                    if is_integer_const(cx, start, 0);
++                    // `.len()` call
++                    if let ExprKind::MethodCall(ref len_path, _, ref len_args) = end.kind;
++                    if len_path.ident.name == sym!(len) && len_args.len() == 1;
++                    // `.iter()` and `.len()` called on same `Path`
++                    if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind;
++                    if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind;
++                    if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments);
++                     then {
++                         span_lint(cx,
++                                   RANGE_ZIP_WITH_LEN,
++                                   expr.span,
++                                   &format!("It is more idiomatic to use `{}.iter().enumerate()`",
++                                            snippet(cx, iter_args[0].span, "_")));
++                    }
++                }
++            }
++        }
++
++        check_exclusive_range_plus_one(cx, expr);
++        check_inclusive_range_minus_one(cx, expr);
++    }
++}
++
++// exclusive range plus one: `x..(y+1)`
++fn check_exclusive_range_plus_one(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++    if_chain! {
++        if let Some(higher::Range {
++            start,
++            end: Some(end),
++            limits: RangeLimits::HalfOpen
++        }) = higher::range(cx, expr);
++        if let Some(y) = y_plus_one(cx, end);
++        then {
++            let span = if expr.span.from_expansion() {
++                expr.span
++                    .ctxt()
++                    .outer_expn_data()
++                    .call_site
++            } else {
++                expr.span
++            };
++            span_lint_and_then(
++                cx,
++                RANGE_PLUS_ONE,
++                span,
++                "an inclusive range would be more readable",
++                |diag| {
++                    let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").to_string());
++                    let end = Sugg::hir(cx, y, "y");
++                    if let Some(is_wrapped) = &snippet_opt(cx, span) {
++                        if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') {
++                            diag.span_suggestion(
++                                span,
++                                "use",
++                                format!("({}..={})", start, end),
++                                Applicability::MaybeIncorrect,
++                            );
++                        } else {
++                            diag.span_suggestion(
++                                span,
++                                "use",
++                                format!("{}..={}", start, end),
++                                Applicability::MachineApplicable, // snippet
++                            );
++                        }
++                    }
++                },
++            );
++        }
++    }
++}
++
++// inclusive range minus one: `x..=(y-1)`
++fn check_inclusive_range_minus_one(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++    if_chain! {
++        if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::range(cx, expr);
++        if let Some(y) = y_minus_one(cx, end);
++        then {
++            span_lint_and_then(
++                cx,
++                RANGE_MINUS_ONE,
++                expr.span,
++                "an exclusive range would be more readable",
++                |diag| {
++                    let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").to_string());
++                    let end = Sugg::hir(cx, y, "y");
++                    diag.span_suggestion(
++                        expr.span,
++                        "use",
++                        format!("{}..{}", start, end),
++                        Applicability::MachineApplicable, // snippet
++                    );
++                },
++            );
++        }
++    }
++}
++
++fn y_plus_one<'t>(cx: &LateContext<'_, '_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
++    match expr.kind {
++        ExprKind::Binary(
++            Spanned {
++                node: BinOpKind::Add, ..
++            },
++            ref lhs,
++            ref rhs,
++        ) => {
++            if is_integer_const(cx, lhs, 1) {
++                Some(rhs)
++            } else if is_integer_const(cx, rhs, 1) {
++                Some(lhs)
++            } else {
++                None
++            }
++        },
++        _ => None,
++    }
++}
++
++fn y_minus_one<'t>(cx: &LateContext<'_, '_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
++    match expr.kind {
++        ExprKind::Binary(
++            Spanned {
++                node: BinOpKind::Sub, ..
++            },
++            ref lhs,
++            ref rhs,
++        ) if is_integer_const(cx, rhs, 1) => Some(lhs),
++        _ => None,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d5cace0c647461b4b1440f99b969366e51b464dc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,613 @@@
++use crate::utils::{
++    fn_has_unsatisfiable_preds, has_drop, is_copy, is_type_diagnostic_item, match_def_path, match_type, paths,
++    snippet_opt, span_lint_hir, span_lint_hir_and_then, walk_ptrs_ty_depth,
++};
++use if_chain::if_chain;
++use rustc_data_structures::{fx::FxHashMap, transitive_relation::TransitiveRelation};
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::FnKind;
++use rustc_hir::{def_id, Body, FnDecl, HirId};
++use rustc_index::bit_set::{BitSet, HybridBitSet};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::mir::{
++    self, traversal,
++    visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _},
++};
++use rustc_middle::ty::{self, fold::TypeVisitor, Ty};
++use rustc_mir::dataflow::BottomValue;
++use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::{BytePos, Span};
++use std::convert::TryFrom;
++
++macro_rules! unwrap_or_continue {
++    ($x:expr) => {
++        match $x {
++            Some(x) => x,
++            None => continue,
++        }
++    };
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for a redundant `clone()` (and its relatives) which clones an owned
++    /// value that is going to be dropped without further use.
++    ///
++    /// **Why is this bad?** It is not always possible for the compiler to eliminate useless
++    /// allocations and deallocations generated by redundant `clone()`s.
++    ///
++    /// **Known problems:**
++    ///
++    /// False-negatives: analysis performed by this lint is conservative and limited.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # use std::path::Path;
++    /// # #[derive(Clone)]
++    /// # struct Foo;
++    /// # impl Foo {
++    /// #     fn new() -> Self { Foo {} }
++    /// # }
++    /// # fn call(x: Foo) {}
++    /// {
++    ///     let x = Foo::new();
++    ///     call(x.clone());
++    ///     call(x.clone()); // this can just pass `x`
++    /// }
++    ///
++    /// ["lorem", "ipsum"].join(" ").to_string();
++    ///
++    /// Path::new("/a/b").join("c").to_path_buf();
++    /// ```
++    pub REDUNDANT_CLONE,
++    perf,
++    "`clone()` of an owned value that is going to be dropped immediately"
++}
++
++declare_lint_pass!(RedundantClone => [REDUNDANT_CLONE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
++    #[allow(clippy::too_many_lines)]
++    fn check_fn(
++        &mut self,
++        cx: &LateContext<'a, 'tcx>,
++        _: FnKind<'tcx>,
++        _: &'tcx FnDecl<'_>,
++        body: &'tcx Body<'_>,
++        _: Span,
++        _: HirId,
++    ) {
++        let def_id = cx.tcx.hir().body_owner_def_id(body.id());
++
++        // Building MIR for `fn`s with unsatisfiable preds results in ICE.
++        if fn_has_unsatisfiable_preds(cx, def_id.to_def_id()) {
++            return;
++        }
++
++        let mir = cx.tcx.optimized_mir(def_id.to_def_id());
++
++        let maybe_storage_live_result = MaybeStorageLive
++            .into_engine(cx.tcx, mir, def_id.to_def_id())
++            .iterate_to_fixpoint()
++            .into_results_cursor(mir);
++        let mut possible_borrower = {
++            let mut vis = PossibleBorrowerVisitor::new(cx, mir);
++            vis.visit_body(&mir);
++            vis.into_map(cx, maybe_storage_live_result)
++        };
++
++        for (bb, bbdata) in mir.basic_blocks().iter_enumerated() {
++            let terminator = bbdata.terminator();
++
++            if terminator.source_info.span.from_expansion() {
++                continue;
++            }
++
++            // Give up on loops
++            if terminator.successors().any(|s| *s == bb) {
++                continue;
++            }
++
++            let (fn_def_id, arg, arg_ty, clone_ret) =
++                unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind));
++
++            let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
++                || match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD)
++                || (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD)
++                    && is_type_diagnostic_item(cx, arg_ty, sym!(string_type)));
++
++            let from_deref = !from_borrow
++                && (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF)
++                    || match_def_path(cx, fn_def_id, &paths::OS_STR_TO_OS_STRING));
++
++            if !from_borrow && !from_deref {
++                continue;
++            }
++
++            // `{ cloned = &arg; clone(move cloned); }` or `{ cloned = &arg; to_path_buf(cloned); }`
++            let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(cx, mir, arg, from_borrow, bb));
++
++            let loc = mir::Location {
++                block: bb,
++                statement_index: bbdata.statements.len(),
++            };
++
++            // `Local` to be cloned, and a local of `clone` call's destination
++            let (local, ret_local) = if from_borrow {
++                // `res = clone(arg)` can be turned into `res = move arg;`
++                // if `arg` is the only borrow of `cloned` at this point.
++
++                if cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc) {
++                    continue;
++                }
++
++                (cloned, clone_ret)
++            } else {
++                // `arg` is a reference as it is `.deref()`ed in the previous block.
++                // Look into the predecessor block and find out the source of deref.
++
++                let ps = &mir.predecessors()[bb];
++                if ps.len() != 1 {
++                    continue;
++                }
++                let pred_terminator = mir[ps[0]].terminator();
++
++                // receiver of the `deref()` call
++                let (pred_arg, deref_clone_ret) = if_chain! {
++                    if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, res)) =
++                        is_call_with_ref_arg(cx, mir, &pred_terminator.kind);
++                    if res == cloned;
++                    if match_def_path(cx, pred_fn_def_id, &paths::DEREF_TRAIT_METHOD);
++                    if match_type(cx, pred_arg_ty, &paths::PATH_BUF)
++                        || match_type(cx, pred_arg_ty, &paths::OS_STRING);
++                    then {
++                        (pred_arg, res)
++                    } else {
++                        continue;
++                    }
++                };
++
++                let (local, cannot_move_out) =
++                    unwrap_or_continue!(find_stmt_assigns_to(cx, mir, pred_arg, true, ps[0]));
++                let loc = mir::Location {
++                    block: bb,
++                    statement_index: mir.basic_blocks()[bb].statements.len(),
++                };
++
++                // This can be turned into `res = move local` if `arg` and `cloned` are not borrowed
++                // at the last statement:
++                //
++                // ```
++                // pred_arg = &local;
++                // cloned = deref(pred_arg);
++                // arg = &cloned;
++                // StorageDead(pred_arg);
++                // res = to_path_buf(cloned);
++                // ```
++                if cannot_move_out || !possible_borrower.only_borrowers(&[arg, cloned], local, loc) {
++                    continue;
++                }
++
++                (local, deref_clone_ret)
++            };
++
++            let is_temp = mir.local_kind(ret_local) == mir::LocalKind::Temp;
++
++            // 1. `local` can be moved out if it is not used later.
++            // 2. If `ret_local` is a temporary and is neither consumed nor mutated, we can remove this `clone`
++            // call anyway.
++            let (used, consumed_or_mutated) = traversal::ReversePostorder::new(&mir, bb).skip(1).fold(
++                (false, !is_temp),
++                |(used, consumed), (tbb, tdata)| {
++                    // Short-circuit
++                    if (used && consumed) ||
++                        // Give up on loops
++                        tdata.terminator().successors().any(|s| *s == bb)
++                    {
++                        return (true, true);
++                    }
++
++                    let mut vis = LocalUseVisitor {
++                        used: (local, false),
++                        consumed_or_mutated: (ret_local, false),
++                    };
++                    vis.visit_basic_block_data(tbb, tdata);
++                    (used || vis.used.1, consumed || vis.consumed_or_mutated.1)
++                },
++            );
++
++            if !used || !consumed_or_mutated {
++                let span = terminator.source_info.span;
++                let scope = terminator.source_info.scope;
++                let node = mir.source_scopes[scope]
++                    .local_data
++                    .as_ref()
++                    .assert_crate_local()
++                    .lint_root;
++
++                if_chain! {
++                    if let Some(snip) = snippet_opt(cx, span);
++                    if let Some(dot) = snip.rfind('.');
++                    then {
++                        let sugg_span = span.with_lo(
++                            span.lo() + BytePos(u32::try_from(dot).unwrap())
++                        );
++                        let mut app = Applicability::MaybeIncorrect;
++
++                        let mut call_snip = &snip[dot + 1..];
++                        // Machine applicable when `call_snip` looks like `foobar()`
++                        if call_snip.ends_with("()") {
++                            call_snip = call_snip[..call_snip.len()-2].trim();
++                            if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') {
++                                app = Applicability::MachineApplicable;
++                            }
++                        }
++
++                        span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| {
++                            diag.span_suggestion(
++                                sugg_span,
++                                "remove this",
++                                String::new(),
++                                app,
++                            );
++                            if used {
++                                diag.span_note(
++                                    span,
++                                    "cloned value is neither consumed nor mutated",
++                                );
++                            } else {
++                                diag.span_note(
++                                    span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())),
++                                    "this value is dropped without further use",
++                                );
++                            }
++                        });
++                    } else {
++                        span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone");
++                    }
++                }
++            }
++        }
++    }
++}
++
++/// If `kind` is `y = func(x: &T)` where `T: !Copy`, returns `(DefId of func, x, T, y)`.
++fn is_call_with_ref_arg<'tcx>(
++    cx: &LateContext<'_, 'tcx>,
++    mir: &'tcx mir::Body<'tcx>,
++    kind: &'tcx mir::TerminatorKind<'tcx>,
++) -> Option<(def_id::DefId, mir::Local, Ty<'tcx>, mir::Local)> {
++    if_chain! {
++        if let mir::TerminatorKind::Call { func, args, destination, .. } = kind;
++        if args.len() == 1;
++        if let mir::Operand::Move(mir::Place { local, .. }) = &args[0];
++        if let ty::FnDef(def_id, _) = func.ty(&*mir, cx.tcx).kind;
++        if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
++        if !is_copy(cx, inner_ty);
++        then {
++            Some((def_id, *local, inner_ty, destination.as_ref().map(|(dest, _)| dest)?.as_local()?))
++        } else {
++            None
++        }
++    }
++}
++
++type CannotMoveOut = bool;
++
++/// Finds the first `to = (&)from`, and returns
++/// ``Some((from, whether `from` cannot be moved out))``.
++fn find_stmt_assigns_to<'tcx>(
++    cx: &LateContext<'_, 'tcx>,
++    mir: &mir::Body<'tcx>,
++    to_local: mir::Local,
++    by_ref: bool,
++    bb: mir::BasicBlock,
++) -> Option<(mir::Local, CannotMoveOut)> {
++    let rvalue = mir.basic_blocks()[bb].statements.iter().rev().find_map(|stmt| {
++        if let mir::StatementKind::Assign(box (mir::Place { local, .. }, v)) = &stmt.kind {
++            return if *local == to_local { Some(v) } else { None };
++        }
++
++        None
++    })?;
++
++    match (by_ref, &*rvalue) {
++        (true, mir::Rvalue::Ref(_, _, place)) | (false, mir::Rvalue::Use(mir::Operand::Copy(place))) => {
++            base_local_and_movability(cx, mir, *place)
++        },
++        (false, mir::Rvalue::Ref(_, _, place)) => {
++            if let [mir::ProjectionElem::Deref] = place.as_ref().projection {
++                base_local_and_movability(cx, mir, *place)
++            } else {
++                None
++            }
++        },
++        _ => None,
++    }
++}
++
++/// Extracts and returns the undermost base `Local` of given `place`. Returns `place` itself
++/// if it is already a `Local`.
++///
++/// Also reports whether given `place` cannot be moved out.
++fn base_local_and_movability<'tcx>(
++    cx: &LateContext<'_, 'tcx>,
++    mir: &mir::Body<'tcx>,
++    place: mir::Place<'tcx>,
++) -> Option<(mir::Local, CannotMoveOut)> {
++    use rustc_middle::mir::PlaceRef;
++
++    // Dereference. You cannot move things out from a borrowed value.
++    let mut deref = false;
++    // Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509.
++    let mut field = false;
++    // If projection is a slice index then clone can be removed only if the
++    // underlying type implements Copy
++    let mut slice = false;
++
++    let PlaceRef { local, mut projection } = place.as_ref();
++    while let [base @ .., elem] = projection {
++        projection = base;
++        deref |= matches!(elem, mir::ProjectionElem::Deref);
++        field |= matches!(elem, mir::ProjectionElem::Field(..))
++            && has_drop(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty);
++        slice |= matches!(elem, mir::ProjectionElem::Index(..))
++            && !is_copy(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty);
++    }
++
++    Some((local, deref || field || slice))
++}
++
++struct LocalUseVisitor {
++    used: (mir::Local, bool),
++    consumed_or_mutated: (mir::Local, bool),
++}
++
++impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
++    fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) {
++        let statements = &data.statements;
++        for (statement_index, statement) in statements.iter().enumerate() {
++            self.visit_statement(statement, mir::Location { block, statement_index });
++        }
++
++        self.visit_terminator(
++            data.terminator(),
++            mir::Location {
++                block,
++                statement_index: statements.len(),
++            },
++        );
++    }
++
++    fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, _: mir::Location) {
++        let local = place.local;
++
++        if local == self.used.0
++            && !matches!(ctx, PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_))
++        {
++            self.used.1 = true;
++        }
++
++        if local == self.consumed_or_mutated.0 {
++            match ctx {
++                PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
++                | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
++                    self.consumed_or_mutated.1 = true;
++                },
++                _ => {},
++            }
++        }
++    }
++}
++
++/// Determines liveness of each local purely based on `StorageLive`/`Dead`.
++#[derive(Copy, Clone)]
++struct MaybeStorageLive;
++
++impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive {
++    type Idx = mir::Local;
++    const NAME: &'static str = "maybe_storage_live";
++
++    fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
++        body.local_decls.len()
++    }
++
++    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
++        for arg in body.args_iter() {
++            state.insert(arg);
++        }
++    }
++}
++
++impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive {
++    fn statement_effect(&self, trans: &mut impl GenKill<Self::Idx>, stmt: &mir::Statement<'tcx>, _: mir::Location) {
++        match stmt.kind {
++            mir::StatementKind::StorageLive(l) => trans.gen(l),
++            mir::StatementKind::StorageDead(l) => trans.kill(l),
++            _ => (),
++        }
++    }
++
++    fn terminator_effect(
++        &self,
++        _trans: &mut impl GenKill<Self::Idx>,
++        _terminator: &mir::Terminator<'tcx>,
++        _loc: mir::Location,
++    ) {
++    }
++
++    fn call_return_effect(
++        &self,
++        _in_out: &mut impl GenKill<Self::Idx>,
++        _block: mir::BasicBlock,
++        _func: &mir::Operand<'tcx>,
++        _args: &[mir::Operand<'tcx>],
++        _return_place: mir::Place<'tcx>,
++    ) {
++        // Nothing to do when a call returns successfully
++    }
++}
++
++impl BottomValue for MaybeStorageLive {
++    /// bottom = dead
++    const BOTTOM_VALUE: bool = false;
++}
++
++/// Collects the possible borrowers of each local.
++/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
++/// possible borrowers of `a`.
++struct PossibleBorrowerVisitor<'a, 'tcx> {
++    possible_borrower: TransitiveRelation<mir::Local>,
++    body: &'a mir::Body<'tcx>,
++    cx: &'a LateContext<'a, 'tcx>,
++}
++
++impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> {
++    fn new(cx: &'a LateContext<'a, 'tcx>, body: &'a mir::Body<'tcx>) -> Self {
++        Self {
++            possible_borrower: TransitiveRelation::default(),
++            cx,
++            body,
++        }
++    }
++
++    fn into_map(
++        self,
++        cx: &LateContext<'a, 'tcx>,
++        maybe_live: ResultsCursor<'tcx, 'tcx, MaybeStorageLive>,
++    ) -> PossibleBorrowerMap<'a, 'tcx> {
++        let mut map = FxHashMap::default();
++        for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
++            if is_copy(cx, self.body.local_decls[row].ty) {
++                continue;
++            }
++
++            let borrowers = self.possible_borrower.reachable_from(&row);
++            if !borrowers.is_empty() {
++                let mut bs = HybridBitSet::new_empty(self.body.local_decls.len());
++                for &c in borrowers {
++                    if c != mir::Local::from_usize(0) {
++                        bs.insert(c);
++                    }
++                }
++
++                if !bs.is_empty() {
++                    map.insert(row, bs);
++                }
++            }
++        }
++
++        let bs = BitSet::new_empty(self.body.local_decls.len());
++        PossibleBorrowerMap {
++            map,
++            maybe_live,
++            bitset: (bs.clone(), bs),
++        }
++    }
++}
++
++impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> {
++    fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
++        let lhs = place.local;
++        match rvalue {
++            mir::Rvalue::Ref(_, _, borrowed) => {
++                self.possible_borrower.add(borrowed.local, lhs);
++            },
++            other => {
++                if !ContainsRegion.visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) {
++                    return;
++                }
++                rvalue_locals(other, |rhs| {
++                    if lhs != rhs {
++                        self.possible_borrower.add(rhs, lhs);
++                    }
++                });
++            },
++        }
++    }
++
++    fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) {
++        if let mir::TerminatorKind::Call {
++            args,
++            destination: Some((mir::Place { local: dest, .. }, _)),
++            ..
++        } = &terminator.kind
++        {
++            // If the call returns something with lifetimes,
++            // let's conservatively assume the returned value contains lifetime of all the arguments.
++            // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`.
++            if !ContainsRegion.visit_ty(&self.body.local_decls[*dest].ty) {
++                return;
++            }
++
++            for op in args {
++                match op {
++                    mir::Operand::Copy(p) | mir::Operand::Move(p) => {
++                        self.possible_borrower.add(p.local, *dest);
++                    },
++                    _ => (),
++                }
++            }
++        }
++    }
++}
++
++struct ContainsRegion;
++
++impl TypeVisitor<'_> for ContainsRegion {
++    fn visit_region(&mut self, _: ty::Region<'_>) -> bool {
++        true
++    }
++}
++
++fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
++    use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use};
++
++    let mut visit_op = |op: &mir::Operand<'_>| match op {
++        mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local),
++        _ => (),
++    };
++
++    match rvalue {
++        Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op),
++        Aggregate(_, ops) => ops.iter().for_each(visit_op),
++        BinaryOp(_, lhs, rhs) | CheckedBinaryOp(_, lhs, rhs) => {
++            visit_op(lhs);
++            visit_op(rhs);
++        },
++        _ => (),
++    }
++}
++
++/// Result of `PossibleBorrowerVisitor`.
++struct PossibleBorrowerMap<'a, 'tcx> {
++    /// Mapping `Local -> its possible borrowers`
++    map: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
++    maybe_live: ResultsCursor<'a, 'tcx, MaybeStorageLive>,
++    // Caches to avoid allocation of `BitSet` on every query
++    bitset: (BitSet<mir::Local>, BitSet<mir::Local>),
++}
++
++impl PossibleBorrowerMap<'_, '_> {
++    /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`.
++    fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool {
++        self.maybe_live.seek_after(at);
++
++        self.bitset.0.clear();
++        let maybe_live = &mut self.maybe_live;
++        if let Some(bitset) = self.map.get(&borrowed) {
++            for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) {
++                self.bitset.0.insert(b);
++            }
++        } else {
++            return false;
++        }
++
++        self.bitset.1.clear();
++        for b in borrowers {
++            self.bitset.1.insert(*b);
++        }
++
++        self.bitset.0 == self.bitset.1
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b12c3c344ef4cbfc1fd61078157edf224d3e1bbb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++use crate::utils::span_lint_and_sugg;
++use rustc_ast::ast::{Expr, ExprKind};
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for fields in struct literals where shorthands
++    /// could be used.
++    ///
++    /// **Why is this bad?** If the field and variable names are the same,
++    /// the field name is redundant.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let bar: u8 = 123;
++    ///
++    /// struct Foo {
++    ///     bar: u8,
++    /// }
++    ///
++    /// let foo = Foo { bar: bar };
++    /// ```
++    /// the last line can be simplified to
++    /// ```ignore
++    /// let foo = Foo { bar };
++    /// ```
++    pub REDUNDANT_FIELD_NAMES,
++    style,
++    "checks for fields in struct literals where shorthands could be used"
++}
++
++declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);
++
++impl EarlyLintPass for RedundantFieldNames {
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++        if let ExprKind::Struct(_, ref fields, _) = expr.kind {
++            for field in fields {
++                if field.is_shorthand {
++                    continue;
++                }
++                if let ExprKind::Path(None, path) = &field.expr.kind {
++                    if path.segments.len() == 1
++                        && path.segments[0].ident == field.ident
++                        && path.segments[0].args.is_none()
++                    {
++                        span_lint_and_sugg(
++                            cx,
++                            REDUNDANT_FIELD_NAMES,
++                            field.span,
++                            "redundant field names in struct initialization",
++                            "replace it with",
++                            field.ident.to_string(),
++                            Applicability::MachineApplicable,
++                        );
++                    }
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7ee298e9833f2ffd17b9ca83ab3fbcc2db051d3d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,216 @@@
++use crate::utils::{match_qpath, match_trait_method, paths, snippet, span_lint_and_then};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Lint for redundant pattern matching over `Result` or
++    /// `Option`
++    ///
++    /// **Why is this bad?** It's more concise and clear to just use the proper
++    /// utility function
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// if let Ok(_) = Ok::<i32, i32>(42) {}
++    /// if let Err(_) = Err::<i32, i32>(42) {}
++    /// if let None = None::<()> {}
++    /// if let Some(_) = Some(42) {}
++    /// match Ok::<i32, i32>(42) {
++    ///     Ok(_) => true,
++    ///     Err(_) => false,
++    /// };
++    /// ```
++    ///
++    /// The more idiomatic use would be:
++    ///
++    /// ```rust
++    /// if Ok::<i32, i32>(42).is_ok() {}
++    /// if Err::<i32, i32>(42).is_err() {}
++    /// if None::<()>.is_none() {}
++    /// if Some(42).is_some() {}
++    /// Ok::<i32, i32>(42).is_ok();
++    /// ```
++    pub REDUNDANT_PATTERN_MATCHING,
++    style,
++    "use the proper utility function avoiding an `if let`"
++}
++
++declare_lint_pass!(RedundantPatternMatching => [REDUNDANT_PATTERN_MATCHING]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantPatternMatching {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
++            match match_source {
++                MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms),
++                MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"),
++                MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"),
++                _ => return,
++            }
++        }
++    }
++}
++
++fn find_sugg_for_if_let<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &'tcx Expr<'_>,
++    op: &Expr<'_>,
++    arms: &[Arm<'_>],
++    keyword: &'static str,
++) {
++    let good_method = match arms[0].pat.kind {
++        PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => {
++            if let PatKind::Wild = patterns[0].kind {
++                if match_qpath(path, &paths::RESULT_OK) {
++                    "is_ok()"
++                } else if match_qpath(path, &paths::RESULT_ERR) {
++                    "is_err()"
++                } else if match_qpath(path, &paths::OPTION_SOME) {
++                    "is_some()"
++                } else {
++                    return;
++                }
++            } else {
++                return;
++            }
++        },
++
++        PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => "is_none()",
++
++        _ => return,
++    };
++
++    // check that `while_let_on_iterator` lint does not trigger
++    if_chain! {
++        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);
++        then {
++            return;
++        }
++    }
++
++    span_lint_and_then(
++        cx,
++        REDUNDANT_PATTERN_MATCHING,
++        arms[0].pat.span,
++        &format!("redundant pattern matching, consider using `{}`", good_method),
++        |diag| {
++            // while let ... = ... { ... }
++            // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++            let expr_span = expr.span;
++
++            // while let ... = ... { ... }
++            //                 ^^^
++            let op_span = op.span.source_callsite();
++
++            // while let ... = ... { ... }
++            // ^^^^^^^^^^^^^^^^^^^
++            let span = expr_span.until(op_span.shrink_to_hi());
++            diag.span_suggestion(
++                span,
++                "try this",
++                format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method),
++                Applicability::MachineApplicable, // snippet
++            );
++        },
++    );
++}
++
++fn find_sugg_for_match<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
++    if arms.len() == 2 {
++        let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
++
++        let found_good_method = match node_pair {
++            (
++                PatKind::TupleStruct(ref path_left, ref patterns_left, _),
++                PatKind::TupleStruct(ref path_right, ref patterns_right, _),
++            ) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
++                if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
++                    find_good_method_for_match(
++                        arms,
++                        path_left,
++                        path_right,
++                        &paths::RESULT_OK,
++                        &paths::RESULT_ERR,
++                        "is_ok()",
++                        "is_err()",
++                    )
++                } else {
++                    None
++                }
++            },
++            (PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right))
++            | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _))
++                if patterns.len() == 1 =>
++            {
++                if let PatKind::Wild = patterns[0].kind {
++                    find_good_method_for_match(
++                        arms,
++                        path_left,
++                        path_right,
++                        &paths::OPTION_SOME,
++                        &paths::OPTION_NONE,
++                        "is_some()",
++                        "is_none()",
++                    )
++                } else {
++                    None
++                }
++            },
++            _ => None,
++        };
++
++        if let Some(good_method) = found_good_method {
++            span_lint_and_then(
++                cx,
++                REDUNDANT_PATTERN_MATCHING,
++                expr.span,
++                &format!("redundant pattern matching, consider using `{}`", good_method),
++                |diag| {
++                    let span = expr.span.to(op.span);
++                    diag.span_suggestion(
++                        span,
++                        "try this",
++                        format!("{}.{}", snippet(cx, op.span, "_"), good_method),
++                        Applicability::MaybeIncorrect, // snippet
++                    );
++                },
++            );
++        }
++    }
++}
++
++fn find_good_method_for_match<'a>(
++    arms: &[Arm<'_>],
++    path_left: &QPath<'_>,
++    path_right: &QPath<'_>,
++    expected_left: &[&str],
++    expected_right: &[&str],
++    should_be_left: &'a str,
++    should_be_right: &'a str,
++) -> Option<&'a str> {
++    let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) {
++        (&(*arms[0].body).kind, &(*arms[1].body).kind)
++    } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) {
++        (&(*arms[1].body).kind, &(*arms[0].body).kind)
++    } else {
++        return None;
++    };
++
++    match body_node_pair {
++        (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
++            (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left),
++            (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right),
++            _ => None,
++        },
++        _ => None,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6fc07f91660ee4f6b6503e6afa7bc379fc877a7e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,78 @@@
++use crate::utils::span_lint_and_then;
++use rustc_errors::Applicability;
++use rustc_hir::{Item, ItemKind, VisibilityKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for items declared `pub(crate)` that are not crate visible because they
++    /// are inside a private module.
++    ///
++    /// **Why is this bad?** Writing `pub(crate)` is misleading when it's redundant due to the parent
++    /// module's visibility.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// mod internal {
++    ///     pub(crate) fn internal_fn() { }
++    /// }
++    /// ```
++    /// This function is not visible outside the module and it can be declared with `pub` or
++    /// private visibility
++    /// ```rust
++    /// mod internal {
++    ///     pub fn internal_fn() { }
++    /// }
++    /// ```
++    pub REDUNDANT_PUB_CRATE,
++    nursery,
++    "Using `pub(crate)` visibility on items that are not crate visible due to the visibility of the module that contains them."
++}
++
++#[derive(Default)]
++pub struct RedundantPubCrate {
++    is_exported: Vec<bool>,
++}
++
++impl_lint_pass!(RedundantPubCrate => [REDUNDANT_PUB_CRATE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantPubCrate {
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'tcx>) {
++        if let VisibilityKind::Crate { .. } = item.vis.node {
++            if !cx.access_levels.is_exported(item.hir_id) {
++                if let Some(false) = self.is_exported.last() {
++                    let span = item.span.with_hi(item.ident.span.hi());
++                    let def_id = cx.tcx.hir().local_def_id(item.hir_id);
++                    let descr = cx.tcx.def_kind(def_id).descr(def_id.to_def_id());
++                    span_lint_and_then(
++                        cx,
++                        REDUNDANT_PUB_CRATE,
++                        span,
++                        &format!("pub(crate) {} inside private module", descr),
++                        |diag| {
++                            diag.span_suggestion(
++                                item.vis.span,
++                                "consider using",
++                                "pub".to_string(),
++                                Applicability::MachineApplicable,
++                            );
++                        },
++                    )
++                }
++            }
++        }
++
++        if let ItemKind::Mod { .. } = item.kind {
++            self.is_exported.push(cx.access_levels.is_exported(item.hir_id));
++        }
++    }
++
++    fn check_item_post(&mut self, _cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'tcx>) {
++        if let ItemKind::Mod { .. } = item.kind {
++            self.is_exported.pop().expect("unbalanced check_item/check_item_post");
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c6f57298c2601a7287e8608fe2b536dff326bc78
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,99 @@@
++use crate::utils::{snippet, span_lint_and_then};
++use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for constants and statics with an explicit `'static` lifetime.
++    ///
++    /// **Why is this bad?** Adding `'static` to every reference can create very
++    /// complicated types.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
++    /// &[...]
++    /// static FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
++    /// &[...]
++    /// ```
++    /// This code can be rewritten as
++    /// ```ignore
++    ///  const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
++    ///  static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
++    /// ```
++    pub REDUNDANT_STATIC_LIFETIMES,
++    style,
++    "Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them."
++}
++
++declare_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]);
++
++impl RedundantStaticLifetimes {
++    // Recursively visit types
++    fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
++        match ty.kind {
++            // Be careful of nested structures (arrays and tuples)
++            TyKind::Array(ref ty, _) => {
++                self.visit_type(&*ty, cx, reason);
++            },
++            TyKind::Tup(ref tup) => {
++                for tup_ty in tup {
++                    self.visit_type(&*tup_ty, cx, reason);
++                }
++            },
++            // This is what we are looking for !
++            TyKind::Rptr(ref optional_lifetime, ref borrow_type) => {
++                // Match the 'static lifetime
++                if let Some(lifetime) = *optional_lifetime {
++                    match borrow_type.ty.kind {
++                        TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => {
++                            if lifetime.ident.name == rustc_span::symbol::kw::StaticLifetime {
++                                let snip = snippet(cx, borrow_type.ty.span, "<type>");
++                                let sugg = format!("&{}", snip);
++                                span_lint_and_then(
++                                    cx,
++                                    REDUNDANT_STATIC_LIFETIMES,
++                                    lifetime.ident.span,
++                                    reason,
++                                    |diag| {
++                                        diag.span_suggestion(
++                                            ty.span,
++                                            "consider removing `'static`",
++                                            sugg,
++                                            Applicability::MachineApplicable, //snippet
++                                        );
++                                    },
++                                );
++                            }
++                        },
++                        _ => {},
++                    }
++                }
++                self.visit_type(&*borrow_type.ty, cx, reason);
++            },
++            TyKind::Slice(ref ty) => {
++                self.visit_type(ty, cx, reason);
++            },
++            _ => {},
++        }
++    }
++}
++
++impl EarlyLintPass for RedundantStaticLifetimes {
++    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
++        if !item.span.from_expansion() {
++            if let ItemKind::Const(_, ref var_type, _) = item.kind {
++                self.visit_type(var_type, cx, "Constants have by default a `'static` lifetime");
++                // Don't check associated consts because `'static` cannot be elided on those (issue
++                // #2438)
++            }
++
++            if let ItemKind::Static(ref var_type, _, _) = item.kind {
++                self.visit_type(var_type, cx, "Statics have by default a `'static` lifetime");
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d5797468e9d53b8ab4bf142e5fed34b2c1fa3444
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,98 @@@
++use crate::utils::{in_macro, snippet_with_applicability, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_ast::ast::{Expr, ExprKind, UnOp};
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `*&` and `*&mut` in expressions.
++    ///
++    /// **Why is this bad?** Immediately dereferencing a reference is no-op and
++    /// makes the code less clear.
++    ///
++    /// **Known problems:** Multiple dereference/addrof pairs are not handled so
++    /// the suggested fix for `x = **&&y` is `x = *&y`, which is still incorrect.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// let a = f(*&mut b);
++    /// let c = *&d;
++    /// ```
++    pub DEREF_ADDROF,
++    complexity,
++    "use of `*&` or `*&mut` in an expression"
++}
++
++declare_lint_pass!(DerefAddrOf => [DEREF_ADDROF]);
++
++fn without_parens(mut e: &Expr) -> &Expr {
++    while let ExprKind::Paren(ref child_e) = e.kind {
++        e = child_e;
++    }
++    e
++}
++
++impl EarlyLintPass for DerefAddrOf {
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
++        if_chain! {
++            if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind;
++            if let ExprKind::AddrOf(_, _, ref addrof_target) = without_parens(deref_target).kind;
++            if !in_macro(addrof_target.span);
++            then {
++                let mut applicability = Applicability::MachineApplicable;
++                span_lint_and_sugg(
++                    cx,
++                    DEREF_ADDROF,
++                    e.span,
++                    "immediately dereferencing a reference",
++                    "try this",
++                    format!("{}", snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability)),
++                    applicability,
++                );
++            }
++        }
++    }
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for references in expressions that use
++    /// auto dereference.
++    ///
++    /// **Why is this bad?** The reference is a no-op and is automatically
++    /// dereferenced by the compiler and makes the code less clear.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// struct Point(u32, u32);
++    /// let point = Point(30, 20);
++    /// let x = (&point).0;
++    /// ```
++    pub REF_IN_DEREF,
++    complexity,
++    "Use of reference in auto dereference expression."
++}
++
++declare_lint_pass!(RefInDeref => [REF_IN_DEREF]);
++
++impl EarlyLintPass for RefInDeref {
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
++        if_chain! {
++            if let ExprKind::Field(ref object, _) = e.kind;
++            if let ExprKind::Paren(ref parened) = object.kind;
++            if let ExprKind::AddrOf(_, _, ref inner) = parened.kind;
++            then {
++                let mut applicability = Applicability::MachineApplicable;
++                span_lint_and_sugg(
++                    cx,
++                    REF_IN_DEREF,
++                    object.span,
++                    "Creating a reference that is immediately dereferenced.",
++                    "try this",
++                    snippet_with_applicability(cx, inner.span, "_", &mut applicability).to_string(),
++                    applicability,
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..30084e3e1ffce993a19efd30787bfd4c15fadf93
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,263 @@@
++use crate::consts::{constant, Constant};
++use crate::utils::{is_expn_of, match_def_path, match_type, paths, span_lint, span_lint_and_help};
++use if_chain::if_chain;
++use rustc_ast::ast::{LitKind, StrStyle};
++use rustc_data_structures::fx::FxHashSet;
++use rustc_hir::{Block, BorrowKind, Crate, Expr, ExprKind, HirId};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::{BytePos, Span};
++use std::convert::TryFrom;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks [regex](https://crates.io/crates/regex) creation
++    /// (with `Regex::new`,`RegexBuilder::new` or `RegexSet::new`) for correct
++    /// regex syntax.
++    ///
++    /// **Why is this bad?** This will lead to a runtime panic.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// Regex::new("|")
++    /// ```
++    pub INVALID_REGEX,
++    correctness,
++    "invalid regular expressions"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for trivial [regex](https://crates.io/crates/regex)
++    /// creation (with `Regex::new`, `RegexBuilder::new` or `RegexSet::new`).
++    ///
++    /// **Why is this bad?** Matching the regex can likely be replaced by `==` or
++    /// `str::starts_with`, `str::ends_with` or `std::contains` or other `str`
++    /// methods.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// Regex::new("^foobar")
++    /// ```
++    pub TRIVIAL_REGEX,
++    style,
++    "trivial regular expressions"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `regex!(_)` which (as of now) is
++    /// usually slower than `Regex::new(_)` unless called in a loop (which is a bad
++    /// idea anyway).
++    ///
++    /// **Why is this bad?** Performance, at least for now. The macro version is
++    /// likely to catch up long-term, but for now the dynamic version is faster.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// regex!("foo|bar")
++    /// ```
++    pub REGEX_MACRO,
++    style,
++    "use of `regex!(_)` instead of `Regex::new(_)`"
++}
++
++#[derive(Clone, Default)]
++pub struct Regex {
++    spans: FxHashSet<Span>,
++    last: Option<HirId>,
++}
++
++impl_lint_pass!(Regex => [INVALID_REGEX, REGEX_MACRO, TRIVIAL_REGEX]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Regex {
++    fn check_crate(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx Crate<'_>) {
++        self.spans.clear();
++    }
++
++    fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>) {
++        if_chain! {
++            if self.last.is_none();
++            if let Some(ref expr) = block.expr;
++            if match_type(cx, cx.tables.expr_ty(expr), &paths::REGEX);
++            if let Some(span) = is_expn_of(expr.span, "regex");
++            then {
++                if !self.spans.contains(&span) {
++                    span_lint(cx,
++                              REGEX_MACRO,
++                              span,
++                              "`regex!(_)` found. \
++                              Please use `Regex::new(_)`, which is faster for now.");
++                    self.spans.insert(span);
++                }
++                self.last = Some(block.hir_id);
++            }
++        }
++    }
++
++    fn check_block_post(&mut self, _: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>) {
++        if self.last.map_or(false, |id| block.hir_id == id) {
++            self.last = None;
++        }
++    }
++
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            if let ExprKind::Call(ref fun, ref args) = expr.kind;
++            if let ExprKind::Path(ref qpath) = fun.kind;
++            if args.len() == 1;
++            if let Some(def_id) = cx.tables.qpath_res(qpath, fun.hir_id).opt_def_id();
++            then {
++                if match_def_path(cx, def_id, &paths::REGEX_NEW) ||
++                   match_def_path(cx, def_id, &paths::REGEX_BUILDER_NEW) {
++                    check_regex(cx, &args[0], true);
++                } else if match_def_path(cx, def_id, &paths::REGEX_BYTES_NEW) ||
++                   match_def_path(cx, def_id, &paths::REGEX_BYTES_BUILDER_NEW) {
++                    check_regex(cx, &args[0], false);
++                } else if match_def_path(cx, def_id, &paths::REGEX_SET_NEW) {
++                    check_set(cx, &args[0], true);
++                } else if match_def_path(cx, def_id, &paths::REGEX_BYTES_SET_NEW) {
++                    check_set(cx, &args[0], false);
++                }
++            }
++        }
++    }
++}
++
++#[allow(clippy::cast_possible_truncation)] // truncation very unlikely here
++#[must_use]
++fn str_span(base: Span, c: regex_syntax::ast::Span, offset: u16) -> Span {
++    let offset = u32::from(offset);
++    let end = base.lo() + BytePos(u32::try_from(c.end.offset).expect("offset too large") + offset);
++    let start = base.lo() + BytePos(u32::try_from(c.start.offset).expect("offset too large") + offset);
++    assert!(start <= end);
++    Span::new(start, end, base.ctxt())
++}
++
++fn const_str<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) -> Option<String> {
++    constant(cx, cx.tables, e).and_then(|(c, _)| match c {
++        Constant::Str(s) => Some(s),
++        _ => None,
++    })
++}
++
++fn is_trivial_regex(s: &regex_syntax::hir::Hir) -> Option<&'static str> {
++    use regex_syntax::hir::Anchor::{EndText, StartText};
++    use regex_syntax::hir::HirKind::{Alternation, Anchor, Concat, Empty, Literal};
++
++    let is_literal = |e: &[regex_syntax::hir::Hir]| {
++        e.iter().all(|e| match *e.kind() {
++            Literal(_) => true,
++            _ => false,
++        })
++    };
++
++    match *s.kind() {
++        Empty | Anchor(_) => Some("the regex is unlikely to be useful as it is"),
++        Literal(_) => Some("consider using `str::contains`"),
++        Alternation(ref exprs) => {
++            if exprs.iter().all(|e| e.kind().is_empty()) {
++                Some("the regex is unlikely to be useful as it is")
++            } else {
++                None
++            }
++        },
++        Concat(ref exprs) => match (exprs[0].kind(), exprs[exprs.len() - 1].kind()) {
++            (&Anchor(StartText), &Anchor(EndText)) if exprs[1..(exprs.len() - 1)].is_empty() => {
++                Some("consider using `str::is_empty`")
++            },
++            (&Anchor(StartText), &Anchor(EndText)) if is_literal(&exprs[1..(exprs.len() - 1)]) => {
++                Some("consider using `==` on `str`s")
++            },
++            (&Anchor(StartText), &Literal(_)) if is_literal(&exprs[1..]) => Some("consider using `str::starts_with`"),
++            (&Literal(_), &Anchor(EndText)) if is_literal(&exprs[1..(exprs.len() - 1)]) => {
++                Some("consider using `str::ends_with`")
++            },
++            _ if is_literal(exprs) => Some("consider using `str::contains`"),
++            _ => None,
++        },
++        _ => None,
++    }
++}
++
++fn check_set<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
++    if_chain! {
++        if let ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) = expr.kind;
++        if let ExprKind::Array(exprs) = expr.kind;
++        then {
++            for expr in exprs {
++                check_regex(cx, expr, utf8);
++            }
++        }
++    }
++}
++
++fn check_regex<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
++    let mut parser = regex_syntax::ParserBuilder::new()
++        .unicode(utf8)
++        .allow_invalid_utf8(!utf8)
++        .build();
++
++    if let ExprKind::Lit(ref lit) = expr.kind {
++        if let LitKind::Str(ref r, style) = lit.node {
++            let r = &r.as_str();
++            let offset = if let StrStyle::Raw(n) = style { 2 + n } else { 1 };
++            match parser.parse(r) {
++                Ok(r) => {
++                    if let Some(repl) = is_trivial_regex(&r) {
++                        span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl);
++                    }
++                },
++                Err(regex_syntax::Error::Parse(e)) => {
++                    span_lint(
++                        cx,
++                        INVALID_REGEX,
++                        str_span(expr.span, *e.span(), offset),
++                        &format!("regex syntax error: {}", e.kind()),
++                    );
++                },
++                Err(regex_syntax::Error::Translate(e)) => {
++                    span_lint(
++                        cx,
++                        INVALID_REGEX,
++                        str_span(expr.span, *e.span(), offset),
++                        &format!("regex syntax error: {}", e.kind()),
++                    );
++                },
++                Err(e) => {
++                    span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {}", e));
++                },
++            }
++        }
++    } else if let Some(r) = const_str(cx, expr) {
++        match parser.parse(&r) {
++            Ok(r) => {
++                if let Some(repl) = is_trivial_regex(&r) {
++                    span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl);
++                }
++            },
++            Err(regex_syntax::Error::Parse(e)) => {
++                span_lint(
++                    cx,
++                    INVALID_REGEX,
++                    expr.span,
++                    &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()),
++                );
++            },
++            Err(regex_syntax::Error::Translate(e)) => {
++                span_lint(
++                    cx,
++                    INVALID_REGEX,
++                    expr.span,
++                    &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()),
++                );
++            },
++            Err(e) => {
++                span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {}", e));
++            },
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5c9117d5b81cd6c62de819caa00c87e626fe8b77
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,339 @@@
++use if_chain::if_chain;
++use rustc_ast::ast;
++use rustc_ast::visit::FnKind;
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++use rustc_span::BytePos;
++
++use crate::utils::{in_macro, match_path_ast, snippet_opt, span_lint_and_sugg, span_lint_and_then};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for return statements at the end of a block.
++    ///
++    /// **Why is this bad?** Removing the `return` and semicolon will make the code
++    /// more rusty.
++    ///
++    /// **Known problems:** If the computation returning the value borrows a local
++    /// variable, removing the `return` may run afoul of the borrow checker.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// fn foo(x: usize) -> usize {
++    ///     return x;
++    /// }
++    /// ```
++    /// simplify to
++    /// ```rust
++    /// fn foo(x: usize) -> usize {
++    ///     x
++    /// }
++    /// ```
++    pub NEEDLESS_RETURN,
++    style,
++    "using a return statement like `return expr;` where an expression would suffice"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `let`-bindings, which are subsequently
++    /// returned.
++    ///
++    /// **Why is this bad?** It is just extraneous code. Remove it to make your code
++    /// more rusty.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// fn foo() -> String {
++    ///     let x = String::new();
++    ///     x
++    /// }
++    /// ```
++    /// instead, use
++    /// ```
++    /// fn foo() -> String {
++    ///     String::new()
++    /// }
++    /// ```
++    pub LET_AND_RETURN,
++    style,
++    "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for unit (`()`) expressions that can be removed.
++    ///
++    /// **Why is this bad?** Such expressions add no value, but can make the code
++    /// less readable. Depending on formatting they can make a `break` or `return`
++    /// statement look like a function call.
++    ///
++    /// **Known problems:** The lint currently misses unit return types in types,
++    /// e.g., the `F` in `fn generic_unit<F: Fn() -> ()>(f: F) { .. }`.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// fn return_unit() -> () {
++    ///     ()
++    /// }
++    /// ```
++    pub UNUSED_UNIT,
++    style,
++    "needless unit expression"
++}
++
++#[derive(PartialEq, Eq, Copy, Clone)]
++enum RetReplacement {
++    Empty,
++    Block,
++}
++
++declare_lint_pass!(Return => [NEEDLESS_RETURN, LET_AND_RETURN, UNUSED_UNIT]);
++
++impl Return {
++    // Check the final stmt or expr in a block for unnecessary return.
++    fn check_block_return(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
++        if let Some(stmt) = block.stmts.last() {
++            match stmt.kind {
++                ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => {
++                    self.check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
++                },
++                _ => (),
++            }
++        }
++    }
++
++    // Check a the final expression in a block if it's a return.
++    fn check_final_expr(
++        &mut self,
++        cx: &EarlyContext<'_>,
++        expr: &ast::Expr,
++        span: Option<Span>,
++        replacement: RetReplacement,
++    ) {
++        match expr.kind {
++            // simple return is always "bad"
++            ast::ExprKind::Ret(ref inner) => {
++                // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
++                if !expr.attrs.iter().any(attr_is_cfg) {
++                    Self::emit_return_lint(
++                        cx,
++                        span.expect("`else return` is not possible"),
++                        inner.as_ref().map(|i| i.span),
++                        replacement,
++                    );
++                }
++            },
++            // a whole block? check it!
++            ast::ExprKind::Block(ref block, _) => {
++                self.check_block_return(cx, block);
++            },
++            // an if/if let expr, check both exprs
++            // note, if without else is going to be a type checking error anyways
++            // (except for unit type functions) so we don't match it
++            ast::ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => {
++                self.check_block_return(cx, ifblock);
++                self.check_final_expr(cx, elsexpr, None, RetReplacement::Empty);
++            },
++            // a match expr, check all arms
++            ast::ExprKind::Match(_, ref arms) => {
++                for arm in arms {
++                    self.check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block);
++                }
++            },
++            _ => (),
++        }
++    }
++
++    fn emit_return_lint(cx: &EarlyContext<'_>, ret_span: Span, inner_span: Option<Span>, replacement: RetReplacement) {
++        match inner_span {
++            Some(inner_span) => {
++                if in_external_macro(cx.sess(), inner_span) || inner_span.from_expansion() {
++                    return;
++                }
++
++                span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
++                    if let Some(snippet) = snippet_opt(cx, inner_span) {
++                        diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable);
++                    }
++                })
++            },
++            None => match replacement {
++                RetReplacement::Empty => {
++                    span_lint_and_sugg(
++                        cx,
++                        NEEDLESS_RETURN,
++                        ret_span,
++                        "unneeded `return` statement",
++                        "remove `return`",
++                        String::new(),
++                        Applicability::MachineApplicable,
++                    );
++                },
++                RetReplacement::Block => {
++                    span_lint_and_sugg(
++                        cx,
++                        NEEDLESS_RETURN,
++                        ret_span,
++                        "unneeded `return` statement",
++                        "replace `return` with an empty block",
++                        "{}".to_string(),
++                        Applicability::MachineApplicable,
++                    );
++                },
++            },
++        }
++    }
++
++    // Check for "let x = EXPR; x"
++    fn check_let_return(cx: &EarlyContext<'_>, block: &ast::Block) {
++        let mut it = block.stmts.iter();
++
++        // we need both a let-binding stmt and an expr
++        if_chain! {
++            if let Some(retexpr) = it.next_back();
++            if let ast::StmtKind::Expr(ref retexpr) = retexpr.kind;
++            if let Some(stmt) = it.next_back();
++            if let ast::StmtKind::Local(ref local) = stmt.kind;
++            // don't lint in the presence of type inference
++            if local.ty.is_none();
++            if local.attrs.is_empty();
++            if let Some(ref initexpr) = local.init;
++            if let ast::PatKind::Ident(_, ident, _) = local.pat.kind;
++            if let ast::ExprKind::Path(_, ref path) = retexpr.kind;
++            if match_path_ast(path, &[&*ident.name.as_str()]);
++            if !in_external_macro(cx.sess(), initexpr.span);
++            if !in_external_macro(cx.sess(), retexpr.span);
++            if !in_external_macro(cx.sess(), local.span);
++            if !in_macro(local.span);
++            then {
++                span_lint_and_then(
++                    cx,
++                    LET_AND_RETURN,
++                    retexpr.span,
++                    "returning the result of a `let` binding from a block",
++                    |err| {
++                        err.span_label(local.span, "unnecessary `let` binding");
++
++                        if let Some(snippet) = snippet_opt(cx, initexpr.span) {
++                            err.multipart_suggestion(
++                                "return the expression directly",
++                                vec![
++                                    (local.span, String::new()),
++                                    (retexpr.span, snippet),
++                                ],
++                                Applicability::MachineApplicable,
++                            );
++                        } else {
++                            err.span_help(initexpr.span, "this expression can be directly returned");
++                        }
++                    },
++                );
++            }
++        }
++    }
++}
++
++impl EarlyLintPass for Return {
++    fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) {
++        match kind {
++            FnKind::Fn(.., Some(block)) => self.check_block_return(cx, block),
++            FnKind::Closure(_, body) => self.check_final_expr(cx, body, Some(body.span), RetReplacement::Empty),
++            FnKind::Fn(.., None) => {},
++        }
++        if_chain! {
++            if let ast::FnRetTy::Ty(ref ty) = kind.decl().output;
++            if let ast::TyKind::Tup(ref vals) = ty.kind;
++            if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span);
++            then {
++                let (rspan, appl) = if let Ok(fn_source) =
++                        cx.sess().source_map()
++                                 .span_to_snippet(span.with_hi(ty.span.hi())) {
++                    if let Some(rpos) = fn_source.rfind("->") {
++                        #[allow(clippy::cast_possible_truncation)]
++                        (ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
++                            Applicability::MachineApplicable)
++                    } else {
++                        (ty.span, Applicability::MaybeIncorrect)
++                    }
++                } else {
++                    (ty.span, Applicability::MaybeIncorrect)
++                };
++                span_lint_and_sugg(
++                    cx,
++                    UNUSED_UNIT,
++                    rspan,
++                    "unneeded unit return type",
++                    "remove the `-> ()`",
++                    String::new(),
++                    appl,
++                );
++            }
++        }
++    }
++
++    fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
++        Self::check_let_return(cx, block);
++        if_chain! {
++            if let Some(ref stmt) = block.stmts.last();
++            if let ast::StmtKind::Expr(ref expr) = stmt.kind;
++            if is_unit_expr(expr) && !stmt.span.from_expansion();
++            then {
++                let sp = expr.span;
++                span_lint_and_sugg(
++                    cx,
++                    UNUSED_UNIT,
++                    sp,
++                    "unneeded unit expression",
++                    "remove the final `()`",
++                    String::new(),
++                    Applicability::MachineApplicable,
++                );
++            }
++        }
++    }
++
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
++        match e.kind {
++            ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => {
++                if is_unit_expr(expr) && !expr.span.from_expansion() {
++                    span_lint_and_sugg(
++                        cx,
++                        UNUSED_UNIT,
++                        expr.span,
++                        "unneeded `()`",
++                        "remove the `()`",
++                        String::new(),
++                        Applicability::MachineApplicable,
++                    );
++                }
++            },
++            _ => (),
++        }
++    }
++}
++
++fn attr_is_cfg(attr: &ast::Attribute) -> bool {
++    attr.meta_item_list().is_some() && attr.check_name(sym!(cfg))
++}
++
++// get the def site
++#[must_use]
++fn get_def(span: Span) -> Option<Span> {
++    if span.from_expansion() {
++        Some(span.ctxt().outer_expn_data().def_site)
++    } else {
++        None
++    }
++}
++
++// is this expr a `()` unit?
++fn is_unit_expr(expr: &ast::Expr) -> bool {
++    if let ast::ExprKind::Tup(ref vals) = expr.kind {
++        vals.is_empty()
++    } else {
++        false
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6820d1620bd187abec727ba6025f09e385926dc5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,57 @@@
++use crate::utils::{get_trait_def_id, paths, span_lint};
++use rustc_hir::{Item, ItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for mis-uses of the serde API.
++    ///
++    /// **Why is this bad?** Serde is very finnicky about how its API should be
++    /// used, but the type system can't be used to enforce it (yet?).
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:** Implementing `Visitor::visit_string` but not
++    /// `Visitor::visit_str`.
++    pub SERDE_API_MISUSE,
++    correctness,
++    "various things that will negatively affect your serde experience"
++}
++
++declare_lint_pass!(SerdeAPI => [SERDE_API_MISUSE]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for SerdeAPI {
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++        if let ItemKind::Impl {
++            of_trait: Some(ref trait_ref),
++            items,
++            ..
++        } = item.kind
++        {
++            let did = trait_ref.path.res.def_id();
++            if let Some(visit_did) = get_trait_def_id(cx, &paths::SERDE_DE_VISITOR) {
++                if did == visit_did {
++                    let mut seen_str = None;
++                    let mut seen_string = None;
++                    for item in items {
++                        match &*item.ident.as_str() {
++                            "visit_str" => seen_str = Some(item.span),
++                            "visit_string" => seen_string = Some(item.span),
++                            _ => {},
++                        }
++                    }
++                    if let Some(span) = seen_string {
++                        if seen_str.is_none() {
++                            span_lint(
++                                cx,
++                                SERDE_API_MISUSE,
++                                span,
++                                "you should not implement `visit_string` without also implementing `visit_str`",
++                            );
++                        }
++                    }
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..11360b0ef84955caf66cc128cbbbb2132cc96554
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,389 @@@
++use crate::reexport::Name;
++use crate::utils::{contains_name, higher, iter_input_pats, snippet, span_lint_and_then};
++use rustc_hir::intravisit::FnKind;
++use rustc_hir::{
++    Block, Body, Expr, ExprKind, FnDecl, Guard, HirId, Local, MutTy, Pat, PatKind, Path, QPath, StmtKind, Ty, TyKind,
++    UnOp,
++};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for bindings that shadow other bindings already in
++    /// scope, while just changing reference level or mutability.
++    ///
++    /// **Why is this bad?** Not much, in fact it's a very common pattern in Rust
++    /// code. Still, some may opt to avoid it in their code base, they can set this
++    /// lint to `Warn`.
++    ///
++    /// **Known problems:** This lint, as the other shadowing related lints,
++    /// currently only catches very simple patterns.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let x = 1;
++    /// let x = &x;
++    /// ```
++    pub SHADOW_SAME,
++    restriction,
++    "rebinding a name to itself, e.g., `let mut x = &mut x`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for bindings that shadow other bindings already in
++    /// scope, while reusing the original value.
++    ///
++    /// **Why is this bad?** Not too much, in fact it's a common pattern in Rust
++    /// code. Still, some argue that name shadowing like this hurts readability,
++    /// because a value may be bound to different things depending on position in
++    /// the code.
++    ///
++    /// **Known problems:** This lint, as the other shadowing related lints,
++    /// currently only catches very simple patterns.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x = 2;
++    /// let x = x + 1;
++    /// ```
++    /// use different variable name:
++    /// ```rust
++    /// let x = 2;
++    /// let y = x + 1;
++    /// ```
++    pub SHADOW_REUSE,
++    restriction,
++    "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for bindings that shadow other bindings already in
++    /// scope, either without a initialization or with one that does not even use
++    /// the original value.
++    ///
++    /// **Why is this bad?** Name shadowing can hurt readability, especially in
++    /// large code bases, because it is easy to lose track of the active binding at
++    /// any place in the code. This can be alleviated by either giving more specific
++    /// names to bindings or introducing more scopes to contain the bindings.
++    ///
++    /// **Known problems:** This lint, as the other shadowing related lints,
++    /// currently only catches very simple patterns.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let y = 1;
++    /// # let z = 2;
++    /// let x = y;
++    /// let x = z; // shadows the earlier binding
++    /// ```
++    pub SHADOW_UNRELATED,
++    pedantic,
++    "rebinding a name without even using the original value"
++}
++
++declare_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Shadow {
++    fn check_fn(
++        &mut self,
++        cx: &LateContext<'a, 'tcx>,
++        _: FnKind<'tcx>,
++        decl: &'tcx FnDecl<'_>,
++        body: &'tcx Body<'_>,
++        _: Span,
++        _: HirId,
++    ) {
++        if in_external_macro(cx.sess(), body.value.span) {
++            return;
++        }
++        check_fn(cx, decl, body);
++    }
++}
++
++fn check_fn<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>) {
++    let mut bindings = Vec::with_capacity(decl.inputs.len());
++    for arg in iter_input_pats(decl, body) {
++        if let PatKind::Binding(.., ident, _) = arg.pat.kind {
++            bindings.push((ident.name, ident.span))
++        }
++    }
++    check_expr(cx, &body.value, &mut bindings);
++}
++
++fn check_block<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Name, Span)>) {
++    let len = bindings.len();
++    for stmt in block.stmts {
++        match stmt.kind {
++            StmtKind::Local(ref local) => check_local(cx, local, bindings),
++            StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => check_expr(cx, e, bindings),
++            StmtKind::Item(..) => {},
++        }
++    }
++    if let Some(ref o) = block.expr {
++        check_expr(cx, o, bindings);
++    }
++    bindings.truncate(len);
++}
++
++fn check_local<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Name, Span)>) {
++    if in_external_macro(cx.sess(), local.span) {
++        return;
++    }
++    if higher::is_from_for_desugar(local) {
++        return;
++    }
++    let Local {
++        ref pat,
++        ref ty,
++        ref init,
++        span,
++        ..
++    } = *local;
++    if let Some(ref t) = *ty {
++        check_ty(cx, t, bindings)
++    }
++    if let Some(ref o) = *init {
++        check_expr(cx, o, bindings);
++        check_pat(cx, pat, Some(o), span, bindings);
++    } else {
++        check_pat(cx, pat, None, span, bindings);
++    }
++}
++
++fn is_binding(cx: &LateContext<'_, '_>, pat_id: HirId) -> bool {
++    let var_ty = cx.tables.node_type_opt(pat_id);
++    if let Some(var_ty) = var_ty {
++        match var_ty.kind {
++            ty::Adt(..) => false,
++            _ => true,
++        }
++    } else {
++        false
++    }
++}
++
++fn check_pat<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    pat: &'tcx Pat<'_>,
++    init: Option<&'tcx Expr<'_>>,
++    span: Span,
++    bindings: &mut Vec<(Name, Span)>,
++) {
++    // TODO: match more stuff / destructuring
++    match pat.kind {
++        PatKind::Binding(.., ident, ref inner) => {
++            let name = ident.name;
++            if is_binding(cx, pat.hir_id) {
++                let mut new_binding = true;
++                for tup in bindings.iter_mut() {
++                    if tup.0 == name {
++                        lint_shadow(cx, name, span, pat.span, init, tup.1);
++                        tup.1 = ident.span;
++                        new_binding = false;
++                        break;
++                    }
++                }
++                if new_binding {
++                    bindings.push((name, ident.span));
++                }
++            }
++            if let Some(ref p) = *inner {
++                check_pat(cx, p, init, span, bindings);
++            }
++        },
++        PatKind::Struct(_, pfields, _) => {
++            if let Some(init_struct) = init {
++                if let ExprKind::Struct(_, ref efields, _) = init_struct.kind {
++                    for field in pfields {
++                        let name = field.ident.name;
++                        let efield = efields
++                            .iter()
++                            .find_map(|f| if f.ident.name == name { Some(&*f.expr) } else { None });
++                        check_pat(cx, &field.pat, efield, span, bindings);
++                    }
++                } else {
++                    for field in pfields {
++                        check_pat(cx, &field.pat, init, span, bindings);
++                    }
++                }
++            } else {
++                for field in pfields {
++                    check_pat(cx, &field.pat, None, span, bindings);
++                }
++            }
++        },
++        PatKind::Tuple(inner, _) => {
++            if let Some(init_tup) = init {
++                if let ExprKind::Tup(ref tup) = init_tup.kind {
++                    for (i, p) in inner.iter().enumerate() {
++                        check_pat(cx, p, Some(&tup[i]), p.span, bindings);
++                    }
++                } else {
++                    for p in inner {
++                        check_pat(cx, p, init, span, bindings);
++                    }
++                }
++            } else {
++                for p in inner {
++                    check_pat(cx, p, None, span, bindings);
++                }
++            }
++        },
++        PatKind::Box(ref inner) => {
++            if let Some(initp) = init {
++                if let ExprKind::Box(ref inner_init) = initp.kind {
++                    check_pat(cx, inner, Some(&**inner_init), span, bindings);
++                } else {
++                    check_pat(cx, inner, init, span, bindings);
++                }
++            } else {
++                check_pat(cx, inner, init, span, bindings);
++            }
++        },
++        PatKind::Ref(ref inner, _) => check_pat(cx, inner, init, span, bindings),
++        // PatVec(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
++        _ => (),
++    }
++}
++
++fn lint_shadow<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    name: Name,
++    span: Span,
++    pattern_span: Span,
++    init: Option<&'tcx Expr<'_>>,
++    prev_span: Span,
++) {
++    if let Some(expr) = init {
++        if is_self_shadow(name, expr) {
++            span_lint_and_then(
++                cx,
++                SHADOW_SAME,
++                span,
++                &format!(
++                    "`{}` is shadowed by itself in `{}`",
++                    snippet(cx, pattern_span, "_"),
++                    snippet(cx, expr.span, "..")
++                ),
++                |diag| {
++                    diag.span_note(prev_span, "previous binding is here");
++                },
++            );
++        } else if contains_name(name, expr) {
++            span_lint_and_then(
++                cx,
++                SHADOW_REUSE,
++                pattern_span,
++                &format!(
++                    "`{}` is shadowed by `{}` which reuses the original value",
++                    snippet(cx, pattern_span, "_"),
++                    snippet(cx, expr.span, "..")
++                ),
++                |diag| {
++                    diag.span_note(expr.span, "initialization happens here");
++                    diag.span_note(prev_span, "previous binding is here");
++                },
++            );
++        } else {
++            span_lint_and_then(
++                cx,
++                SHADOW_UNRELATED,
++                pattern_span,
++                &format!(
++                    "`{}` is shadowed by `{}`",
++                    snippet(cx, pattern_span, "_"),
++                    snippet(cx, expr.span, "..")
++                ),
++                |diag| {
++                    diag.span_note(expr.span, "initialization happens here");
++                    diag.span_note(prev_span, "previous binding is here");
++                },
++            );
++        }
++    } else {
++        span_lint_and_then(
++            cx,
++            SHADOW_UNRELATED,
++            span,
++            &format!("`{}` shadows a previous declaration", snippet(cx, pattern_span, "_")),
++            |diag| {
++                diag.span_note(prev_span, "previous binding is here");
++            },
++        );
++    }
++}
++
++fn check_expr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Name, Span)>) {
++    if in_external_macro(cx.sess(), expr.span) {
++        return;
++    }
++    match expr.kind {
++        ExprKind::Unary(_, ref e)
++        | ExprKind::Field(ref e, _)
++        | ExprKind::AddrOf(_, _, ref e)
++        | ExprKind::Box(ref e) => check_expr(cx, e, bindings),
++        ExprKind::Block(ref block, _) | ExprKind::Loop(ref block, _, _) => check_block(cx, block, bindings),
++        // ExprKind::Call
++        // ExprKind::MethodCall
++        ExprKind::Array(v) | ExprKind::Tup(v) => {
++            for e in v {
++                check_expr(cx, e, bindings)
++            }
++        },
++        ExprKind::Match(ref init, arms, _) => {
++            check_expr(cx, init, bindings);
++            let len = bindings.len();
++            for arm in arms {
++                check_pat(cx, &arm.pat, Some(&**init), arm.pat.span, bindings);
++                // This is ugly, but needed to get the right type
++                if let Some(ref guard) = arm.guard {
++                    match guard {
++                        Guard::If(if_expr) => check_expr(cx, if_expr, bindings),
++                    }
++                }
++                check_expr(cx, &arm.body, bindings);
++                bindings.truncate(len);
++            }
++        },
++        _ => (),
++    }
++}
++
++fn check_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Name, Span)>) {
++    match ty.kind {
++        TyKind::Slice(ref sty) => check_ty(cx, sty, bindings),
++        TyKind::Array(ref fty, ref anon_const) => {
++            check_ty(cx, fty, bindings);
++            check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings);
++        },
++        TyKind::Ptr(MutTy { ty: ref mty, .. }) | TyKind::Rptr(_, MutTy { ty: ref mty, .. }) => {
++            check_ty(cx, mty, bindings)
++        },
++        TyKind::Tup(tup) => {
++            for t in tup {
++                check_ty(cx, t, bindings)
++            }
++        },
++        TyKind::Typeof(ref anon_const) => check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings),
++        _ => (),
++    }
++}
++
++fn is_self_shadow(name: Name, expr: &Expr<'_>) -> bool {
++    match expr.kind {
++        ExprKind::Box(ref inner) | ExprKind::AddrOf(_, _, ref inner) => is_self_shadow(name, inner),
++        ExprKind::Block(ref block, _) => {
++            block.stmts.is_empty() && block.expr.as_ref().map_or(false, |e| is_self_shadow(name, e))
++        },
++        ExprKind::Unary(op, ref inner) => (UnOp::UnDeref == op) && is_self_shadow(name, inner),
++        ExprKind::Path(QPath::Resolved(_, ref path)) => path_eq_name(name, path),
++        _ => false,
++    }
++}
++
++fn path_eq_name(name: Name, path: &Path<'_>) -> bool {
++    !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.as_str() == name.as_str()
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8d767a7fec88d60d88a47eabb0eb3210917cda3c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++use crate::utils::{in_macro, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_ast::ast::{Item, ItemKind, UseTreeKind};
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::edition::Edition;
++
++declare_clippy_lint! {
++    /// **What it does:** Checking for imports with single component use path.
++    ///
++    /// **Why is this bad?** Import with single component use path such as `use cratename;`
++    /// is not necessary, and thus should be removed.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust, ignore
++    /// use regex;
++    ///
++    /// fn main() {
++    ///     regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
++    /// }
++    /// ```
++    /// Better as
++    /// ```rust, ignore
++    /// fn main() {
++    ///     regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
++    /// }
++    /// ```
++    pub SINGLE_COMPONENT_PATH_IMPORTS,
++    style,
++    "imports with single component path are redundant"
++}
++
++declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]);
++
++impl EarlyLintPass for SingleComponentPathImports {
++    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
++        if_chain! {
++            if !in_macro(item.span);
++            if cx.sess.opts.edition == Edition::Edition2018;
++            if !item.vis.node.is_pub();
++            if let ItemKind::Use(use_tree) = &item.kind;
++            if let segments = &use_tree.prefix.segments;
++            if segments.len() == 1;
++            if let UseTreeKind::Simple(None, _, _) = use_tree.kind;
++            then {
++                span_lint_and_sugg(
++                    cx,
++                    SINGLE_COMPONENT_PATH_IMPORTS,
++                    item.span,
++                    "this import is redundant",
++                    "remove it entirely",
++                    String::new(),
++                    Applicability::MachineApplicable
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fb3706be1c2135bc581f42b66c27b072b20dec0d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,324 @@@
++use crate::utils::sugg::Sugg;
++use crate::utils::{get_enclosing_block, match_qpath, span_lint_and_then, SpanlessEq};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, NestedVisitorMap, Visitor};
++use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, Stmt, StmtKind};
++use rustc_lint::{LateContext, LateLintPass, Lint};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::symbol::Symbol;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks slow zero-filled vector initialization
++    ///
++    /// **Why is this bad?** These structures are non-idiomatic and less efficient than simply using
++    /// `vec![0; len]`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # use core::iter::repeat;
++    /// # let len = 4;
++    /// let mut vec1 = Vec::with_capacity(len);
++    /// vec1.resize(len, 0);
++    ///
++    /// let mut vec2 = Vec::with_capacity(len);
++    /// vec2.extend(repeat(0).take(len))
++    /// ```
++    pub SLOW_VECTOR_INITIALIZATION,
++    perf,
++    "slow vector initialization"
++}
++
++declare_lint_pass!(SlowVectorInit => [SLOW_VECTOR_INITIALIZATION]);
++
++/// `VecAllocation` contains data regarding a vector allocated with `with_capacity` and then
++/// assigned to a variable. For example, `let mut vec = Vec::with_capacity(0)` or
++/// `vec = Vec::with_capacity(0)`
++struct VecAllocation<'tcx> {
++    /// Symbol of the local variable name
++    variable_name: Symbol,
++
++    /// Reference to the expression which allocates the vector
++    allocation_expr: &'tcx Expr<'tcx>,
++
++    /// Reference to the expression used as argument on `with_capacity` call. This is used
++    /// to only match slow zero-filling idioms of the same length than vector initialization.
++    len_expr: &'tcx Expr<'tcx>,
++}
++
++/// Type of slow initialization
++enum InitializationType<'tcx> {
++    /// Extend is a slow initialization with the form `vec.extend(repeat(0).take(..))`
++    Extend(&'tcx Expr<'tcx>),
++
++    /// Resize is a slow initialization with the form `vec.resize(.., 0)`
++    Resize(&'tcx Expr<'tcx>),
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for SlowVectorInit {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        // Matches initialization on reassignements. For example: `vec = Vec::with_capacity(100)`
++        if_chain! {
++            if let ExprKind::Assign(ref left, ref right, _) = expr.kind;
++
++            // Extract variable name
++            if let ExprKind::Path(QPath::Resolved(_, ref path)) = left.kind;
++            if let Some(variable_name) = path.segments.get(0);
++
++            // Extract len argument
++            if let Some(ref len_arg) = Self::is_vec_with_capacity(right);
++
++            then {
++                let vi = VecAllocation {
++                    variable_name: variable_name.ident.name,
++                    allocation_expr: right,
++                    len_expr: len_arg,
++                };
++
++                Self::search_initialization(cx, vi, expr.hir_id);
++            }
++        }
++    }
++
++    fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) {
++        // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)`
++        if_chain! {
++            if let StmtKind::Local(ref local) = stmt.kind;
++            if let PatKind::Binding(BindingAnnotation::Mutable, .., variable_name, None) = local.pat.kind;
++            if let Some(ref init) = local.init;
++            if let Some(ref len_arg) = Self::is_vec_with_capacity(init);
++
++            then {
++                let vi = VecAllocation {
++                    variable_name: variable_name.name,
++                    allocation_expr: init,
++                    len_expr: len_arg,
++                };
++
++                Self::search_initialization(cx, vi, stmt.hir_id);
++            }
++        }
++    }
++}
++
++impl SlowVectorInit {
++    /// Checks if the given expression is `Vec::with_capacity(..)`. It will return the expression
++    /// of the first argument of `with_capacity` call if it matches or `None` if it does not.
++    fn is_vec_with_capacity<'tcx>(expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
++        if_chain! {
++            if let ExprKind::Call(ref func, ref args) = expr.kind;
++            if let ExprKind::Path(ref path) = func.kind;
++            if match_qpath(path, &["Vec", "with_capacity"]);
++            if args.len() == 1;
++
++            then {
++                return Some(&args[0]);
++            }
++        }
++
++        None
++    }
++
++    /// Search initialization for the given vector
++    fn search_initialization<'tcx>(cx: &LateContext<'_, 'tcx>, vec_alloc: VecAllocation<'tcx>, parent_node: HirId) {
++        let enclosing_body = get_enclosing_block(cx, parent_node);
++
++        if enclosing_body.is_none() {
++            return;
++        }
++
++        let mut v = VectorInitializationVisitor {
++            cx,
++            vec_alloc,
++            slow_expression: None,
++            initialization_found: false,
++        };
++
++        v.visit_block(enclosing_body.unwrap());
++
++        if let Some(ref allocation_expr) = v.slow_expression {
++            Self::lint_initialization(cx, allocation_expr, &v.vec_alloc);
++        }
++    }
++
++    fn lint_initialization<'tcx>(
++        cx: &LateContext<'_, 'tcx>,
++        initialization: &InitializationType<'tcx>,
++        vec_alloc: &VecAllocation<'_>,
++    ) {
++        match initialization {
++            InitializationType::Extend(e) | InitializationType::Resize(e) => Self::emit_lint(
++                cx,
++                e,
++                vec_alloc,
++                "slow zero-filling initialization",
++                SLOW_VECTOR_INITIALIZATION,
++            ),
++        };
++    }
++
++    fn emit_lint<'tcx>(
++        cx: &LateContext<'_, 'tcx>,
++        slow_fill: &Expr<'_>,
++        vec_alloc: &VecAllocation<'_>,
++        msg: &str,
++        lint: &'static Lint,
++    ) {
++        let len_expr = Sugg::hir(cx, vec_alloc.len_expr, "len");
++
++        span_lint_and_then(cx, lint, slow_fill.span, msg, |diag| {
++            diag.span_suggestion(
++                vec_alloc.allocation_expr.span,
++                "consider replace allocation with",
++                format!("vec![0; {}]", len_expr),
++                Applicability::Unspecified,
++            );
++        });
++    }
++}
++
++/// `VectorInitializationVisitor` searches for unsafe or slow vector initializations for the given
++/// vector.
++struct VectorInitializationVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++
++    /// Contains the information.
++    vec_alloc: VecAllocation<'tcx>,
++
++    /// Contains the slow initialization expression, if one was found.
++    slow_expression: Option<InitializationType<'tcx>>,
++
++    /// `true` if the initialization of the vector has been found on the visited block.
++    initialization_found: bool,
++}
++
++impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
++    /// Checks if the given expression is extending a vector with `repeat(0).take(..)`
++    fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            if self.initialization_found;
++            if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind;
++            if let ExprKind::Path(ref qpath_subj) = args[0].kind;
++            if match_qpath(&qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]);
++            if path.ident.name == sym!(extend);
++            if let Some(ref extend_arg) = args.get(1);
++            if self.is_repeat_take(extend_arg);
++
++            then {
++                self.slow_expression = Some(InitializationType::Extend(expr));
++            }
++        }
++    }
++
++    /// Checks if the given expression is resizing a vector with 0
++    fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            if self.initialization_found;
++            if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind;
++            if let ExprKind::Path(ref qpath_subj) = args[0].kind;
++            if match_qpath(&qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]);
++            if path.ident.name == sym!(resize);
++            if let (Some(ref len_arg), Some(fill_arg)) = (args.get(1), args.get(2));
++
++            // Check that is filled with 0
++            if let ExprKind::Lit(ref lit) = fill_arg.kind;
++            if let LitKind::Int(0, _) = lit.node;
++
++            // Check that len expression is equals to `with_capacity` expression
++            if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr);
++
++            then {
++                self.slow_expression = Some(InitializationType::Resize(expr));
++            }
++        }
++    }
++
++    /// Returns `true` if give expression is `repeat(0).take(...)`
++    fn is_repeat_take(&self, expr: &Expr<'_>) -> bool {
++        if_chain! {
++            if let ExprKind::MethodCall(ref take_path, _, ref take_args) = expr.kind;
++            if take_path.ident.name == sym!(take);
++
++            // Check that take is applied to `repeat(0)`
++            if let Some(ref repeat_expr) = take_args.get(0);
++            if Self::is_repeat_zero(repeat_expr);
++
++            // Check that len expression is equals to `with_capacity` expression
++            if let Some(ref len_arg) = take_args.get(1);
++            if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr);
++
++            then {
++                return true;
++            }
++        }
++
++        false
++    }
++
++    /// Returns `true` if given expression is `repeat(0)`
++    fn is_repeat_zero(expr: &Expr<'_>) -> bool {
++        if_chain! {
++            if let ExprKind::Call(ref fn_expr, ref repeat_args) = expr.kind;
++            if let ExprKind::Path(ref qpath_repeat) = fn_expr.kind;
++            if match_qpath(&qpath_repeat, &["repeat"]);
++            if let Some(ref repeat_arg) = repeat_args.get(0);
++            if let ExprKind::Lit(ref lit) = repeat_arg.kind;
++            if let LitKind::Int(0, _) = lit.node;
++
++            then {
++                return true
++            }
++        }
++
++        false
++    }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
++        if self.initialization_found {
++            match stmt.kind {
++                StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => {
++                    self.search_slow_extend_filling(expr);
++                    self.search_slow_resize_filling(expr);
++                },
++                _ => (),
++            }
++
++            self.initialization_found = false;
++        } else {
++            walk_stmt(self, stmt);
++        }
++    }
++
++    fn visit_block(&mut self, block: &'tcx Block<'_>) {
++        if self.initialization_found {
++            if let Some(ref s) = block.stmts.get(0) {
++                self.visit_stmt(s)
++            }
++
++            self.initialization_found = false;
++        } else {
++            walk_block(self, block);
++        }
++    }
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        // Skip all the expressions previous to the vector initialization
++        if self.vec_alloc.allocation_expr.hir_id == expr.hir_id {
++            self.initialization_found = true;
++        }
++
++        walk_expr(self, expr);
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2c51271e312dee312c7dac5a126390682b362061
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,200 @@@
++use rustc_errors::Applicability;
++use rustc_hir::{BinOpKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Spanned;
++
++use if_chain::if_chain;
++
++use crate::utils::SpanlessEq;
++use crate::utils::{get_parent_expr, is_allowed, is_type_diagnostic_item, span_lint, span_lint_and_sugg, walk_ptrs_ty};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for string appends of the form `x = x + y` (without
++    /// `let`!).
++    ///
++    /// **Why is this bad?** It's not really bad, but some people think that the
++    /// `.push_str(_)` method is more readable.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// let mut x = "Hello".to_owned();
++    /// x = x + ", World";
++    /// ```
++    pub STRING_ADD_ASSIGN,
++    pedantic,
++    "using `x = x + ..` where x is a `String` instead of `push_str()`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for all instances of `x + _` where `x` is of type
++    /// `String`, but only if [`string_add_assign`](#string_add_assign) does *not*
++    /// match.
++    ///
++    /// **Why is this bad?** It's not bad in and of itself. However, this particular
++    /// `Add` implementation is asymmetric (the other operand need not be `String`,
++    /// but `x` does), while addition as mathematically defined is symmetric, also
++    /// the `String::push_str(_)` function is a perfectly good replacement.
++    /// Therefore, some dislike it and wish not to have it in their code.
++    ///
++    /// That said, other people think that string addition, having a long tradition
++    /// in other languages is actually fine, which is why we decided to make this
++    /// particular lint `allow` by default.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// let x = "Hello".to_owned();
++    /// x + ", World";
++    /// ```
++    pub STRING_ADD,
++    restriction,
++    "using `x + ..` where x is a `String` instead of `push_str()`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for the `as_bytes` method called on string literals
++    /// that contain only ASCII characters.
++    ///
++    /// **Why is this bad?** Byte string literals (e.g., `b"foo"`) can be used
++    /// instead. They are shorter but less discoverable than `as_bytes()`.
++    ///
++    /// **Known Problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let bs = "a byte string".as_bytes();
++    /// ```
++    pub STRING_LIT_AS_BYTES,
++    style,
++    "calling `as_bytes` on a string literal instead of using a byte string literal"
++}
++
++declare_lint_pass!(StringAdd => [STRING_ADD, STRING_ADD_ASSIGN]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringAdd {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        if in_external_macro(cx.sess(), e.span) {
++            return;
++        }
++
++        if let ExprKind::Binary(
++            Spanned {
++                node: BinOpKind::Add, ..
++            },
++            ref left,
++            _,
++        ) = e.kind
++        {
++            if is_string(cx, left) {
++                if !is_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) {
++                    let parent = get_parent_expr(cx, e);
++                    if let Some(p) = parent {
++                        if let ExprKind::Assign(ref target, _, _) = p.kind {
++                            // avoid duplicate matches
++                            if SpanlessEq::new(cx).eq_expr(target, left) {
++                                return;
++                            }
++                        }
++                    }
++                }
++                span_lint(
++                    cx,
++                    STRING_ADD,
++                    e.span,
++                    "you added something to a string. Consider using `String::push_str()` instead",
++                );
++            }
++        } else if let ExprKind::Assign(ref target, ref src, _) = e.kind {
++            if is_string(cx, target) && is_add(cx, src, target) {
++                span_lint(
++                    cx,
++                    STRING_ADD_ASSIGN,
++                    e.span,
++                    "you assigned the result of adding something to this string. Consider using \
++                     `String::push_str()` instead",
++                );
++            }
++        }
++    }
++}
++
++fn is_string(cx: &LateContext<'_, '_>, e: &Expr<'_>) -> bool {
++    is_type_diagnostic_item(cx, walk_ptrs_ty(cx.tables.expr_ty(e)), sym!(string_type))
++}
++
++fn is_add(cx: &LateContext<'_, '_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
++    match src.kind {
++        ExprKind::Binary(
++            Spanned {
++                node: BinOpKind::Add, ..
++            },
++            ref left,
++            _,
++        ) => SpanlessEq::new(cx).eq_expr(target, left),
++        ExprKind::Block(ref block, _) => {
++            block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target))
++        },
++        _ => false,
++    }
++}
++
++// Max length a b"foo" string can take
++const MAX_LENGTH_BYTE_STRING_LIT: usize = 32;
++
++declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringLitAsBytes {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        use crate::utils::{snippet, snippet_with_applicability};
++        use rustc_ast::ast::LitKind;
++
++        if_chain! {
++            if let ExprKind::MethodCall(path, _, args) = &e.kind;
++            if path.ident.name == sym!(as_bytes);
++            if let ExprKind::Lit(lit) = &args[0].kind;
++            if let LitKind::Str(lit_content, _) = &lit.node;
++            then {
++                let callsite = snippet(cx, args[0].span.source_callsite(), r#""foo""#);
++                let mut applicability = Applicability::MachineApplicable;
++                if callsite.starts_with("include_str!") {
++                    span_lint_and_sugg(
++                        cx,
++                        STRING_LIT_AS_BYTES,
++                        e.span,
++                        "calling `as_bytes()` on `include_str!(..)`",
++                        "consider using `include_bytes!(..)` instead",
++                        snippet_with_applicability(cx, args[0].span, r#""foo""#, &mut applicability).replacen(
++                            "include_str",
++                            "include_bytes",
++                            1,
++                        ),
++                        applicability,
++                    );
++                } else if lit_content.as_str().is_ascii()
++                    && lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT
++                    && !args[0].span.from_expansion()
++                {
++                    span_lint_and_sugg(
++                        cx,
++                        STRING_LIT_AS_BYTES,
++                        e.span,
++                        "calling `as_bytes()` on a string literal",
++                        "consider using a byte string literal instead",
++                        format!(
++                            "b{}",
++                            snippet_with_applicability(cx, args[0].span, r#""foo""#, &mut applicability)
++                        ),
++                        applicability,
++                    );
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f1e223d9a48c65c95268893cac55b88854afcb68
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,205 @@@
++use crate::utils::{get_trait_def_id, span_lint, trait_ref_of_method};
++use if_chain::if_chain;
++use rustc_hir as hir;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Lints for suspicious operations in impls of arithmetic operators, e.g.
++    /// subtracting elements in an Add impl.
++    ///
++    /// **Why this is bad?** This is probably a typo or copy-and-paste error and not intended.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// impl Add for Foo {
++    ///     type Output = Foo;
++    ///
++    ///     fn add(self, other: Foo) -> Foo {
++    ///         Foo(self.0 - other.0)
++    ///     }
++    /// }
++    /// ```
++    pub SUSPICIOUS_ARITHMETIC_IMPL,
++    correctness,
++    "suspicious use of operators in impl of arithmetic trait"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Lints for suspicious operations in impls of OpAssign, e.g.
++    /// subtracting elements in an AddAssign impl.
++    ///
++    /// **Why this is bad?** This is probably a typo or copy-and-paste error and not intended.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// impl AddAssign for Foo {
++    ///     fn add_assign(&mut self, other: Foo) {
++    ///         *self = *self - other;
++    ///     }
++    /// }
++    /// ```
++    pub SUSPICIOUS_OP_ASSIGN_IMPL,
++    correctness,
++    "suspicious use of operators in impl of OpAssign trait"
++}
++
++declare_lint_pass!(SuspiciousImpl => [SUSPICIOUS_ARITHMETIC_IMPL, SUSPICIOUS_OP_ASSIGN_IMPL]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for SuspiciousImpl {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if let hir::ExprKind::Binary(binop, _, _) | hir::ExprKind::AssignOp(binop, ..) = expr.kind {
++            match binop.node {
++                hir::BinOpKind::Eq
++                | hir::BinOpKind::Lt
++                | hir::BinOpKind::Le
++                | hir::BinOpKind::Ne
++                | hir::BinOpKind::Ge
++                | hir::BinOpKind::Gt => return,
++                _ => {},
++            }
++            // Check if the binary expression is part of another bi/unary expression
++            // or operator assignment as a child node
++            let mut parent_expr = cx.tcx.hir().get_parent_node(expr.hir_id);
++            while parent_expr != hir::CRATE_HIR_ID {
++                if let hir::Node::Expr(e) = cx.tcx.hir().get(parent_expr) {
++                    match e.kind {
++                        hir::ExprKind::Binary(..)
++                        | hir::ExprKind::Unary(hir::UnOp::UnNot, _)
++                        | hir::ExprKind::Unary(hir::UnOp::UnNeg, _)
++                        | hir::ExprKind::AssignOp(..) => return,
++                        _ => {},
++                    }
++                }
++                parent_expr = cx.tcx.hir().get_parent_node(parent_expr);
++            }
++            // as a parent node
++            let mut visitor = BinaryExprVisitor { in_binary_expr: false };
++            walk_expr(&mut visitor, expr);
++
++            if visitor.in_binary_expr {
++                return;
++            }
++
++            if let Some(impl_trait) = check_binop(
++                cx,
++                expr,
++                binop.node,
++                &["Add", "Sub", "Mul", "Div"],
++                &[
++                    hir::BinOpKind::Add,
++                    hir::BinOpKind::Sub,
++                    hir::BinOpKind::Mul,
++                    hir::BinOpKind::Div,
++                ],
++            ) {
++                span_lint(
++                    cx,
++                    SUSPICIOUS_ARITHMETIC_IMPL,
++                    binop.span,
++                    &format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait),
++                );
++            }
++
++            if let Some(impl_trait) = check_binop(
++                cx,
++                expr,
++                binop.node,
++                &[
++                    "AddAssign",
++                    "SubAssign",
++                    "MulAssign",
++                    "DivAssign",
++                    "BitAndAssign",
++                    "BitOrAssign",
++                    "BitXorAssign",
++                    "RemAssign",
++                    "ShlAssign",
++                    "ShrAssign",
++                ],
++                &[
++                    hir::BinOpKind::Add,
++                    hir::BinOpKind::Sub,
++                    hir::BinOpKind::Mul,
++                    hir::BinOpKind::Div,
++                    hir::BinOpKind::BitAnd,
++                    hir::BinOpKind::BitOr,
++                    hir::BinOpKind::BitXor,
++                    hir::BinOpKind::Rem,
++                    hir::BinOpKind::Shl,
++                    hir::BinOpKind::Shr,
++                ],
++            ) {
++                span_lint(
++                    cx,
++                    SUSPICIOUS_OP_ASSIGN_IMPL,
++                    binop.span,
++                    &format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait),
++                );
++            }
++        }
++    }
++}
++
++fn check_binop(
++    cx: &LateContext<'_, '_>,
++    expr: &hir::Expr<'_>,
++    binop: hir::BinOpKind,
++    traits: &[&'static str],
++    expected_ops: &[hir::BinOpKind],
++) -> Option<&'static str> {
++    let mut trait_ids = vec![];
++    let [krate, module] = crate::utils::paths::OPS_MODULE;
++
++    for &t in traits {
++        let path = [krate, module, t];
++        if let Some(trait_id) = get_trait_def_id(cx, &path) {
++            trait_ids.push(trait_id);
++        } else {
++            return None;
++        }
++    }
++
++    // Get the actually implemented trait
++    let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id);
++
++    if_chain! {
++        if let Some(trait_ref) = trait_ref_of_method(cx, parent_fn);
++        if let Some(idx) = trait_ids.iter().position(|&tid| tid == trait_ref.path.res.def_id());
++        if binop != expected_ops[idx];
++        then{
++            return Some(traits[idx])
++        }
++    }
++
++    None
++}
++
++struct BinaryExprVisitor {
++    in_binary_expr: bool,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for BinaryExprVisitor {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
++        match expr.kind {
++            hir::ExprKind::Binary(..)
++            | hir::ExprKind::Unary(hir::UnOp::UnNot, _)
++            | hir::ExprKind::Unary(hir::UnOp::UnNeg, _)
++            | hir::ExprKind::AssignOp(..) => self.in_binary_expr = true,
++            _ => {},
++        }
++
++        walk_expr(self, expr);
++    }
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c52e6a643f2a28e0348c764a4836d545db10a082
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,263 @@@
++use crate::utils::sugg::Sugg;
++use crate::utils::{
++    differing_macro_contexts, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then, walk_ptrs_ty,
++    SpanlessEq,
++};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, StmtKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for manual swapping.
++    ///
++    /// **Why is this bad?** The `std::mem::swap` function exposes the intent better
++    /// without deinitializing or copying either variable.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let mut a = 42;
++    /// let mut b = 1337;
++    ///
++    /// let t = b;
++    /// b = a;
++    /// a = t;
++    /// ```
++    /// Use std::mem::swap():
++    /// ```rust
++    /// let mut a = 1;
++    /// let mut b = 2;
++    /// std::mem::swap(&mut a, &mut b);
++    /// ```
++    pub MANUAL_SWAP,
++    complexity,
++    "manual swap of two variables"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `foo = bar; bar = foo` sequences.
++    ///
++    /// **Why is this bad?** This looks like a failed attempt to swap.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let mut a = 1;
++    /// # let mut b = 2;
++    /// a = b;
++    /// b = a;
++    /// ```
++    /// If swapping is intended, use `swap()` instead:
++    /// ```rust
++    /// # let mut a = 1;
++    /// # let mut b = 2;
++    /// std::mem::swap(&mut a, &mut b);
++    /// ```
++    pub ALMOST_SWAPPED,
++    correctness,
++    "`foo = bar; bar = foo` sequence"
++}
++
++declare_lint_pass!(Swap => [MANUAL_SWAP, ALMOST_SWAPPED]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Swap {
++    fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>) {
++        check_manual_swap(cx, block);
++        check_suspicious_swap(cx, block);
++    }
++}
++
++/// Implementation of the `MANUAL_SWAP` lint.
++fn check_manual_swap(cx: &LateContext<'_, '_>, block: &Block<'_>) {
++    for w in block.stmts.windows(3) {
++        if_chain! {
++            // let t = foo();
++            if let StmtKind::Local(ref tmp) = w[0].kind;
++            if let Some(ref tmp_init) = tmp.init;
++            if let PatKind::Binding(.., ident, None) = tmp.pat.kind;
++
++            // foo() = bar();
++            if let StmtKind::Semi(ref first) = w[1].kind;
++            if let ExprKind::Assign(ref lhs1, ref rhs1, _) = first.kind;
++
++            // bar() = t;
++            if let StmtKind::Semi(ref second) = w[2].kind;
++            if let ExprKind::Assign(ref lhs2, ref rhs2, _) = second.kind;
++            if let ExprKind::Path(QPath::Resolved(None, ref rhs2)) = rhs2.kind;
++            if rhs2.segments.len() == 1;
++
++            if ident.as_str() == rhs2.segments[0].ident.as_str();
++            if SpanlessEq::new(cx).ignore_fn().eq_expr(tmp_init, lhs1);
++            if SpanlessEq::new(cx).ignore_fn().eq_expr(rhs1, lhs2);
++            then {
++                if let ExprKind::Field(ref lhs1, _) = lhs1.kind {
++                    if let ExprKind::Field(ref lhs2, _) = lhs2.kind {
++                        if lhs1.hir_id.owner == lhs2.hir_id.owner {
++                            return;
++                        }
++                    }
++                }
++
++                let mut applicability = Applicability::MachineApplicable;
++
++                let slice = check_for_slice(cx, lhs1, lhs2);
++                let (replace, what, sugg) = if let Slice::NotSwappable = slice {
++                    return;
++                } else if let Slice::Swappable(slice, idx1, idx2) = slice {
++                    if let Some(slice) = Sugg::hir_opt(cx, slice) {
++                        (
++                            false,
++                            format!(" elements of `{}`", slice),
++                            format!(
++                                "{}.swap({}, {})",
++                                slice.maybe_par(),
++                                snippet_with_applicability(cx, idx1.span, "..", &mut applicability),
++                                snippet_with_applicability(cx, idx2.span, "..", &mut applicability),
++                            ),
++                        )
++                    } else {
++                        (false, String::new(), String::new())
++                    }
++                } else if let (Some(first), Some(second)) = (Sugg::hir_opt(cx, lhs1), Sugg::hir_opt(cx, rhs1)) {
++                    (
++                        true,
++                        format!(" `{}` and `{}`", first, second),
++                        format!("std::mem::swap({}, {})", first.mut_addr(), second.mut_addr()),
++                    )
++                } else {
++                    (true, String::new(), String::new())
++                };
++
++                let span = w[0].span.to(second.span);
++
++                span_lint_and_then(
++                    cx,
++                    MANUAL_SWAP,
++                    span,
++                    &format!("this looks like you are swapping{} manually", what),
++                    |diag| {
++                        if !sugg.is_empty() {
++                            diag.span_suggestion(
++                                span,
++                                "try",
++                                sugg,
++                                applicability,
++                            );
++
++                            if replace {
++                                diag.note("or maybe you should use `std::mem::replace`?");
++                            }
++                        }
++                    }
++                );
++            }
++        }
++    }
++}
++
++enum Slice<'a> {
++    /// `slice.swap(idx1, idx2)` can be used
++    ///
++    /// ## Example
++    ///
++    /// ```rust
++    /// # let mut a = vec![0, 1];
++    /// let t = a[1];
++    /// a[1] = a[0];
++    /// a[0] = t;
++    /// // can be written as
++    /// a.swap(0, 1);
++    /// ```
++    Swappable(&'a Expr<'a>, &'a Expr<'a>, &'a Expr<'a>),
++    /// The `swap` function cannot be used.
++    ///
++    /// ## Example
++    ///
++    /// ```rust
++    /// # let mut a = [vec![1, 2], vec![3, 4]];
++    /// let t = a[0][1];
++    /// a[0][1] = a[1][0];
++    /// a[1][0] = t;
++    /// ```
++    NotSwappable,
++    /// Not a slice
++    None,
++}
++
++/// Checks if both expressions are index operations into "slice-like" types.
++fn check_for_slice<'a>(cx: &LateContext<'_, '_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr<'_>) -> Slice<'a> {
++    if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.kind {
++        if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.kind {
++            if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, lhs2) {
++                let ty = walk_ptrs_ty(cx.tables.expr_ty(lhs1));
++
++                if matches!(ty.kind, ty::Slice(_))
++                    || matches!(ty.kind, ty::Array(_, _))
++                    || is_type_diagnostic_item(cx, ty, sym!(vec_type))
++                    || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type))
++                {
++                    return Slice::Swappable(lhs1, idx1, idx2);
++                }
++            } else {
++                return Slice::NotSwappable;
++            }
++        }
++    }
++
++    Slice::None
++}
++
++/// Implementation of the `ALMOST_SWAPPED` lint.
++fn check_suspicious_swap(cx: &LateContext<'_, '_>, block: &Block<'_>) {
++    for w in block.stmts.windows(2) {
++        if_chain! {
++            if let StmtKind::Semi(ref first) = w[0].kind;
++            if let StmtKind::Semi(ref second) = w[1].kind;
++            if !differing_macro_contexts(first.span, second.span);
++            if let ExprKind::Assign(ref lhs0, ref rhs0, _) = first.kind;
++            if let ExprKind::Assign(ref lhs1, ref rhs1, _) = second.kind;
++            if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1);
++            if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0);
++            then {
++                let lhs0 = Sugg::hir_opt(cx, lhs0);
++                let rhs0 = Sugg::hir_opt(cx, rhs0);
++                let (what, lhs, rhs) = if let (Some(first), Some(second)) = (lhs0, rhs0) {
++                    (
++                        format!(" `{}` and `{}`", first, second),
++                        first.mut_addr().to_string(),
++                        second.mut_addr().to_string(),
++                    )
++                } else {
++                    (String::new(), String::new(), String::new())
++                };
++
++                let span = first.span.to(second.span);
++
++                span_lint_and_then(cx,
++                                   ALMOST_SWAPPED,
++                                   span,
++                                   &format!("this looks like you are trying to swap{}", what),
++                                   |diag| {
++                                       if !what.is_empty() {
++                                           diag.span_suggestion(
++                                               span,
++                                               "try",
++                                               format!(
++                                                   "std::mem::swap({}, {})",
++                                                   lhs,
++                                                   rhs,
++                                               ),
++                                               Applicability::MaybeIncorrect,
++                                           );
++                                           diag.note("or maybe you should use `std::mem::replace`?");
++                                       }
++                                   });
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7b673e15b764a101c0c267229575a952d472433a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,219 @@@
++use crate::utils::span_lint_and_sugg;
++use rustc_ast::ast;
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::{BytePos, Span};
++use std::convert::TryFrom;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks doc comments for usage of tab characters.
++    ///
++    /// **Why is this bad?** The rust style-guide promotes spaces instead of tabs for indentation.
++    /// To keep a consistent view on the source, also doc comments should not have tabs.
++    /// Also, explaining ascii-diagrams containing tabs can get displayed incorrectly when the
++    /// display settings of the author and reader differ.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// ///
++    /// /// Struct to hold two strings:
++    /// ///   - first         one
++    /// ///   - second        one
++    /// pub struct DoubleString {
++    ///    ///
++    ///    ///        - First String:
++    ///    ///                - needs to be inside here
++    ///    first_string: String,
++    ///    ///
++    ///    ///        - Second String:
++    ///    ///                - needs to be inside here
++    ///    second_string: String,
++    ///}
++    /// ```
++    ///
++    /// Will be converted to:
++    /// ```rust
++    /// ///
++    /// /// Struct to hold two strings:
++    /// ///     - first        one
++    /// ///     - second    one
++    /// pub struct DoubleString {
++    ///    ///
++    ///    ///     - First String:
++    ///    ///         - needs to be inside here
++    ///    first_string: String,
++    ///    ///
++    ///    ///     - Second String:
++    ///    ///         - needs to be inside here
++    ///    second_string: String,
++    ///}
++    /// ```
++    pub TABS_IN_DOC_COMMENTS,
++    style,
++    "using tabs in doc comments is not recommended"
++}
++
++declare_lint_pass!(TabsInDocComments => [TABS_IN_DOC_COMMENTS]);
++
++impl TabsInDocComments {
++    fn warn_if_tabs_in_doc(cx: &EarlyContext<'_>, attr: &ast::Attribute) {
++        if let ast::AttrKind::DocComment(comment) = attr.kind {
++            let comment = comment.as_str();
++
++            for (lo, hi) in get_chunks_of_tabs(&comment) {
++                let new_span = Span::new(
++                    attr.span.lo() + BytePos(lo),
++                    attr.span.lo() + BytePos(hi),
++                    attr.span.ctxt(),
++                );
++                span_lint_and_sugg(
++                    cx,
++                    TABS_IN_DOC_COMMENTS,
++                    new_span,
++                    "using tabs in doc comments is not recommended",
++                    "consider using four spaces per tab",
++                    "    ".repeat((hi - lo) as usize),
++                    Applicability::MaybeIncorrect,
++                );
++            }
++        }
++    }
++}
++
++impl EarlyLintPass for TabsInDocComments {
++    fn check_attribute(&mut self, cx: &EarlyContext<'_>, attribute: &ast::Attribute) {
++        Self::warn_if_tabs_in_doc(cx, &attribute);
++    }
++}
++
++///
++/// scans the string for groups of tabs and returns the start(inclusive) and end positions
++/// (exclusive) of all groups
++/// e.g. "sd\tasd\t\taa" will be converted to [(2, 3), (6, 8)] as
++///       012 3456 7 89
++///         ^-^  ^---^
++fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> {
++    let line_length_way_to_long = "doc comment longer than 2^32 chars";
++    let mut spans: Vec<(u32, u32)> = vec![];
++    let mut current_start: u32 = 0;
++
++    // tracker to decide if the last group of tabs is not closed by a non-tab character
++    let mut is_active = false;
++
++    let chars_array: Vec<_> = the_str.chars().collect();
++
++    if chars_array == vec!['\t'] {
++        return vec![(0, 1)];
++    }
++
++    for (index, arr) in chars_array.windows(2).enumerate() {
++        let index = u32::try_from(index).expect(line_length_way_to_long);
++        match arr {
++            ['\t', '\t'] => {
++                // either string starts with double tab, then we have to set it active,
++                // otherwise is_active is true anyway
++                is_active = true;
++            },
++            [_, '\t'] => {
++                // as ['\t', '\t'] is excluded, this has to be a start of a tab group,
++                // set indices accordingly
++                is_active = true;
++                current_start = index + 1;
++            },
++            ['\t', _] => {
++                // this now has to be an end of the group, hence we have to push a new tuple
++                is_active = false;
++                spans.push((current_start, index + 1));
++            },
++            _ => {},
++        }
++    }
++
++    // only possible when tabs are at the end, insert last group
++    if is_active {
++        spans.push((
++            current_start,
++            u32::try_from(the_str.chars().count()).expect(line_length_way_to_long),
++        ));
++    }
++
++    spans
++}
++
++#[cfg(test)]
++mod tests_for_get_chunks_of_tabs {
++    use super::get_chunks_of_tabs;
++
++    #[test]
++    fn test_empty_string() {
++        let res = get_chunks_of_tabs("");
++
++        assert_eq!(res, vec![]);
++    }
++
++    #[test]
++    fn test_simple() {
++        let res = get_chunks_of_tabs("sd\t\t\taa");
++
++        assert_eq!(res, vec![(2, 5)]);
++    }
++
++    #[test]
++    fn test_only_t() {
++        let res = get_chunks_of_tabs("\t\t");
++
++        assert_eq!(res, vec![(0, 2)]);
++    }
++
++    #[test]
++    fn test_only_one_t() {
++        let res = get_chunks_of_tabs("\t");
++
++        assert_eq!(res, vec![(0, 1)]);
++    }
++
++    #[test]
++    fn test_double() {
++        let res = get_chunks_of_tabs("sd\tasd\t\taa");
++
++        assert_eq!(res, vec![(2, 3), (6, 8)]);
++    }
++
++    #[test]
++    fn test_start() {
++        let res = get_chunks_of_tabs("\t\taa");
++
++        assert_eq!(res, vec![(0, 2)]);
++    }
++
++    #[test]
++    fn test_end() {
++        let res = get_chunks_of_tabs("aa\t\t");
++
++        assert_eq!(res, vec![(2, 4)]);
++    }
++
++    #[test]
++    fn test_start_single() {
++        let res = get_chunks_of_tabs("\taa");
++
++        assert_eq!(res, vec![(0, 1)]);
++    }
++
++    #[test]
++    fn test_end_single() {
++        let res = get_chunks_of_tabs("aa\t");
++
++        assert_eq!(res, vec![(2, 3)]);
++    }
++
++    #[test]
++    fn test_no_tabs() {
++        let res = get_chunks_of_tabs("dsfs");
++
++        assert_eq!(res, vec![]);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bbb883aaf328719555988ccfa567131f07399061
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++use crate::utils::{is_adjusted, span_lint};
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for construction of a structure or tuple just to
++    /// assign a value in it.
++    ///
++    /// **Why is this bad?** Readability. If the structure is only created to be
++    /// updated, why not write the structure you want in the first place?
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// (0, 0).0 = 1
++    /// ```
++    pub TEMPORARY_ASSIGNMENT,
++    complexity,
++    "assignments to temporaries"
++}
++
++fn is_temporary(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++    match &expr.kind {
++        ExprKind::Struct(..) | ExprKind::Tup(..) => true,
++        ExprKind::Path(qpath) => {
++            if let Res::Def(DefKind::Const, ..) = cx.tables.qpath_res(qpath, expr.hir_id) {
++                true
++            } else {
++                false
++            }
++        },
++        _ => false,
++    }
++}
++
++declare_lint_pass!(TemporaryAssignment => [TEMPORARY_ASSIGNMENT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TemporaryAssignment {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if let ExprKind::Assign(target, ..) = &expr.kind {
++            let mut base = target;
++            while let ExprKind::Field(f, _) | ExprKind::Index(f, _) = &base.kind {
++                base = f;
++            }
++            if is_temporary(cx, base) && !is_adjusted(cx, base) {
++                span_lint(cx, TEMPORARY_ASSIGNMENT, expr.span, "assignment to temporary");
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c6302ca03d918ddee1fbc9ad93a94ee6fc8d79ac
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,94 @@@
++use crate::utils::{match_def_path, snippet_with_applicability, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `.to_digit(..).is_some()` on `char`s.
++    ///
++    /// **Why is this bad?** This is a convoluted way of checking if a `char` is a digit. It's
++    /// more straight forward to use the dedicated `is_digit` method.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let c = 'c';
++    /// # let radix = 10;
++    /// let is_digit = c.to_digit(radix).is_some();
++    /// ```
++    /// can be written as:
++    /// ```
++    /// # let c = 'c';
++    /// # let radix = 10;
++    /// let is_digit = c.is_digit(radix);
++    /// ```
++    pub TO_DIGIT_IS_SOME,
++    style,
++    "`char.is_digit()` is clearer"
++}
++
++declare_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ToDigitIsSome {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if_chain! {
++            if let hir::ExprKind::MethodCall(is_some_path, _, is_some_args) = &expr.kind;
++            if is_some_path.ident.name.as_str() == "is_some";
++            if let [to_digit_expr] = &**is_some_args;
++            then {
++                let match_result = match &to_digit_expr.kind {
++                    hir::ExprKind::MethodCall(to_digits_path, _, to_digit_args) => {
++                        if_chain! {
++                            if let [char_arg, radix_arg] = &**to_digit_args;
++                            if to_digits_path.ident.name.as_str() == "to_digit";
++                            let char_arg_ty = cx.tables.expr_ty_adjusted(char_arg);
++                            if char_arg_ty.kind == ty::Char;
++                            then {
++                                Some((true, char_arg, radix_arg))
++                            } else {
++                                None
++                            }
++                        }
++                    }
++                    hir::ExprKind::Call(to_digits_call, to_digit_args) => {
++                        if_chain! {
++                            if let [char_arg, radix_arg] = &**to_digit_args;
++                            if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind;
++                            if let to_digits_call_res = cx.tables.qpath_res(to_digits_path, to_digits_call.hir_id);
++                            if let Some(to_digits_def_id) = to_digits_call_res.opt_def_id();
++                            if match_def_path(cx, to_digits_def_id, &["core", "char", "methods", "<impl char>", "to_digit"]);
++                            then {
++                                Some((false, char_arg, radix_arg))
++                            } else {
++                                None
++                            }
++                        }
++                    }
++                    _ => None
++                };
++
++                if let Some((is_method_call, char_arg, radix_arg)) = match_result {
++                    let mut applicability = Applicability::MachineApplicable;
++                    let char_arg_snip = snippet_with_applicability(cx, char_arg.span, "_", &mut applicability);
++                    let radix_snip = snippet_with_applicability(cx, radix_arg.span, "_", &mut applicability);
++
++                    span_lint_and_sugg(
++                        cx,
++                        TO_DIGIT_IS_SOME,
++                        expr.span,
++                        "use of `.to_digit(..).is_some()`",
++                        "try this",
++                        if is_method_call {
++                            format!("{}.is_digit({})", char_arg_snip, radix_snip)
++                        } else {
++                            format!("char::is_digit({}, {})", char_arg_snip, radix_snip)
++                        },
++                        applicability,
++                    );
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..67121729663c6aa4ec62fa46af4c673c42318171
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,86 @@@
++use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_help, SpanlessHash};
++use rustc_data_structures::fx::FxHashMap;
++use rustc_errors::Applicability;
++use rustc_hir::{GenericBound, Generics, WherePredicate};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++
++#[derive(Copy, Clone)]
++pub struct TraitBounds;
++
++declare_clippy_lint! {
++    /// **What it does:** This lint warns about unnecessary type repetitions in trait bounds
++    ///
++    /// **Why is this bad?** Repeating the type for every bound makes the code
++    /// less readable than combining the bounds
++    ///
++    /// **Example:**
++    /// ```rust
++    /// pub fn foo<T>(t: T) where T: Copy, T: Clone {}
++    /// ```
++    ///
++    /// Could be written as:
++    ///
++    /// ```rust
++    /// pub fn foo<T>(t: T) where T: Copy + Clone {}
++    /// ```
++    pub TYPE_REPETITION_IN_BOUNDS,
++    pedantic,
++    "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
++}
++
++impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TraitBounds {
++    fn check_generics(&mut self, cx: &LateContext<'a, 'tcx>, gen: &'tcx Generics<'_>) {
++        if in_macro(gen.span) {
++            return;
++        }
++        let hash = |ty| -> u64 {
++            let mut hasher = SpanlessHash::new(cx, cx.tables);
++            hasher.hash_ty(ty);
++            hasher.finish()
++        };
++        let mut map = FxHashMap::default();
++        let mut applicability = Applicability::MaybeIncorrect;
++        for bound in gen.where_clause.predicates {
++            if let WherePredicate::BoundPredicate(ref p) = bound {
++                let h = hash(&p.bounded_ty);
++                if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>()) {
++                    let mut hint_string = format!(
++                        "consider combining the bounds: `{}:",
++                        snippet(cx, p.bounded_ty.span, "_")
++                    );
++                    for b in v.iter() {
++                        if let GenericBound::Trait(ref poly_trait_ref, _) = b {
++                            let path = &poly_trait_ref.trait_ref.path;
++                            hint_string.push_str(&format!(
++                                " {} +",
++                                snippet_with_applicability(cx, path.span, "..", &mut applicability)
++                            ));
++                        }
++                    }
++                    for b in p.bounds.iter() {
++                        if let GenericBound::Trait(ref poly_trait_ref, _) = b {
++                            let path = &poly_trait_ref.trait_ref.path;
++                            hint_string.push_str(&format!(
++                                " {} +",
++                                snippet_with_applicability(cx, path.span, "..", &mut applicability)
++                            ));
++                        }
++                    }
++                    hint_string.truncate(hint_string.len() - 2);
++                    hint_string.push('`');
++                    span_lint_and_help(
++                        cx,
++                        TYPE_REPETITION_IN_BOUNDS,
++                        p.span,
++                        "this type has already been used as a bound predicate",
++                        None,
++                        &hint_string,
++                    );
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e24d2c4f495dba2aeb01e8ef09a590f0f9000348
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,653 @@@
++use crate::utils::{
++    is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg,
++    span_lint_and_then, sugg,
++};
++use if_chain::if_chain;
++use rustc_ast::ast;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, TyKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use std::borrow::Cow;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for transmutes that can't ever be correct on any
++    /// architecture.
++    ///
++    /// **Why is this bad?** It's basically guaranteed to be undefined behaviour.
++    ///
++    /// **Known problems:** When accessing C, users might want to store pointer
++    /// sized objects in `extradata` arguments to save an allocation.
++    ///
++    /// **Example:**
++    /// ```ignore
++    /// let ptr: *const T = core::intrinsics::transmute('x')
++    /// ```
++    pub WRONG_TRANSMUTE,
++    correctness,
++    "transmutes that are confusing at best, undefined behaviour at worst and always useless"
++}
++
++// FIXME: Move this to `complexity` again, after #5343 is fixed
++declare_clippy_lint! {
++    /// **What it does:** Checks for transmutes to the original type of the object
++    /// and transmutes that could be a cast.
++    ///
++    /// **Why is this bad?** Readability. The code tricks people into thinking that
++    /// something complex is going on.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
++    /// ```
++    pub USELESS_TRANSMUTE,
++    nursery,
++    "transmutes that have the same to and from types or could be a cast/coercion"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for transmutes between a type `T` and `*T`.
++    ///
++    /// **Why is this bad?** It's easy to mistakenly transmute between a type and a
++    /// pointer to that type.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// core::intrinsics::transmute(t) // where the result type is the same as
++    ///                                // `*t` or `&t`'s
++    /// ```
++    pub CROSSPOINTER_TRANSMUTE,
++    complexity,
++    "transmutes that have to or from types that are a pointer to the other"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for transmutes from a pointer to a reference.
++    ///
++    /// **Why is this bad?** This can always be rewritten with `&` and `*`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// unsafe {
++    ///     let _: &T = std::mem::transmute(p); // where p: *const T
++    /// }
++    ///
++    /// // can be written:
++    /// let _: &T = &*p;
++    /// ```
++    pub TRANSMUTE_PTR_TO_REF,
++    complexity,
++    "transmutes from a pointer to a reference type"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for transmutes from an integer to a `char`.
++    ///
++    /// **Why is this bad?** Not every integer is a Unicode scalar value.
++    ///
++    /// **Known problems:**
++    /// - [`from_u32`] which this lint suggests using is slower than `transmute`
++    /// as it needs to validate the input.
++    /// If you are certain that the input is always a valid Unicode scalar value,
++    /// use [`from_u32_unchecked`] which is as fast as `transmute`
++    /// but has a semantically meaningful name.
++    /// - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.
++    ///
++    /// [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html
++    /// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x = 1_u32;
++    /// unsafe {
++    ///     let _: char = std::mem::transmute(x); // where x: u32
++    /// }
++    ///
++    /// // should be:
++    /// let _ = std::char::from_u32(x).unwrap();
++    /// ```
++    pub TRANSMUTE_INT_TO_CHAR,
++    complexity,
++    "transmutes from an integer to a `char`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for transmutes from a `&[u8]` to a `&str`.
++    ///
++    /// **Why is this bad?** Not every byte slice is a valid UTF-8 string.
++    ///
++    /// **Known problems:**
++    /// - [`from_utf8`] which this lint suggests using is slower than `transmute`
++    /// as it needs to validate the input.
++    /// If you are certain that the input is always a valid UTF-8,
++    /// use [`from_utf8_unchecked`] which is as fast as `transmute`
++    /// but has a semantically meaningful name.
++    /// - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.
++    ///
++    /// [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html
++    /// [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let b: &[u8] = &[1_u8, 2_u8];
++    /// unsafe {
++    ///     let _: &str = std::mem::transmute(b); // where b: &[u8]
++    /// }
++    ///
++    /// // should be:
++    /// let _ = std::str::from_utf8(b).unwrap();
++    /// ```
++    pub TRANSMUTE_BYTES_TO_STR,
++    complexity,
++    "transmutes from a `&[u8]` to a `&str`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for transmutes from an integer to a `bool`.
++    ///
++    /// **Why is this bad?** This might result in an invalid in-memory representation of a `bool`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x = 1_u8;
++    /// unsafe {
++    ///     let _: bool = std::mem::transmute(x); // where x: u8
++    /// }
++    ///
++    /// // should be:
++    /// let _: bool = x != 0;
++    /// ```
++    pub TRANSMUTE_INT_TO_BOOL,
++    complexity,
++    "transmutes from an integer to a `bool`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for transmutes from an integer to a float.
++    ///
++    /// **Why is this bad?** Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive
++    /// and safe.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// unsafe {
++    ///     let _: f32 = std::mem::transmute(1_u32); // where x: u32
++    /// }
++    ///
++    /// // should be:
++    /// let _: f32 = f32::from_bits(1_u32);
++    /// ```
++    pub TRANSMUTE_INT_TO_FLOAT,
++    complexity,
++    "transmutes from an integer to a float"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for transmutes from a float to an integer.
++    ///
++    /// **Why is this bad?** Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive
++    /// and safe.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// unsafe {
++    ///     let _: u32 = std::mem::transmute(1f32);
++    /// }
++    ///
++    /// // should be:
++    /// let _: u32 = 1f32.to_bits();
++    /// ```
++    pub TRANSMUTE_FLOAT_TO_INT,
++    complexity,
++    "transmutes from a float to an integer"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for transmutes from a pointer to a pointer, or
++    /// from a reference to a reference.
++    ///
++    /// **Why is this bad?** Transmutes are dangerous, and these can instead be
++    /// written as casts.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let ptr = &1u32 as *const u32;
++    /// unsafe {
++    ///     // pointer-to-pointer transmute
++    ///     let _: *const f32 = std::mem::transmute(ptr);
++    ///     // ref-ref transmute
++    ///     let _: &f32 = std::mem::transmute(&1u32);
++    /// }
++    /// // These can be respectively written:
++    /// let _ = ptr as *const f32;
++    /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
++    /// ```
++    pub TRANSMUTE_PTR_TO_PTR,
++    complexity,
++    "transmutes from a pointer to a pointer / a reference to a reference"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for transmutes between collections whose
++    /// types have different ABI, size or alignment.
++    ///
++    /// **Why is this bad?** This is undefined behavior.
++    ///
++    /// **Known problems:** Currently, we cannot know whether a type is a
++    /// collection, so we just lint the ones that come with `std`.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// // different size, therefore likely out-of-bounds memory access
++    /// // You absolutely do not want this in your code!
++    /// unsafe {
++    ///     std::mem::transmute::<_, Vec<u32>>(vec![2_u16])
++    /// };
++    /// ```
++    ///
++    /// You must always iterate, map and collect the values:
++    ///
++    /// ```rust
++    /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
++    /// ```
++    pub UNSOUND_COLLECTION_TRANSMUTE,
++    correctness,
++    "transmute between collections of layout-incompatible types"
++}
++declare_lint_pass!(Transmute => [
++    CROSSPOINTER_TRANSMUTE,
++    TRANSMUTE_PTR_TO_REF,
++    TRANSMUTE_PTR_TO_PTR,
++    USELESS_TRANSMUTE,
++    WRONG_TRANSMUTE,
++    TRANSMUTE_INT_TO_CHAR,
++    TRANSMUTE_BYTES_TO_STR,
++    TRANSMUTE_INT_TO_BOOL,
++    TRANSMUTE_INT_TO_FLOAT,
++    TRANSMUTE_FLOAT_TO_INT,
++    UNSOUND_COLLECTION_TRANSMUTE,
++]);
++
++// used to check for UNSOUND_COLLECTION_TRANSMUTE
++static COLLECTIONS: &[&[&str]] = &[
++    &paths::VEC,
++    &paths::VEC_DEQUE,
++    &paths::BINARY_HEAP,
++    &paths::BTREESET,
++    &paths::BTREEMAP,
++    &paths::HASHSET,
++    &paths::HASHMAP,
++];
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
++    #[allow(clippy::similar_names, clippy::too_many_lines)]
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
++        if_chain! {
++            if let ExprKind::Call(ref path_expr, ref args) = e.kind;
++            if let ExprKind::Path(ref qpath) = path_expr.kind;
++            if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id();
++            if match_def_path(cx, def_id, &paths::TRANSMUTE);
++            then {
++                let from_ty = cx.tables.expr_ty(&args[0]);
++                let to_ty = cx.tables.expr_ty(e);
++
++                match (&from_ty.kind, &to_ty.kind) {
++                    _ if from_ty == to_ty => span_lint(
++                        cx,
++                        USELESS_TRANSMUTE,
++                        e.span,
++                        &format!("transmute from a type (`{}`) to itself", from_ty),
++                    ),
++                    (&ty::Ref(_, rty, rty_mutbl), &ty::RawPtr(ptr_ty)) => span_lint_and_then(
++                        cx,
++                        USELESS_TRANSMUTE,
++                        e.span,
++                        "transmute from a reference to a pointer",
++                        |diag| {
++                            if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
++                                let rty_and_mut = ty::TypeAndMut {
++                                    ty: rty,
++                                    mutbl: rty_mutbl,
++                                };
++
++                                let sugg = if ptr_ty == rty_and_mut {
++                                    arg.as_ty(to_ty)
++                                } else {
++                                    arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty)
++                                };
++
++                                diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified);
++                            }
++                        },
++                    ),
++                    (&ty::Int(_), &ty::RawPtr(_)) | (&ty::Uint(_), &ty::RawPtr(_)) => span_lint_and_then(
++                        cx,
++                        USELESS_TRANSMUTE,
++                        e.span,
++                        "transmute from an integer to a pointer",
++                        |diag| {
++                            if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
++                                diag.span_suggestion(
++                                    e.span,
++                                    "try",
++                                    arg.as_ty(&to_ty.to_string()).to_string(),
++                                    Applicability::Unspecified,
++                                );
++                            }
++                        },
++                    ),
++                    (&ty::Float(_), &ty::Ref(..))
++                    | (&ty::Float(_), &ty::RawPtr(_))
++                    | (&ty::Char, &ty::Ref(..))
++                    | (&ty::Char, &ty::RawPtr(_)) => span_lint(
++                        cx,
++                        WRONG_TRANSMUTE,
++                        e.span,
++                        &format!("transmute from a `{}` to a pointer", from_ty),
++                    ),
++                    (&ty::RawPtr(from_ptr), _) if from_ptr.ty == to_ty => span_lint(
++                        cx,
++                        CROSSPOINTER_TRANSMUTE,
++                        e.span,
++                        &format!(
++                            "transmute from a type (`{}`) to the type that it points to (`{}`)",
++                            from_ty, to_ty
++                        ),
++                    ),
++                    (_, &ty::RawPtr(to_ptr)) if to_ptr.ty == from_ty => span_lint(
++                        cx,
++                        CROSSPOINTER_TRANSMUTE,
++                        e.span,
++                        &format!(
++                            "transmute from a type (`{}`) to a pointer to that type (`{}`)",
++                            from_ty, to_ty
++                        ),
++                    ),
++                    (&ty::RawPtr(from_pty), &ty::Ref(_, to_ref_ty, mutbl)) => span_lint_and_then(
++                        cx,
++                        TRANSMUTE_PTR_TO_REF,
++                        e.span,
++                        &format!(
++                            "transmute from a pointer type (`{}`) to a reference type \
++                             (`{}`)",
++                            from_ty, to_ty
++                        ),
++                        |diag| {
++                            let arg = sugg::Sugg::hir(cx, &args[0], "..");
++                            let (deref, cast) = if mutbl == Mutability::Mut {
++                                ("&mut *", "*mut")
++                            } else {
++                                ("&*", "*const")
++                            };
++
++                            let arg = if from_pty.ty == to_ref_ty {
++                                arg
++                            } else {
++                                arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, to_ref_ty)))
++                            };
++
++                            diag.span_suggestion(
++                                e.span,
++                                "try",
++                                sugg::make_unop(deref, arg).to_string(),
++                                Applicability::Unspecified,
++                            );
++                        },
++                    ),
++                    (&ty::Int(ast::IntTy::I32), &ty::Char) | (&ty::Uint(ast::UintTy::U32), &ty::Char) => {
++                        span_lint_and_then(
++                            cx,
++                            TRANSMUTE_INT_TO_CHAR,
++                            e.span,
++                            &format!("transmute from a `{}` to a `char`", from_ty),
++                            |diag| {
++                                let arg = sugg::Sugg::hir(cx, &args[0], "..");
++                                let arg = if let ty::Int(_) = from_ty.kind {
++                                    arg.as_ty(ast::UintTy::U32.name_str())
++                                } else {
++                                    arg
++                                };
++                                diag.span_suggestion(
++                                    e.span,
++                                    "consider using",
++                                    format!("std::char::from_u32({}).unwrap()", arg.to_string()),
++                                    Applicability::Unspecified,
++                                );
++                            },
++                        )
++                    },
++                    (&ty::Ref(_, ty_from, from_mutbl), &ty::Ref(_, ty_to, to_mutbl)) => {
++                        if_chain! {
++                            if let (&ty::Slice(slice_ty), &ty::Str) = (&ty_from.kind, &ty_to.kind);
++                            if let ty::Uint(ast::UintTy::U8) = slice_ty.kind;
++                            if from_mutbl == to_mutbl;
++                            then {
++                                let postfix = if from_mutbl == Mutability::Mut {
++                                    "_mut"
++                                } else {
++                                    ""
++                                };
++
++                                span_lint_and_sugg(
++                                    cx,
++                                    TRANSMUTE_BYTES_TO_STR,
++                                    e.span,
++                                    &format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
++                                    "consider using",
++                                    format!(
++                                        "std::str::from_utf8{}({}).unwrap()",
++                                        postfix,
++                                        snippet(cx, args[0].span, ".."),
++                                    ),
++                                    Applicability::Unspecified,
++                                );
++                            } else {
++                                if cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty) {
++                                    span_lint_and_then(
++                                        cx,
++                                        TRANSMUTE_PTR_TO_PTR,
++                                        e.span,
++                                        "transmute from a reference to a reference",
++                                        |diag| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
++                                            let ty_from_and_mut = ty::TypeAndMut {
++                                                ty: ty_from,
++                                                mutbl: from_mutbl
++                                            };
++                                            let ty_to_and_mut = ty::TypeAndMut { ty: ty_to, mutbl: to_mutbl };
++                                            let sugg_paren = arg
++                                                .as_ty(cx.tcx.mk_ptr(ty_from_and_mut))
++                                                .as_ty(cx.tcx.mk_ptr(ty_to_and_mut));
++                                            let sugg = if to_mutbl == Mutability::Mut {
++                                                sugg_paren.mut_addr_deref()
++                                            } else {
++                                                sugg_paren.addr_deref()
++                                            };
++                                            diag.span_suggestion(
++                                                e.span,
++                                                "try",
++                                                sugg.to_string(),
++                                                Applicability::Unspecified,
++                                            );
++                                        },
++                                    )
++                                }
++                            }
++                        }
++                    },
++                    (&ty::RawPtr(_), &ty::RawPtr(to_ty)) => span_lint_and_then(
++                        cx,
++                        TRANSMUTE_PTR_TO_PTR,
++                        e.span,
++                        "transmute from a pointer to a pointer",
++                        |diag| {
++                            if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
++                                let sugg = arg.as_ty(cx.tcx.mk_ptr(to_ty));
++                                diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified);
++                            }
++                        },
++                    ),
++                    (&ty::Int(ast::IntTy::I8), &ty::Bool) | (&ty::Uint(ast::UintTy::U8), &ty::Bool) => {
++                        span_lint_and_then(
++                            cx,
++                            TRANSMUTE_INT_TO_BOOL,
++                            e.span,
++                            &format!("transmute from a `{}` to a `bool`", from_ty),
++                            |diag| {
++                                let arg = sugg::Sugg::hir(cx, &args[0], "..");
++                                let zero = sugg::Sugg::NonParen(Cow::from("0"));
++                                diag.span_suggestion(
++                                    e.span,
++                                    "consider using",
++                                    sugg::make_binop(ast::BinOpKind::Ne, &arg, &zero).to_string(),
++                                    Applicability::Unspecified,
++                                );
++                            },
++                        )
++                    },
++                    (&ty::Int(_), &ty::Float(_)) | (&ty::Uint(_), &ty::Float(_)) => span_lint_and_then(
++                        cx,
++                        TRANSMUTE_INT_TO_FLOAT,
++                        e.span,
++                        &format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
++                        |diag| {
++                            let arg = sugg::Sugg::hir(cx, &args[0], "..");
++                            let arg = if let ty::Int(int_ty) = from_ty.kind {
++                                arg.as_ty(format!(
++                                    "u{}",
++                                    int_ty.bit_width().map_or_else(|| "size".to_string(), |v| v.to_string())
++                                ))
++                            } else {
++                                arg
++                            };
++                            diag.span_suggestion(
++                                e.span,
++                                "consider using",
++                                format!("{}::from_bits({})", to_ty, arg.to_string()),
++                                Applicability::Unspecified,
++                            );
++                        },
++                    ),
++                    (&ty::Float(float_ty), &ty::Int(_)) | (&ty::Float(float_ty), &ty::Uint(_)) => span_lint_and_then(
++                        cx,
++                        TRANSMUTE_FLOAT_TO_INT,
++                        e.span,
++                        &format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
++                        |diag| {
++                            let mut expr = &args[0];
++                            let mut arg = sugg::Sugg::hir(cx, expr, "..");
++
++                            if let ExprKind::Unary(UnOp::UnNeg, inner_expr) = &expr.kind {
++                                expr = &inner_expr;
++                            }
++
++                            if_chain! {
++                                // if the expression is a float literal and it is unsuffixed then
++                                // add a suffix so the suggestion is valid and unambiguous
++                                let op = format!("{}{}", arg, float_ty.name_str()).into();
++                                if let ExprKind::Lit(lit) = &expr.kind;
++                                if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node;
++                                then {
++                                    match arg {
++                                        sugg::Sugg::MaybeParen(_) => arg = sugg::Sugg::MaybeParen(op),
++                                        _ => arg = sugg::Sugg::NonParen(op)
++                                    }
++                                }
++                            }
++
++                            arg = sugg::Sugg::NonParen(format!("{}.to_bits()", arg.maybe_par()).into());
++
++                            // cast the result of `to_bits` if `to_ty` is signed
++                            arg = if let ty::Int(int_ty) = to_ty.kind {
++                                arg.as_ty(int_ty.name_str().to_string())
++                            } else {
++                                arg
++                            };
++
++                            diag.span_suggestion(
++                                e.span,
++                                "consider using",
++                                arg.to_string(),
++                                Applicability::Unspecified,
++                            );
++                        },
++                    ),
++                    (&ty::Adt(ref from_adt, ref from_substs), &ty::Adt(ref to_adt, ref to_substs)) => {
++                        if from_adt.did != to_adt.did ||
++                                !COLLECTIONS.iter().any(|path| match_def_path(cx, to_adt.did, path)) {
++                            return;
++                        }
++                        if from_substs.types().zip(to_substs.types())
++                                              .any(|(from_ty, to_ty)| is_layout_incompatible(cx, from_ty, to_ty)) {
++                            span_lint(
++                                cx,
++                                UNSOUND_COLLECTION_TRANSMUTE,
++                                e.span,
++                                &format!(
++                                    "transmute from `{}` to `{}` with mismatched layout is unsound",
++                                    from_ty,
++                                    to_ty
++                                )
++                            );
++                        }
++                    },
++                    _ => return,
++                }
++            }
++        }
++    }
++}
++
++/// Gets the snippet of `Bar` in `…::transmute<Foo, &Bar>`. If that snippet is
++/// not available , use
++/// the type's `ToString` implementation. In weird cases it could lead to types
++/// with invalid `'_`
++/// lifetime, but it should be rare.
++fn get_type_snippet(cx: &LateContext<'_, '_>, path: &QPath<'_>, to_ref_ty: Ty<'_>) -> String {
++    let seg = last_path_segment(path);
++    if_chain! {
++        if let Some(ref params) = seg.args;
++        if !params.parenthesized;
++        if let Some(to_ty) = params.args.iter().filter_map(|arg| match arg {
++            GenericArg::Type(ty) => Some(ty),
++            _ => None,
++        }).nth(1);
++        if let TyKind::Rptr(_, ref to_ty) = to_ty.kind;
++        then {
++            return snippet(cx, to_ty.ty.span, &to_ref_ty.to_string()).to_string();
++        }
++    }
++
++    to_ref_ty.to_string()
++}
++
++// check if the component types of the transmuted collection and the result have different ABI,
++// size or alignment
++fn is_layout_incompatible<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
++    let empty_param_env = ty::ParamEnv::empty();
++    // check if `from` and `to` are normalizable to avoid ICE (#4968)
++    if !(is_normalizable(cx, empty_param_env, from) && is_normalizable(cx, empty_param_env, to)) {
++        return false;
++    }
++    let from_ty_layout = cx.tcx.layout_of(empty_param_env.and(from));
++    let to_ty_layout = cx.tcx.layout_of(empty_param_env.and(to));
++    if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) {
++        from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi
++    } else {
++        // no idea about layout, so don't lint
++        false
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1d0332c580500f73433486c69fe798ee57cd07b5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,89 @@@
++use crate::consts::{constant_context, Constant};
++use crate::utils::{match_qpath, paths, span_lint};
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for transmute calls which would receive a null pointer.
++    ///
++    /// **Why is this bad?** Transmuting a null pointer is undefined behavior.
++    ///
++    /// **Known problems:** Not all cases can be detected at the moment of this writing.
++    /// For example, variables which hold a null pointer and are then fed to a `transmute`
++    /// call, aren't detectable yet.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
++    /// ```
++    pub TRANSMUTING_NULL,
++    correctness,
++    "transmutes from a null pointer to a reference, which is undefined behavior"
++}
++
++declare_lint_pass!(TransmutingNull => [TRANSMUTING_NULL]);
++
++const LINT_MSG: &str = "transmuting a known null pointer into a reference.";
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TransmutingNull {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if in_external_macro(cx.sess(), expr.span) {
++            return;
++        }
++
++        if_chain! {
++            if let ExprKind::Call(ref func, ref args) = expr.kind;
++            if let ExprKind::Path(ref path) = func.kind;
++            if match_qpath(path, &paths::STD_MEM_TRANSMUTE);
++            if args.len() == 1;
++
++            then {
++
++                // Catching transmute over constants that resolve to `null`.
++                let mut const_eval_context = constant_context(cx, cx.tables);
++                if_chain! {
++                    if let ExprKind::Path(ref _qpath) = args[0].kind;
++                    let x = const_eval_context.expr(&args[0]);
++                    if let Some(constant) = x;
++                    if let Constant::RawPtr(0) = constant;
++                    then {
++                        span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
++                    }
++                }
++
++                // Catching:
++                // `std::mem::transmute(0 as *const i32)`
++                if_chain! {
++                    if let ExprKind::Cast(ref inner_expr, ref _cast_ty) = args[0].kind;
++                    if let ExprKind::Lit(ref lit) = inner_expr.kind;
++                    if let LitKind::Int(0, _) = lit.node;
++                    then {
++                        span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
++                    }
++                }
++
++                // Catching:
++                // `std::mem::transmute(std::ptr::null::<i32>())`
++                if_chain! {
++                    if let ExprKind::Call(ref func1, ref args1) = args[0].kind;
++                    if let ExprKind::Path(ref path1) = func1.kind;
++                    if match_qpath(path1, &paths::STD_PTR_NULL);
++                    if args1.is_empty();
++                    then {
++                        span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
++                    }
++                }
++
++                // FIXME:
++                // Also catch transmutations of variables which are known nulls.
++                // To do this, MIR const propagation seems to be the better tool.
++                // Whenever MIR const prop routines are more developed, this will
++                // become available. As of this writing (25/03/19) it is not yet.
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2c101220c5d6897f943b0799d615993283d2194e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,178 @@@
++use std::cmp;
++
++use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::intravisit::FnKind;
++use rustc_hir::{Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::config::Config as SessionConfig;
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::Span;
++use rustc_target::abi::LayoutOf;
++use rustc_target::spec::abi::Abi;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for functions taking arguments by reference, where
++    /// the argument type is `Copy` and small enough to be more efficient to always
++    /// pass by value.
++    ///
++    /// **Why is this bad?** In many calling conventions instances of structs will
++    /// be passed through registers if they fit into two or less general purpose
++    /// registers.
++    ///
++    /// **Known problems:** This lint is target register size dependent, it is
++    /// limited to 32-bit to try and reduce portability problems between 32 and
++    /// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit
++    /// will be different.
++    ///
++    /// The configuration option `trivial_copy_size_limit` can be set to override
++    /// this limit for a project.
++    ///
++    /// This lint attempts to allow passing arguments by reference if a reference
++    /// to that argument is returned. This is implemented by comparing the lifetime
++    /// of the argument and return value for equality. However, this can cause
++    /// false positives in cases involving multiple lifetimes that are bounded by
++    /// each other.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// // Bad
++    /// fn foo(v: &u32) {}
++    /// ```
++    ///
++    /// ```rust
++    /// // Better
++    /// fn foo(v: u32) {}
++    /// ```
++    pub TRIVIALLY_COPY_PASS_BY_REF,
++    pedantic,
++    "functions taking small copyable arguments by reference"
++}
++
++#[derive(Copy, Clone)]
++pub struct TriviallyCopyPassByRef {
++    limit: u64,
++}
++
++impl<'a, 'tcx> TriviallyCopyPassByRef {
++    pub fn new(limit: Option<u64>, target: &SessionConfig) -> Self {
++        let limit = limit.unwrap_or_else(|| {
++            let bit_width = u64::from(target.ptr_width);
++            // Cap the calculated bit width at 32-bits to reduce
++            // portability problems between 32 and 64-bit targets
++            let bit_width = cmp::min(bit_width, 32);
++            #[allow(clippy::integer_division)]
++            let byte_width = bit_width / 8;
++            // Use a limit of 2 times the register byte width
++            byte_width * 2
++        });
++        Self { limit }
++    }
++
++    fn check_poly_fn(&mut self, cx: &LateContext<'_, 'tcx>, hir_id: HirId, decl: &FnDecl<'_>, span: Option<Span>) {
++        let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
++
++        let fn_sig = cx.tcx.fn_sig(fn_def_id);
++        let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);
++
++        // Use lifetimes to determine if we're returning a reference to the
++        // argument. In that case we can't switch to pass-by-value as the
++        // argument will not live long enough.
++        let output_lts = match fn_sig.output().kind {
++            ty::Ref(output_lt, _, _) => vec![output_lt],
++            ty::Adt(_, substs) => substs.regions().collect(),
++            _ => vec![],
++        };
++
++        for (input, &ty) in decl.inputs.iter().zip(fn_sig.inputs()) {
++            // All spans generated from a proc-macro invocation are the same...
++            match span {
++                Some(s) if s == input.span => return,
++                _ => (),
++            }
++
++            if_chain! {
++                if let ty::Ref(input_lt, ty, Mutability::Not) = ty.kind;
++                if !output_lts.contains(&input_lt);
++                if is_copy(cx, ty);
++                if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes());
++                if size <= self.limit;
++                if let hir::TyKind::Rptr(_, MutTy { ty: ref decl_ty, .. }) = input.kind;
++                then {
++                    let value_type = if is_self_ty(decl_ty) {
++                        "self".into()
++                    } else {
++                        snippet(cx, decl_ty.span, "_").into()
++                    };
++                    span_lint_and_sugg(
++                        cx,
++                        TRIVIALLY_COPY_PASS_BY_REF,
++                        input.span,
++                        &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.limit),
++                        "consider passing by value instead",
++                        value_type,
++                        Applicability::Unspecified,
++                    );
++                }
++            }
++        }
++    }
++}
++
++impl_lint_pass!(TriviallyCopyPassByRef => [TRIVIALLY_COPY_PASS_BY_REF]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TriviallyCopyPassByRef {
++    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem<'_>) {
++        if item.span.from_expansion() {
++            return;
++        }
++
++        if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind {
++            self.check_poly_fn(cx, item.hir_id, &*method_sig.decl, None);
++        }
++    }
++
++    fn check_fn(
++        &mut self,
++        cx: &LateContext<'a, 'tcx>,
++        kind: FnKind<'tcx>,
++        decl: &'tcx FnDecl<'_>,
++        _body: &'tcx Body<'_>,
++        span: Span,
++        hir_id: HirId,
++    ) {
++        if span.from_expansion() {
++            return;
++        }
++
++        match kind {
++            FnKind::ItemFn(.., header, _, attrs) => {
++                if header.abi != Abi::Rust {
++                    return;
++                }
++                for a in attrs {
++                    if a.meta_item_list().is_some() && a.check_name(sym!(proc_macro_derive)) {
++                        return;
++                    }
++                }
++            },
++            FnKind::Method(..) => (),
++            _ => return,
++        }
++
++        // Exclude non-inherent impls
++        if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
++            if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } |
++                ItemKind::Trait(..))
++            {
++                return;
++            }
++        }
++
++        self.check_poly_fn(cx, hir_id, decl, Some(span));
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7018fa6804ba71949c7892daf05d3c21ddbe4168
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,122 @@@
++use crate::utils::{match_qpath, paths, snippet, snippet_with_macro_callsite, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty::Ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usages of `Err(x)?`.
++    ///
++    /// **Why is this bad?** The `?` operator is designed to allow calls that
++    /// can fail to be easily chained. For example, `foo()?.bar()` or
++    /// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will
++    /// always return), it is more clear to write `return Err(x)`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// fn foo(fail: bool) -> Result<i32, String> {
++    ///     if fail {
++    ///       Err("failed")?;
++    ///     }
++    ///     Ok(0)
++    /// }
++    /// ```
++    /// Could be written:
++    ///
++    /// ```rust
++    /// fn foo(fail: bool) -> Result<i32, String> {
++    ///     if fail {
++    ///       return Err("failed".into());
++    ///     }
++    ///     Ok(0)
++    /// }
++    /// ```
++    pub TRY_ERR,
++    style,
++    "return errors explicitly rather than hiding them behind a `?`"
++}
++
++declare_lint_pass!(TryErr => [TRY_ERR]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TryErr {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        // Looks for a structure like this:
++        // match ::std::ops::Try::into_result(Err(5)) {
++        //     ::std::result::Result::Err(err) =>
++        //         #[allow(unreachable_code)]
++        //         return ::std::ops::Try::from_error(::std::convert::From::from(err)),
++        //     ::std::result::Result::Ok(val) =>
++        //         #[allow(unreachable_code)]
++        //         val,
++        // };
++        if_chain! {
++            if !in_external_macro(cx.tcx.sess, expr.span);
++            if let ExprKind::Match(ref match_arg, _, MatchSource::TryDesugar) = expr.kind;
++            if let ExprKind::Call(ref match_fun, ref try_args) = match_arg.kind;
++            if let ExprKind::Path(ref match_fun_path) = match_fun.kind;
++            if match_qpath(match_fun_path, &paths::TRY_INTO_RESULT);
++            if let Some(ref try_arg) = try_args.get(0);
++            if let ExprKind::Call(ref err_fun, ref err_args) = try_arg.kind;
++            if let Some(ref err_arg) = err_args.get(0);
++            if let ExprKind::Path(ref err_fun_path) = err_fun.kind;
++            if match_qpath(err_fun_path, &paths::RESULT_ERR);
++            if let Some(return_type) = find_err_return_type(cx, &expr.kind);
++
++            then {
++                let err_type = cx.tables.expr_ty(err_arg);
++                let origin_snippet = if err_arg.span.from_expansion() {
++                    snippet_with_macro_callsite(cx, err_arg.span, "_")
++                } else {
++                    snippet(cx, err_arg.span, "_")
++                };
++                let suggestion = if err_type == return_type {
++                    format!("return Err({})", origin_snippet)
++                } else {
++                    format!("return Err({}.into())", origin_snippet)
++                };
++
++                span_lint_and_sugg(
++                    cx,
++                    TRY_ERR,
++                    expr.span,
++                    "returning an `Err(_)` with the `?` operator",
++                    "try this",
++                    suggestion,
++                    Applicability::MachineApplicable
++                );
++            }
++        }
++    }
++}
++
++// In order to determine whether to suggest `.into()` or not, we need to find the error type the
++// function returns. To do that, we look for the From::from call (see tree above), and capture
++// its output type.
++fn find_err_return_type<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx ExprKind<'_>) -> Option<Ty<'tcx>> {
++    if let ExprKind::Match(_, ref arms, MatchSource::TryDesugar) = expr {
++        arms.iter().find_map(|ty| find_err_return_type_arm(cx, ty))
++    } else {
++        None
++    }
++}
++
++// Check for From::from in one of the match arms.
++fn find_err_return_type_arm<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arm: &'tcx Arm<'_>) -> Option<Ty<'tcx>> {
++    if_chain! {
++        if let ExprKind::Ret(Some(ref err_ret)) = arm.body.kind;
++        if let ExprKind::Call(ref from_error_path, ref from_error_args) = err_ret.kind;
++        if let ExprKind::Path(ref from_error_fn) = from_error_path.kind;
++        if match_qpath(from_error_fn, &paths::TRY_FROM_ERROR);
++        if let Some(from_error_arg) = from_error_args.get(0);
++        then {
++            Some(cx.tables.expr_ty(from_error_arg))
++        } else {
++            None
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6d49f50d550e858d0a0f87bb92fa3e275f093e95
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2562 @@@
++#![allow(rustc::default_hash_types)]
++
++use std::borrow::Cow;
++use std::cmp::Ordering;
++use std::collections::BTreeMap;
++
++use if_chain::if_chain;
++use rustc_ast::ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy};
++use rustc_errors::{Applicability, DiagnosticBuilder};
++use rustc_hir as hir;
++use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor};
++use rustc_hir::{
++    BinOpKind, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem,
++    ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, QPath, Stmt, StmtKind, TraitFn,
++    TraitItem, TraitItemKind, TyKind, UnOp,
++};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TypeckTables};
++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_target::spec::abi::Abi;
++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, in_constant, int_bits, is_type_diagnostic_item,
++    last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral,
++    qpath_res, same_tys, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite,
++    span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext,
++};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for use of `Box<Vec<_>>` anywhere in the code.
++    ///
++    /// **Why is this bad?** `Vec` already keeps its contents in a separate area on
++    /// the heap. So if you `Box` it, you just add another level of indirection
++    /// without any benefit whatsoever.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// struct X {
++    ///     values: Box<Vec<Foo>>,
++    /// }
++    /// ```
++    ///
++    /// Better:
++    ///
++    /// ```rust,ignore
++    /// struct X {
++    ///     values: Vec<Foo>,
++    /// }
++    /// ```
++    pub BOX_VEC,
++    perf,
++    "usage of `Box<Vec<T>>`, vector elements are already on the heap"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code.
++    ///
++    /// **Why is this bad?** `Vec` already keeps its contents in a separate area on
++    /// the heap. So if you `Box` its contents, you just add another level of indirection.
++    ///
++    /// **Known problems:** Vec<Box<T: Sized>> makes sense if T is a large type (see #3530,
++    /// 1st comment).
++    ///
++    /// **Example:**
++    /// ```rust
++    /// struct X {
++    ///     values: Vec<Box<i32>>,
++    /// }
++    /// ```
++    ///
++    /// Better:
++    ///
++    /// ```rust
++    /// struct X {
++    ///     values: Vec<i32>,
++    /// }
++    /// ```
++    pub VEC_BOX,
++    complexity,
++    "usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for use of `Option<Option<_>>` in function signatures and type
++    /// definitions
++    ///
++    /// **Why is this bad?** `Option<_>` represents an optional value. `Option<Option<_>>`
++    /// represents an optional optional value which is logically the same thing as an optional
++    /// value but has an unneeded extra level of wrapping.
++    ///
++    /// If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases,
++    /// consider a custom `enum` instead, with clear names for each case.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example**
++    /// ```rust
++    /// fn get_data() -> Option<Option<u32>> {
++    ///     None
++    /// }
++    /// ```
++    ///
++    /// Better:
++    ///
++    /// ```rust
++    /// pub enum Contents {
++    ///     Data(Vec<u8>), // Was Some(Some(Vec<u8>))
++    ///     NotYetFetched, // Was Some(None)
++    ///     None,          // Was None
++    /// }
++    ///
++    /// fn get_data() -> Contents {
++    ///     Contents::None
++    /// }
++    /// ```
++    pub OPTION_OPTION,
++    pedantic,
++    "usage of `Option<Option<T>>`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of any `LinkedList`, suggesting to use a
++    /// `Vec` or a `VecDeque` (formerly called `RingBuf`).
++    ///
++    /// **Why is this bad?** Gankro says:
++    ///
++    /// > The TL;DR of `LinkedList` is that it's built on a massive amount of
++    /// pointers and indirection.
++    /// > It wastes memory, it has terrible cache locality, and is all-around slow.
++    /// `RingBuf`, while
++    /// > "only" amortized for push/pop, should be faster in the general case for
++    /// almost every possible
++    /// > workload, and isn't even amortized at all if you can predict the capacity
++    /// you need.
++    /// >
++    /// > `LinkedList`s are only really good if you're doing a lot of merging or
++    /// splitting of lists.
++    /// > This is because they can just mangle some pointers instead of actually
++    /// copying the data. Even
++    /// > if you're doing a lot of insertion in the middle of the list, `RingBuf`
++    /// can still be better
++    /// > because of how expensive it is to seek to the middle of a `LinkedList`.
++    ///
++    /// **Known problems:** False positives – the instances where using a
++    /// `LinkedList` makes sense are few and far between, but they can still happen.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # use std::collections::LinkedList;
++    /// let x: LinkedList<usize> = LinkedList::new();
++    /// ```
++    pub LINKEDLIST,
++    pedantic,
++    "usage of LinkedList, usually a vector is faster, or a more specialized data structure like a `VecDeque`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for use of `&Box<T>` anywhere in the code.
++    ///
++    /// **Why is this bad?** Any `&Box<T>` can also be a `&T`, which is more
++    /// general.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// fn foo(bar: &Box<T>) { ... }
++    /// ```
++    ///
++    /// Better:
++    ///
++    /// ```rust,ignore
++    /// fn foo(bar: &T) { ... }
++    /// ```
++    pub BORROWED_BOX,
++    complexity,
++    "a borrow of a boxed type"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for use of redundant allocations anywhere in the code.
++    ///
++    /// **Why is this bad?** Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Box<T>>`, `Box<&T>`
++    /// add an unnecessary level of indirection.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # use std::rc::Rc;
++    /// fn foo(bar: Rc<&usize>) {}
++    /// ```
++    ///
++    /// Better:
++    ///
++    /// ```rust
++    /// fn foo(bar: &usize) {}
++    /// ```
++    pub REDUNDANT_ALLOCATION,
++    perf,
++    "redundant allocation"
++}
++
++pub struct Types {
++    vec_box_size_threshold: u64,
++}
++
++impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Types {
++    fn check_fn(
++        &mut self,
++        cx: &LateContext<'_, '_>,
++        _: FnKind<'_>,
++        decl: &FnDecl<'_>,
++        _: &Body<'_>,
++        _: Span,
++        id: HirId,
++    ) {
++        // Skip trait implementations; see issue #605.
++        if let Some(hir::Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_item(id)) {
++            if let ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
++                return;
++            }
++        }
++
++        self.check_fn_decl(cx, decl);
++    }
++
++    fn check_struct_field(&mut self, cx: &LateContext<'_, '_>, field: &hir::StructField<'_>) {
++        self.check_ty(cx, &field.ty, false);
++    }
++
++    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),
++            _ => (),
++        }
++    }
++
++    fn check_local(&mut self, cx: &LateContext<'_, '_>, local: &Local<'_>) {
++        if let Some(ref ty) = local.ty {
++            self.check_ty(cx, ty, true);
++        }
++    }
++}
++
++/// Checks if `qpath` has last segment with type parameter matching `path`
++fn match_type_parameter(cx: &LateContext<'_, '_>, qpath: &QPath<'_>, path: &[&str]) -> Option<Span> {
++    let last = last_path_segment(qpath);
++    if_chain! {
++        if let Some(ref params) = last.args;
++        if !params.parenthesized;
++        if let Some(ty) = params.args.iter().find_map(|arg| match arg {
++            GenericArg::Type(ty) => Some(ty),
++            _ => None,
++        });
++        if let TyKind::Path(ref qpath) = ty.kind;
++        if let Some(did) = qpath_res(cx, qpath, ty.hir_id).opt_def_id();
++        if match_def_path(cx, did, path);
++        then {
++            return Some(ty.span);
++        }
++    }
++    None
++}
++
++fn match_borrows_parameter(_cx: &LateContext<'_, '_>, qpath: &QPath<'_>) -> Option<Span> {
++    let last = last_path_segment(qpath);
++    if_chain! {
++        if let Some(ref params) = last.args;
++        if !params.parenthesized;
++        if let Some(ty) = params.args.iter().find_map(|arg| match arg {
++            GenericArg::Type(ty) => Some(ty),
++            _ => None,
++        });
++        if let TyKind::Rptr(..) = ty.kind;
++        then {
++            return Some(ty.span);
++        }
++    }
++    None
++}
++
++impl Types {
++    pub fn new(vec_box_size_threshold: u64) -> Self {
++        Self { vec_box_size_threshold }
++    }
++
++    fn check_fn_decl(&mut self, cx: &LateContext<'_, '_>, decl: &FnDecl<'_>) {
++        for input in decl.inputs {
++            self.check_ty(cx, input, false);
++        }
++
++        if let FnRetTy::Return(ref ty) = decl.output {
++            self.check_ty(cx, ty, false);
++        }
++    }
++
++    /// Recursively check for `TypePass` lints in the given type. Stop at the first
++    /// lint found.
++    ///
++    /// The parameter `is_local` distinguishes the context of the type; types from
++    /// local bindings should only be checked for the `BORROWED_BOX` lint.
++    #[allow(clippy::too_many_lines)]
++    fn check_ty(&mut self, cx: &LateContext<'_, '_>, hir_ty: &hir::Ty<'_>, is_local: bool) {
++        if hir_ty.span.from_expansion() {
++            return;
++        }
++        match hir_ty.kind {
++            TyKind::Path(ref qpath) if !is_local => {
++                let hir_id = hir_ty.hir_id;
++                let res = qpath_res(cx, qpath, hir_id);
++                if let Some(def_id) = res.opt_def_id() {
++                    if Some(def_id) == cx.tcx.lang_items().owned_box() {
++                        if let Some(span) = match_borrows_parameter(cx, qpath) {
++                            span_lint_and_sugg(
++                                cx,
++                                REDUNDANT_ALLOCATION,
++                                hir_ty.span,
++                                "usage of `Box<&T>`",
++                                "try",
++                                snippet(cx, span, "..").to_string(),
++                                Applicability::MachineApplicable,
++                            );
++                            return; // don't recurse into the type
++                        }
++                        if match_type_parameter(cx, qpath, &paths::VEC).is_some() {
++                            span_lint_and_help(
++                                cx,
++                                BOX_VEC,
++                                hir_ty.span,
++                                "you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`",
++                                None,
++                                "`Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation.",
++                            );
++                            return; // don't recurse into the type
++                        }
++                    } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
++                        if let Some(span) = match_type_parameter(cx, qpath, &paths::RC) {
++                            span_lint_and_sugg(
++                                cx,
++                                REDUNDANT_ALLOCATION,
++                                hir_ty.span,
++                                "usage of `Rc<Rc<T>>`",
++                                "try",
++                                snippet(cx, span, "..").to_string(),
++                                Applicability::MachineApplicable,
++                            );
++                            return; // don't recurse into the type
++                        }
++                        if let Some(span) = match_type_parameter(cx, qpath, &paths::BOX) {
++                            span_lint_and_sugg(
++                                cx,
++                                REDUNDANT_ALLOCATION,
++                                hir_ty.span,
++                                "usage of `Rc<Box<T>>`",
++                                "try",
++                                snippet(cx, span, "..").to_string(),
++                                Applicability::MachineApplicable,
++                            );
++                            return; // don't recurse into the type
++                        }
++                        if let Some(span) = match_borrows_parameter(cx, qpath) {
++                            span_lint_and_sugg(
++                                cx,
++                                REDUNDANT_ALLOCATION,
++                                hir_ty.span,
++                                "usage of `Rc<&T>`",
++                                "try",
++                                snippet(cx, span, "..").to_string(),
++                                Applicability::MachineApplicable,
++                            );
++                            return; // don't recurse into the type
++                        }
++                    } else if cx.tcx.is_diagnostic_item(sym!(vec_type), def_id) {
++                        if_chain! {
++                            // Get the _ part of Vec<_>
++                            if let Some(ref last) = last_path_segment(qpath).args;
++                            if let Some(ty) = last.args.iter().find_map(|arg| match arg {
++                                GenericArg::Type(ty) => Some(ty),
++                                _ => None,
++                            });
++                            // ty is now _ at this point
++                            if let TyKind::Path(ref ty_qpath) = ty.kind;
++                            let res = qpath_res(cx, ty_qpath, ty.hir_id);
++                            if let Some(def_id) = res.opt_def_id();
++                            if Some(def_id) == cx.tcx.lang_items().owned_box();
++                            // At this point, we know ty is Box<T>, now get T
++                            if let Some(ref last) = last_path_segment(ty_qpath).args;
++                            if let Some(boxed_ty) = last.args.iter().find_map(|arg| match arg {
++                                GenericArg::Type(ty) => Some(ty),
++                                _ => None,
++                            });
++                            let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty);
++                            if ty_ty.is_sized(cx.tcx.at(ty.span), cx.param_env);
++                            if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes());
++                            if ty_ty_size <= self.vec_box_size_threshold;
++                            then {
++                                span_lint_and_sugg(
++                                    cx,
++                                    VEC_BOX,
++                                    hir_ty.span,
++                                    "`Vec<T>` is already on the heap, the boxing is unnecessary.",
++                                    "try",
++                                    format!("Vec<{}>", ty_ty),
++                                    Applicability::MachineApplicable,
++                                );
++                                return; // don't recurse into the type
++                            }
++                        }
++                    } else if cx.tcx.is_diagnostic_item(sym!(option_type), def_id) {
++                        if match_type_parameter(cx, qpath, &paths::OPTION).is_some() {
++                            span_lint(
++                                cx,
++                                OPTION_OPTION,
++                                hir_ty.span,
++                                "consider using `Option<T>` instead of `Option<Option<T>>` or a custom \
++                                 enum if you need to distinguish all 3 cases",
++                            );
++                            return; // don't recurse into the type
++                        }
++                    } else if match_def_path(cx, def_id, &paths::LINKED_LIST) {
++                        span_lint_and_help(
++                            cx,
++                            LINKEDLIST,
++                            hir_ty.span,
++                            "I see you're using a LinkedList! Perhaps you meant some other data structure?",
++                            None,
++                            "a `VecDeque` might work",
++                        );
++                        return; // don't recurse into the type
++                    }
++                }
++                match *qpath {
++                    QPath::Resolved(Some(ref ty), ref p) => {
++                        self.check_ty(cx, ty, is_local);
++                        for ty in p.segments.iter().flat_map(|seg| {
++                            seg.args
++                                .as_ref()
++                                .map_or_else(|| [].iter(), |params| params.args.iter())
++                                .filter_map(|arg| match arg {
++                                    GenericArg::Type(ty) => Some(ty),
++                                    _ => None,
++                                })
++                        }) {
++                            self.check_ty(cx, ty, is_local);
++                        }
++                    },
++                    QPath::Resolved(None, ref p) => {
++                        for ty in p.segments.iter().flat_map(|seg| {
++                            seg.args
++                                .as_ref()
++                                .map_or_else(|| [].iter(), |params| params.args.iter())
++                                .filter_map(|arg| match arg {
++                                    GenericArg::Type(ty) => Some(ty),
++                                    _ => None,
++                                })
++                        }) {
++                            self.check_ty(cx, ty, is_local);
++                        }
++                    },
++                    QPath::TypeRelative(ref ty, ref seg) => {
++                        self.check_ty(cx, ty, is_local);
++                        if let Some(ref params) = seg.args {
++                            for ty in params.args.iter().filter_map(|arg| match arg {
++                                GenericArg::Type(ty) => Some(ty),
++                                _ => None,
++                            }) {
++                                self.check_ty(cx, ty, is_local);
++                            }
++                        }
++                    },
++                }
++            },
++            TyKind::Rptr(ref lt, ref mut_ty) => self.check_ty_rptr(cx, hir_ty, is_local, lt, mut_ty),
++            // recurse
++            TyKind::Slice(ref ty) | TyKind::Array(ref ty, _) | TyKind::Ptr(MutTy { ref ty, .. }) => {
++                self.check_ty(cx, ty, is_local)
++            },
++            TyKind::Tup(tys) => {
++                for ty in tys {
++                    self.check_ty(cx, ty, is_local);
++                }
++            },
++            _ => {},
++        }
++    }
++
++    fn check_ty_rptr(
++        &mut self,
++        cx: &LateContext<'_, '_>,
++        hir_ty: &hir::Ty<'_>,
++        is_local: bool,
++        lt: &Lifetime,
++        mut_ty: &MutTy<'_>,
++    ) {
++        match mut_ty.ty.kind {
++            TyKind::Path(ref qpath) => {
++                let hir_id = mut_ty.ty.hir_id;
++                let def = qpath_res(cx, qpath, hir_id);
++                if_chain! {
++                    if let Some(def_id) = def.opt_def_id();
++                    if Some(def_id) == cx.tcx.lang_items().owned_box();
++                    if let QPath::Resolved(None, ref path) = *qpath;
++                    if let [ref bx] = *path.segments;
++                    if let Some(ref params) = bx.args;
++                    if !params.parenthesized;
++                    if let Some(inner) = params.args.iter().find_map(|arg| match arg {
++                        GenericArg::Type(ty) => Some(ty),
++                        _ => None,
++                    });
++                    then {
++                        if is_any_trait(inner) {
++                            // Ignore `Box<Any>` types; see issue #1884 for details.
++                            return;
++                        }
++
++                        let ltopt = if lt.is_elided() {
++                            String::new()
++                        } else {
++                            format!("{} ", lt.name.ident().as_str())
++                        };
++
++                        if mut_ty.mutbl == Mutability::Mut {
++                            // Ignore `&mut Box<T>` types; see issue #2907 for
++                            // details.
++                            return;
++                        }
++                        let mut applicability = Applicability::MachineApplicable;
++                        span_lint_and_sugg(
++                            cx,
++                            BORROWED_BOX,
++                            hir_ty.span,
++                            "you seem to be trying to use `&Box<T>`. Consider using just `&T`",
++                            "try",
++                            format!(
++                                "&{}{}",
++                                ltopt,
++                                &snippet_with_applicability(cx, inner.span, "..", &mut applicability)
++                            ),
++                            Applicability::Unspecified,
++                        );
++                        return; // don't recurse into the type
++                    }
++                };
++                self.check_ty(cx, &mut_ty.ty, is_local);
++            },
++            _ => self.check_ty(cx, &mut_ty.ty, is_local),
++        }
++    }
++}
++
++// Returns true if given type is `Any` trait.
++fn is_any_trait(t: &hir::Ty<'_>) -> bool {
++    if_chain! {
++        if let TyKind::TraitObject(ref traits, _) = t.kind;
++        if !traits.is_empty();
++        // Only Send/Sync can be used as additional traits, so it is enough to
++        // check only the first trait.
++        if match_path(&traits[0].trait_ref.path, &paths::ANY_TRAIT);
++        then {
++            return true;
++        }
++    }
++
++    false
++}
++
++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<'a, 'tcx> LateLintPass<'a, 'tcx> for LetUnitValue {
++    fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) {
++        if let StmtKind::Local(ref local) = stmt.kind {
++            if is_unit(cx.tables.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<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitCmp {
++    fn check_expr(&mut self, cx: &LateContext<'a, '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.tables.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.tables.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<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg {
++    fn check_expr(&mut self, cx: &LateContext<'a, '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) => {
++                for arg in args {
++                    if is_unit(cx.tables.expr_ty(arg)) && !is_unit_literal(arg) {
++                        if let ExprKind::Match(.., match_source) = &arg.kind {
++                            if *match_source == MatchSource::TryDesugar {
++                                continue;
++                            }
++                        }
++
++                        span_lint_and_sugg(
++                            cx,
++                            UNIT_ARG,
++                            arg.span,
++                            "passing a unit value to a function",
++                            "if you intended to pass a unit value, use a unit literal instead",
++                            "()".to_string(),
++                            Applicability::MaybeIncorrect,
++                        );
++                    }
++                }
++            },
++            _ => (),
++        }
++    }
++}
++
++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 {
++    match ty.kind {
++        ty::Tuple(slice) if slice.is_empty() => true,
++        _ => false,
++    }
++}
++
++fn is_unit_literal(expr: &Expr<'_>) -> bool {
++    match expr.kind {
++        ExprKind::Tup(ref slice) if slice.is_empty() => true,
++        _ => false,
++    }
++}
++
++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
++    /// rounding errors. This possible rounding is to be expected, so this lint is
++    /// `Allow` by default.
++    ///
++    /// Basically, this warns on casting any integer with 32 or more bits to `f32`
++    /// or any 64-bit integer to `f64`.
++    ///
++    /// **Why is this bad?** It's not bad at all. But in some applications it can be
++    /// helpful to know where precision loss can take place. This lint can help find
++    /// those places in the code.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x = u64::MAX;
++    /// x as f64;
++    /// ```
++    pub CAST_PRECISION_LOSS,
++    pedantic,
++    "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for casts from a signed to an unsigned numerical
++    /// type. In this case, negative values wrap around to large positive values,
++    /// which can be quite surprising in practice. However, as the cast works as
++    /// defined, this lint is `Allow` by default.
++    ///
++    /// **Why is this bad?** Possibly surprising results. You can activate this lint
++    /// as a one-time check to see where numerical wrapping can arise.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let y: i8 = -1;
++    /// y as u128; // will return 18446744073709551615
++    /// ```
++    pub CAST_SIGN_LOSS,
++    pedantic,
++    "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for casts between numerical types that may
++    /// truncate large values. This is expected behavior, so the cast is `Allow` by
++    /// default.
++    ///
++    /// **Why is this bad?** In some problem domains, it is good practice to avoid
++    /// truncation. This lint can be activated to help assess where additional
++    /// checks could be beneficial.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// fn as_u8(x: u64) -> u8 {
++    ///     x as u8
++    /// }
++    /// ```
++    pub CAST_POSSIBLE_TRUNCATION,
++    pedantic,
++    "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for casts from an unsigned type to a signed type of
++    /// the same size. Performing such a cast is a 'no-op' for the compiler,
++    /// i.e., nothing is changed at the bit level, and the binary representation of
++    /// the value is reinterpreted. This can cause wrapping if the value is too big
++    /// for the target signed type. However, the cast works as defined, so this lint
++    /// is `Allow` by default.
++    ///
++    /// **Why is this bad?** While such a cast is not bad in itself, the results can
++    /// be surprising when this is not the intended behavior, as demonstrated by the
++    /// example below.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// u32::MAX as i32; // will yield a value of `-1`
++    /// ```
++    pub CAST_POSSIBLE_WRAP,
++    pedantic,
++    "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for casts between numerical types that may
++    /// be replaced by safe conversion functions.
++    ///
++    /// **Why is this bad?** Rust's `as` keyword will perform many kinds of
++    /// conversions, including silently lossy conversions. Conversion functions such
++    /// as `i32::from` will only perform lossless conversions. Using the conversion
++    /// functions prevents conversions from turning into silent lossy conversions if
++    /// the types of the input expressions ever change, and make it easier for
++    /// people reading the code to know that the conversion is lossless.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// fn as_u64(x: u8) -> u64 {
++    ///     x as u64
++    /// }
++    /// ```
++    ///
++    /// Using `::from` would look like this:
++    ///
++    /// ```rust
++    /// fn as_u64(x: u8) -> u64 {
++    ///     u64::from(x)
++    /// }
++    /// ```
++    pub CAST_LOSSLESS,
++    pedantic,
++    "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for casts to the same type.
++    ///
++    /// **Why is this bad?** It's just unnecessary.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let _ = 2i32 as i32;
++    /// ```
++    pub UNNECESSARY_CAST,
++    complexity,
++    "cast to the same type, e.g., `x as i32` where `x: i32`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for casts from a less-strictly-aligned pointer to a
++    /// more-strictly-aligned pointer
++    ///
++    /// **Why is this bad?** Dereferencing the resulting pointer may be undefined
++    /// behavior.
++    ///
++    /// **Known problems:** Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar
++    /// on the resulting pointer is fine.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let _ = (&1u8 as *const u8) as *const u16;
++    /// let _ = (&mut 1u8 as *mut u8) as *mut u16;
++    /// ```
++    pub CAST_PTR_ALIGNMENT,
++    correctness,
++    "cast from a pointer to a more-strictly-aligned pointer"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for casts of function pointers to something other than usize
++    ///
++    /// **Why is this bad?**
++    /// Casting a function pointer to anything other than usize/isize is not portable across
++    /// architectures, because you end up losing bits if the target type is too small or end up with a
++    /// bunch of extra bits that waste space and add more instructions to the final binary than
++    /// strictly necessary for the problem
++    ///
++    /// Casting to isize also doesn't make sense since there are no signed addresses.
++    ///
++    /// **Example**
++    ///
++    /// ```rust
++    /// // Bad
++    /// fn fun() -> i32 { 1 }
++    /// let a = fun as i64;
++    ///
++    /// // Good
++    /// fn fun2() -> i32 { 1 }
++    /// let a = fun2 as usize;
++    /// ```
++    pub FN_TO_NUMERIC_CAST,
++    style,
++    "casting a function pointer to a numeric type other than usize"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for casts of a function pointer to a numeric type not wide enough to
++    /// store address.
++    ///
++    /// **Why is this bad?**
++    /// Such a cast discards some bits of the function's address. If this is intended, it would be more
++    /// clearly expressed by casting to usize first, then casting the usize to the intended type (with
++    /// a comment) to perform the truncation.
++    ///
++    /// **Example**
++    ///
++    /// ```rust
++    /// // Bad
++    /// fn fn1() -> i16 {
++    ///     1
++    /// };
++    /// let _ = fn1 as i32;
++    ///
++    /// // Better: Cast to usize first, then comment with the reason for the truncation
++    /// fn fn2() -> i16 {
++    ///     1
++    /// };
++    /// let fn_ptr = fn2 as usize;
++    /// let fn_ptr_truncated = fn_ptr as i32;
++    /// ```
++    pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
++    style,
++    "casting a function pointer to a numeric type not wide enough to store the address"
++}
++
++/// Returns the size in bits of an integral type.
++/// Will return 0 if the type is not an int or uint variant
++fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 {
++    match typ.kind {
++        ty::Int(i) => match i {
++            IntTy::Isize => tcx.data_layout.pointer_size.bits(),
++            IntTy::I8 => 8,
++            IntTy::I16 => 16,
++            IntTy::I32 => 32,
++            IntTy::I64 => 64,
++            IntTy::I128 => 128,
++        },
++        ty::Uint(i) => match i {
++            UintTy::Usize => tcx.data_layout.pointer_size.bits(),
++            UintTy::U8 => 8,
++            UintTy::U16 => 16,
++            UintTy::U32 => 32,
++            UintTy::U64 => 64,
++            UintTy::U128 => 128,
++        },
++        _ => 0,
++    }
++}
++
++fn is_isize_or_usize(typ: Ty<'_>) -> bool {
++    match typ.kind {
++        ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => true,
++        _ => false,
++    }
++}
++
++fn span_precision_loss_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to_f64: bool) {
++    let mantissa_nbits = if cast_to_f64 { 52 } else { 23 };
++    let arch_dependent = is_isize_or_usize(cast_from) && cast_to_f64;
++    let arch_dependent_str = "on targets with 64-bit wide pointers ";
++    let from_nbits_str = if arch_dependent {
++        "64".to_owned()
++    } else if is_isize_or_usize(cast_from) {
++        "32 or 64".to_owned()
++    } else {
++        int_ty_to_nbits(cast_from, cx.tcx).to_string()
++    };
++    span_lint(
++        cx,
++        CAST_PRECISION_LOSS,
++        expr.span,
++        &format!(
++            "casting `{0}` to `{1}` causes a loss of precision {2}(`{0}` is {3} bits wide, \
++             but `{1}`'s mantissa is only {4} bits wide)",
++            cast_from,
++            if cast_to_f64 { "f64" } else { "f32" },
++            if arch_dependent { arch_dependent_str } else { "" },
++            from_nbits_str,
++            mantissa_nbits
++        ),
++    );
++}
++
++fn should_strip_parens(op: &Expr<'_>, snip: &str) -> bool {
++    if let ExprKind::Binary(_, _, _) = op.kind {
++        if snip.starts_with('(') && snip.ends_with(')') {
++            return true;
++        }
++    }
++    false
++}
++
++fn span_lossless_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>, op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
++    // Do not suggest using From in consts/statics until it is valid to do so (see #2267).
++    if in_constant(cx, expr.hir_id) {
++        return;
++    }
++    // The suggestion is to use a function call, so if the original expression
++    // has parens on the outside, they are no longer needed.
++    let mut applicability = Applicability::MachineApplicable;
++    let opt = snippet_opt(cx, op.span);
++    let sugg = if let Some(ref snip) = opt {
++        if should_strip_parens(op, snip) {
++            &snip[1..snip.len() - 1]
++        } else {
++            snip.as_str()
++        }
++    } else {
++        applicability = Applicability::HasPlaceholders;
++        ".."
++    };
++
++    span_lint_and_sugg(
++        cx,
++        CAST_LOSSLESS,
++        expr.span,
++        &format!(
++            "casting `{}` to `{}` may become silently lossy if you later change the type",
++            cast_from, cast_to
++        ),
++        "try",
++        format!("{}::from({})", cast_to, sugg),
++        applicability,
++    );
++}
++
++enum ArchSuffix {
++    _32,
++    _64,
++    None,
++}
++
++fn check_loss_of_sign(cx: &LateContext<'_, '_>, expr: &Expr<'_>, op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
++    if !cast_from.is_signed() || cast_to.is_signed() {
++        return;
++    }
++
++    // don't lint for positive constants
++    let const_val = constant(cx, &cx.tables, op);
++    if_chain! {
++        if let Some((const_val, _)) = const_val;
++        if let Constant::Int(n) = const_val;
++        if let ty::Int(ity) = cast_from.kind;
++        if sext(cx.tcx, n, ity) >= 0;
++        then {
++            return
++        }
++    }
++
++    // don't lint for the result of methods that always return non-negative values
++    if let ExprKind::MethodCall(ref path, _, _) = op.kind {
++        let mut method_name = path.ident.name.as_str();
++        let whitelisted_methods = ["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"];
++
++        if_chain! {
++            if method_name == "unwrap";
++            if let Some(arglist) = method_chain_args(op, &["unwrap"]);
++            if let ExprKind::MethodCall(ref inner_path, _, _) = &arglist[0][0].kind;
++            then {
++                method_name = inner_path.ident.name.as_str();
++            }
++        }
++
++        if whitelisted_methods.iter().any(|&name| method_name == name) {
++            return;
++        }
++    }
++
++    span_lint(
++        cx,
++        CAST_SIGN_LOSS,
++        expr.span,
++        &format!(
++            "casting `{}` to `{}` may lose the sign of the value",
++            cast_from, cast_to
++        ),
++    );
++}
++
++fn check_truncation_and_wrapping(cx: &LateContext<'_, '_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
++    let arch_64_suffix = " on targets with 64-bit wide pointers";
++    let arch_32_suffix = " on targets with 32-bit wide pointers";
++    let cast_unsigned_to_signed = !cast_from.is_signed() && cast_to.is_signed();
++    let from_nbits = int_ty_to_nbits(cast_from, cx.tcx);
++    let to_nbits = int_ty_to_nbits(cast_to, cx.tcx);
++    let (span_truncation, suffix_truncation, span_wrap, suffix_wrap) =
++        match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) {
++            (true, true) | (false, false) => (
++                to_nbits < from_nbits,
++                ArchSuffix::None,
++                to_nbits == from_nbits && cast_unsigned_to_signed,
++                ArchSuffix::None,
++            ),
++            (true, false) => (
++                to_nbits <= 32,
++                if to_nbits == 32 {
++                    ArchSuffix::_64
++                } else {
++                    ArchSuffix::None
++                },
++                to_nbits <= 32 && cast_unsigned_to_signed,
++                ArchSuffix::_32,
++            ),
++            (false, true) => (
++                from_nbits == 64,
++                ArchSuffix::_32,
++                cast_unsigned_to_signed,
++                if from_nbits == 64 {
++                    ArchSuffix::_64
++                } else {
++                    ArchSuffix::_32
++                },
++            ),
++        };
++    if span_truncation {
++        span_lint(
++            cx,
++            CAST_POSSIBLE_TRUNCATION,
++            expr.span,
++            &format!(
++                "casting `{}` to `{}` may truncate the value{}",
++                cast_from,
++                cast_to,
++                match suffix_truncation {
++                    ArchSuffix::_32 => arch_32_suffix,
++                    ArchSuffix::_64 => arch_64_suffix,
++                    ArchSuffix::None => "",
++                }
++            ),
++        );
++    }
++    if span_wrap {
++        span_lint(
++            cx,
++            CAST_POSSIBLE_WRAP,
++            expr.span,
++            &format!(
++                "casting `{}` to `{}` may wrap around the value{}",
++                cast_from,
++                cast_to,
++                match suffix_wrap {
++                    ArchSuffix::_32 => arch_32_suffix,
++                    ArchSuffix::_64 => arch_64_suffix,
++                    ArchSuffix::None => "",
++                }
++            ),
++        );
++    }
++}
++
++fn check_lossless(cx: &LateContext<'_, '_>, expr: &Expr<'_>, op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
++    let cast_signed_to_unsigned = cast_from.is_signed() && !cast_to.is_signed();
++    let from_nbits = int_ty_to_nbits(cast_from, cx.tcx);
++    let to_nbits = int_ty_to_nbits(cast_to, cx.tcx);
++    if !is_isize_or_usize(cast_from) && !is_isize_or_usize(cast_to) && from_nbits < to_nbits && !cast_signed_to_unsigned
++    {
++        span_lossless_lint(cx, expr, op, cast_from, cast_to);
++    }
++}
++
++declare_lint_pass!(Casts => [
++    CAST_PRECISION_LOSS,
++    CAST_SIGN_LOSS,
++    CAST_POSSIBLE_TRUNCATION,
++    CAST_POSSIBLE_WRAP,
++    CAST_LOSSLESS,
++    UNNECESSARY_CAST,
++    CAST_PTR_ALIGNMENT,
++    FN_TO_NUMERIC_CAST,
++    FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
++]);
++
++// Check if the given type is either `core::ffi::c_void` or
++// one of the platform specific `libc::<platform>::c_void` of libc.
++fn is_c_void(cx: &LateContext<'_, '_>, ty: Ty<'_>) -> bool {
++    if let ty::Adt(adt, _) = ty.kind {
++        let names = cx.get_def_path(adt.did);
++
++        if names.is_empty() {
++            return false;
++        }
++        if names[0] == sym!(libc) || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) {
++            return true;
++        }
++    }
++    false
++}
++
++/// Returns the mantissa bits wide of a fp type.
++/// Will return 0 if the type is not a fp
++fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 {
++    match typ.kind {
++        ty::Float(FloatTy::F32) => 23,
++        ty::Float(FloatTy::F64) | ty::Infer(InferTy::FloatVar(_)) => 52,
++        _ => 0,
++    }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Casts {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if expr.span.from_expansion() {
++            return;
++        }
++        if let ExprKind::Cast(ref ex, _) = expr.kind {
++            let (cast_from, cast_to) = (cx.tables.expr_ty(ex), cx.tables.expr_ty(expr));
++            lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to);
++            if let ExprKind::Lit(ref lit) = ex.kind {
++                if_chain! {
++                    if let LitKind::Int(n, _) = lit.node;
++                    if let Some(src) = snippet_opt(cx, lit.span);
++                    if cast_to.is_floating_point();
++                    if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node);
++                    let from_nbits = 128 - n.leading_zeros();
++                    let to_nbits = fp_ty_mantissa_nbits(cast_to);
++                    if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal();
++                    then {
++                        span_lint_and_sugg(
++                            cx,
++                            UNNECESSARY_CAST,
++                            expr.span,
++                            &format!("casting integer literal to `{}` is unnecessary", cast_to),
++                            "try",
++                            format!("{}_{}", n, cast_to),
++                            Applicability::MachineApplicable,
++                        );
++                        return;
++                    }
++                }
++                match lit.node {
++                    LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {},
++                    _ => {
++                        if cast_from.kind == cast_to.kind && !in_external_macro(cx.sess(), expr.span) {
++                            span_lint(
++                                cx,
++                                UNNECESSARY_CAST,
++                                expr.span,
++                                &format!(
++                                    "casting to the same type is unnecessary (`{}` -> `{}`)",
++                                    cast_from, cast_to
++                                ),
++                            );
++                        }
++                    },
++                }
++            }
++            if cast_from.is_numeric() && cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
++                lint_numeric_casts(cx, expr, ex, cast_from, cast_to);
++            }
++
++            lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
++        }
++    }
++}
++
++fn lint_numeric_casts<'tcx>(
++    cx: &LateContext<'_, 'tcx>,
++    expr: &Expr<'tcx>,
++    cast_expr: &Expr<'_>,
++    cast_from: Ty<'tcx>,
++    cast_to: Ty<'tcx>,
++) {
++    match (cast_from.is_integral(), cast_to.is_integral()) {
++        (true, false) => {
++            let from_nbits = int_ty_to_nbits(cast_from, cx.tcx);
++            let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind {
++                32
++            } else {
++                64
++            };
++            if is_isize_or_usize(cast_from) || from_nbits >= to_nbits {
++                span_precision_loss_lint(cx, expr, cast_from, to_nbits == 64);
++            }
++            if from_nbits < to_nbits {
++                span_lossless_lint(cx, expr, cast_expr, cast_from, cast_to);
++            }
++        },
++        (false, true) => {
++            span_lint(
++                cx,
++                CAST_POSSIBLE_TRUNCATION,
++                expr.span,
++                &format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to),
++            );
++            if !cast_to.is_signed() {
++                span_lint(
++                    cx,
++                    CAST_SIGN_LOSS,
++                    expr.span,
++                    &format!(
++                        "casting `{}` to `{}` may lose the sign of the value",
++                        cast_from, cast_to
++                    ),
++                );
++            }
++        },
++        (true, true) => {
++            check_loss_of_sign(cx, expr, cast_expr, cast_from, cast_to);
++            check_truncation_and_wrapping(cx, expr, cast_from, cast_to);
++            check_lossless(cx, expr, cast_expr, cast_from, cast_to);
++        },
++        (false, false) => {
++            if let (&ty::Float(FloatTy::F64), &ty::Float(FloatTy::F32)) = (&cast_from.kind, &cast_to.kind) {
++                span_lint(
++                    cx,
++                    CAST_POSSIBLE_TRUNCATION,
++                    expr.span,
++                    "casting `f64` to `f32` may truncate the value",
++                );
++            }
++            if let (&ty::Float(FloatTy::F32), &ty::Float(FloatTy::F64)) = (&cast_from.kind, &cast_to.kind) {
++                span_lossless_lint(cx, expr, cast_expr, cast_from, cast_to);
++            }
++        },
++    }
++}
++
++fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) {
++    if_chain! {
++        if let ty::RawPtr(from_ptr_ty) = &cast_from.kind;
++        if let ty::RawPtr(to_ptr_ty) = &cast_to.kind;
++        if let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty);
++        if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty);
++        if from_layout.align.abi < to_layout.align.abi;
++        // with c_void, we inherently need to trust the user
++        if !is_c_void(cx, from_ptr_ty.ty);
++        // when casting from a ZST, we don't know enough to properly lint
++        if !from_layout.is_zst();
++        then {
++            span_lint(
++                cx,
++                CAST_PTR_ALIGNMENT,
++                expr.span,
++                &format!(
++                    "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)",
++                    cast_from,
++                    cast_to,
++                    from_layout.align.abi.bytes(),
++                    to_layout.align.abi.bytes(),
++                ),
++            );
++        }
++    }
++}
++
++fn lint_fn_to_numeric_cast(
++    cx: &LateContext<'_, '_>,
++    expr: &Expr<'_>,
++    cast_expr: &Expr<'_>,
++    cast_from: Ty<'_>,
++    cast_to: Ty<'_>,
++) {
++    // We only want to check casts to `ty::Uint` or `ty::Int`
++    match cast_to.kind {
++        ty::Uint(_) | ty::Int(..) => { /* continue on */ },
++        _ => return,
++    }
++    match cast_from.kind {
++        ty::FnDef(..) | ty::FnPtr(_) => {
++            let mut applicability = Applicability::MaybeIncorrect;
++            let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
++
++            let to_nbits = int_ty_to_nbits(cast_to, cx.tcx);
++            if to_nbits < cx.tcx.data_layout.pointer_size.bits() {
++                span_lint_and_sugg(
++                    cx,
++                    FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
++                    expr.span,
++                    &format!(
++                        "casting function pointer `{}` to `{}`, which truncates the value",
++                        from_snippet, cast_to
++                    ),
++                    "try",
++                    format!("{} as usize", from_snippet),
++                    applicability,
++                );
++            } else if cast_to.kind != ty::Uint(UintTy::Usize) {
++                span_lint_and_sugg(
++                    cx,
++                    FN_TO_NUMERIC_CAST,
++                    expr.span,
++                    &format!("casting function pointer `{}` to `{}`", from_snippet, cast_to),
++                    "try",
++                    format!("{} as usize", from_snippet),
++                    applicability,
++                );
++            }
++        },
++        _ => {},
++    }
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for types used in structs, parameters and `let`
++    /// declarations above a certain complexity threshold.
++    ///
++    /// **Why is this bad?** Too complex types make the code less readable. Consider
++    /// using a `type` definition to simplify them.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # use std::rc::Rc;
++    /// struct Foo {
++    ///     inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>,
++    /// }
++    /// ```
++    pub TYPE_COMPLEXITY,
++    complexity,
++    "usage of very complex types that might be better factored into `type` definitions"
++}
++
++pub struct TypeComplexity {
++    threshold: u64,
++}
++
++impl TypeComplexity {
++    #[must_use]
++    pub fn new(threshold: u64) -> Self {
++        Self { threshold }
++    }
++}
++
++impl_lint_pass!(TypeComplexity => [TYPE_COMPLEXITY]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeComplexity {
++    fn check_fn(
++        &mut self,
++        cx: &LateContext<'a, 'tcx>,
++        _: FnKind<'tcx>,
++        decl: &'tcx FnDecl<'_>,
++        _: &'tcx Body<'_>,
++        _: Span,
++        _: HirId,
++    ) {
++        self.check_fndecl(cx, decl);
++    }
++
++    fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, field: &'tcx hir::StructField<'_>) {
++        // enum variants are also struct fields now
++        self.check_type(cx, &field.ty);
++    }
++
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++        match item.kind {
++            ItemKind::Static(ref ty, _, _) | ItemKind::Const(ref ty, _) => self.check_type(cx, ty),
++            // functions, enums, structs, impls and traits are covered
++            _ => (),
++        }
++    }
++
++    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem<'_>) {
++        match item.kind {
++            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
++            _ => (),
++        }
++    }
++
++    fn check_impl_item(&mut self, cx: &LateContext<'a, '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
++            _ => (),
++        }
++    }
++
++    fn check_local(&mut self, cx: &LateContext<'a, 'tcx>, local: &'tcx Local<'_>) {
++        if let Some(ref ty) = local.ty {
++            self.check_type(cx, ty);
++        }
++    }
++}
++
++impl<'a, 'tcx> TypeComplexity {
++    fn check_fndecl(&self, cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl<'_>) {
++        for arg in decl.inputs {
++            self.check_type(cx, arg);
++        }
++        if let FnRetTy::Return(ref ty) = decl.output {
++            self.check_type(cx, ty);
++        }
++    }
++
++    fn check_type(&self, cx: &LateContext<'_, '_>, ty: &hir::Ty<'_>) {
++        if ty.span.from_expansion() {
++            return;
++        }
++        let score = {
++            let mut visitor = TypeComplexityVisitor { score: 0, nest: 1 };
++            visitor.visit_ty(ty);
++            visitor.score
++        };
++
++        if score > self.threshold {
++            span_lint(
++                cx,
++                TYPE_COMPLEXITY,
++                ty.span,
++                "very complex type used. Consider factoring parts into `type` definitions",
++            );
++        }
++    }
++}
++
++/// Walks a type and assigns a complexity score to it.
++struct TypeComplexityVisitor {
++    /// total complexity score of the type
++    score: u64,
++    /// current nesting level
++    nest: u64,
++}
++
++impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor {
++    type Map = Map<'tcx>;
++
++    fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) {
++        let (add_score, sub_nest) = match ty.kind {
++            // _, &x and *x have only small overhead; don't mess with nesting level
++            TyKind::Infer | TyKind::Ptr(..) | TyKind::Rptr(..) => (1, 0),
++
++            // the "normal" components of a type: named types, arrays/tuples
++            TyKind::Path(..) | TyKind::Slice(..) | TyKind::Tup(..) | TyKind::Array(..) => (10 * self.nest, 1),
++
++            // 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, _) => {
++                let has_lifetime_parameters = param_bounds.iter().any(|bound| {
++                    bound.bound_generic_params.iter().any(|gen| match gen.kind {
++                        GenericParamKind::Lifetime { .. } => true,
++                        _ => false,
++                    })
++                });
++                if has_lifetime_parameters {
++                    // complex trait bounds like A<'a, 'b>
++                    (50 * self.nest, 1)
++                } else {
++                    // simple trait bounds like A + B
++                    (20 * self.nest, 0)
++                }
++            },
++
++            _ => (0, 0),
++        };
++        self.score += add_score;
++        self.nest += sub_nest;
++        walk_ty(self, ty);
++        self.nest -= sub_nest;
++    }
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for expressions where a character literal is cast
++    /// to `u8` and suggests using a byte literal instead.
++    ///
++    /// **Why is this bad?** In general, casting values to smaller types is
++    /// error-prone and should be avoided where possible. In the particular case of
++    /// converting a character literal to u8, it is easy to avoid by just using a
++    /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter
++    /// than `'a' as u8`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// 'x' as u8
++    /// ```
++    ///
++    /// A better version, using the byte literal:
++    ///
++    /// ```rust,ignore
++    /// b'x'
++    /// ```
++    pub CHAR_LIT_AS_U8,
++    complexity,
++    "casting a character literal to `u8` truncates"
++}
++
++declare_lint_pass!(CharLitAsU8 => [CHAR_LIT_AS_U8]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CharLitAsU8 {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            if !expr.span.from_expansion();
++            if let ExprKind::Cast(e, _) = &expr.kind;
++            if let ExprKind::Lit(l) = &e.kind;
++            if let LitKind::Char(c) = l.node;
++            if ty::Uint(UintTy::U8) == cx.tables.expr_ty(expr).kind;
++            then {
++                let mut applicability = Applicability::MachineApplicable;
++                let snippet = snippet_with_applicability(cx, e.span, "'x'", &mut applicability);
++
++                span_lint_and_then(
++                    cx,
++                    CHAR_LIT_AS_U8,
++                    expr.span,
++                    "casting a character literal to `u8` truncates",
++                    |diag| {
++                        diag.note("`char` is four bytes wide, but `u8` is a single byte");
++
++                        if c.is_ascii() {
++                            diag.span_suggestion(
++                                expr.span,
++                                "use a byte literal instead",
++                                format!("b{}", snippet),
++                                applicability,
++                            );
++                        }
++                });
++            }
++        }
++    }
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for comparisons where one side of the relation is
++    /// either the minimum or maximum value for its type and warns if it involves a
++    /// case that is always true or always false. Only integer and boolean types are
++    /// checked.
++    ///
++    /// **Why is this bad?** An expression like `min <= x` may misleadingly imply
++    /// that it is possible for `x` to be less than the minimum. Expressions like
++    /// `max < x` are probably mistakes.
++    ///
++    /// **Known problems:** For `usize` the size of the current compile target will
++    /// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such
++    /// a comparison to detect target pointer width will trigger this lint. One can
++    /// use `mem::sizeof` and compare its value or conditional compilation
++    /// attributes
++    /// like `#[cfg(target_pointer_width = "64")] ..` instead.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// let vec: Vec<isize> = Vec::new();
++    /// if vec.len() <= 0 {}
++    /// if 100 > i32::MAX {}
++    /// ```
++    pub ABSURD_EXTREME_COMPARISONS,
++    correctness,
++    "a comparison with a maximum or minimum value that is always true or false"
++}
++
++declare_lint_pass!(AbsurdExtremeComparisons => [ABSURD_EXTREME_COMPARISONS]);
++
++enum ExtremeType {
++    Minimum,
++    Maximum,
++}
++
++struct ExtremeExpr<'a> {
++    which: ExtremeType,
++    expr: &'a Expr<'a>,
++}
++
++enum AbsurdComparisonResult {
++    AlwaysFalse,
++    AlwaysTrue,
++    InequalityImpossible,
++}
++
++fn is_cast_between_fixed_and_target<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
++    if let ExprKind::Cast(ref cast_exp, _) = expr.kind {
++        let precast_ty = cx.tables.expr_ty(cast_exp);
++        let cast_ty = cx.tables.expr_ty(expr);
++
++        return is_isize_or_usize(precast_ty) != is_isize_or_usize(cast_ty);
++    }
++
++    false
++}
++
++fn detect_absurd_comparison<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    op: BinOpKind,
++    lhs: &'tcx Expr<'_>,
++    rhs: &'tcx Expr<'_>,
++) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> {
++    use crate::types::AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible};
++    use crate::types::ExtremeType::{Maximum, Minimum};
++    use crate::utils::comparisons::{normalize_comparison, Rel};
++
++    // absurd comparison only makes sense on primitive types
++    // primitive types don't implement comparison operators with each other
++    if cx.tables.expr_ty(lhs) != cx.tables.expr_ty(rhs) {
++        return None;
++    }
++
++    // comparisons between fix sized types and target sized types are considered unanalyzable
++    if is_cast_between_fixed_and_target(cx, lhs) || is_cast_between_fixed_and_target(cx, rhs) {
++        return None;
++    }
++
++    let (rel, normalized_lhs, normalized_rhs) = normalize_comparison(op, lhs, rhs)?;
++
++    let lx = detect_extreme_expr(cx, normalized_lhs);
++    let rx = detect_extreme_expr(cx, normalized_rhs);
++
++    Some(match rel {
++        Rel::Lt => {
++            match (lx, rx) {
++                (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, AlwaysFalse), // max < x
++                (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, AlwaysFalse), // x < min
++                _ => return None,
++            }
++        },
++        Rel::Le => {
++            match (lx, rx) {
++                (Some(l @ ExtremeExpr { which: Minimum, .. }), _) => (l, AlwaysTrue), // min <= x
++                (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, InequalityImpossible), // max <= x
++                (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, InequalityImpossible), // x <= min
++                (_, Some(r @ ExtremeExpr { which: Maximum, .. })) => (r, AlwaysTrue), // x <= max
++                _ => return None,
++            }
++        },
++        Rel::Ne | Rel::Eq => return None,
++    })
++}
++
++fn detect_extreme_expr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> {
++    use crate::types::ExtremeType::{Maximum, Minimum};
++
++    let ty = cx.tables.expr_ty(expr);
++
++    let cv = constant(cx, cx.tables, expr)?.0;
++
++    let which = match (&ty.kind, cv) {
++        (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => Minimum,
++        (&ty::Int(ity), Constant::Int(i))
++            if i == unsext(cx.tcx, i128::min_value() >> (128 - int_bits(cx.tcx, ity)), ity) =>
++        {
++            Minimum
++        },
++
++        (&ty::Bool, Constant::Bool(true)) => Maximum,
++        (&ty::Int(ity), Constant::Int(i))
++            if i == unsext(cx.tcx, i128::max_value() >> (128 - int_bits(cx.tcx, ity)), ity) =>
++        {
++            Maximum
++        },
++        (&ty::Uint(uty), Constant::Int(i)) if clip(cx.tcx, u128::max_value(), uty) == i => Maximum,
++
++        _ => return None,
++    };
++    Some(ExtremeExpr { which, expr })
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AbsurdExtremeComparisons {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        use crate::types::AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible};
++        use crate::types::ExtremeType::{Maximum, Minimum};
++
++        if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind {
++            if let Some((culprit, result)) = detect_absurd_comparison(cx, cmp.node, lhs, rhs) {
++                if !expr.span.from_expansion() {
++                    let msg = "this comparison involving the minimum or maximum element for this \
++                               type contains a case that is always true or always false";
++
++                    let conclusion = match result {
++                        AlwaysFalse => "this comparison is always false".to_owned(),
++                        AlwaysTrue => "this comparison is always true".to_owned(),
++                        InequalityImpossible => format!(
++                            "the case where the two sides are not equal never occurs, consider using `{} == {}` \
++                             instead",
++                            snippet(cx, lhs.span, "lhs"),
++                            snippet(cx, rhs.span, "rhs")
++                        ),
++                    };
++
++                    let help = format!(
++                        "because `{}` is the {} value for this type, {}",
++                        snippet(cx, culprit.expr.span, "x"),
++                        match culprit.which {
++                            Minimum => "minimum",
++                            Maximum => "maximum",
++                        },
++                        conclusion
++                    );
++
++                    span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help);
++                }
++            }
++        }
++    }
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for comparisons where the relation is always either
++    /// true or false, but where one side has been upcast so that the comparison is
++    /// necessary. Only integer types are checked.
++    ///
++    /// **Why is this bad?** An expression like `let x : u8 = ...; (x as u32) > 300`
++    /// will mistakenly imply that it is possible for `x` to be outside the range of
++    /// `u8`.
++    ///
++    /// **Known problems:**
++    /// https://github.com/rust-lang/rust-clippy/issues/886
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x: u8 = 1;
++    /// (x as u32) > 300;
++    /// ```
++    pub INVALID_UPCAST_COMPARISONS,
++    pedantic,
++    "a comparison involving an upcast which is always true or false"
++}
++
++declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]);
++
++#[derive(Copy, Clone, Debug, Eq)]
++enum FullInt {
++    S(i128),
++    U(u128),
++}
++
++impl FullInt {
++    #[allow(clippy::cast_sign_loss)]
++    #[must_use]
++    fn cmp_s_u(s: i128, u: u128) -> Ordering {
++        if s < 0 {
++            Ordering::Less
++        } else if u > (i128::max_value() as u128) {
++            Ordering::Greater
++        } else {
++            (s as u128).cmp(&u)
++        }
++    }
++}
++
++impl PartialEq for FullInt {
++    #[must_use]
++    fn eq(&self, other: &Self) -> bool {
++        self.partial_cmp(other).expect("`partial_cmp` only returns `Some(_)`") == Ordering::Equal
++    }
++}
++
++impl PartialOrd for FullInt {
++    #[must_use]
++    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
++        Some(match (self, other) {
++            (&Self::S(s), &Self::S(o)) => s.cmp(&o),
++            (&Self::U(s), &Self::U(o)) => s.cmp(&o),
++            (&Self::S(s), &Self::U(o)) => Self::cmp_s_u(s, o),
++            (&Self::U(s), &Self::S(o)) => Self::cmp_s_u(o, s).reverse(),
++        })
++    }
++}
++impl Ord for FullInt {
++    #[must_use]
++    fn cmp(&self, other: &Self) -> Ordering {
++        self.partial_cmp(other)
++            .expect("`partial_cmp` for FullInt can never return `None`")
++    }
++}
++
++fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> {
++    if let ExprKind::Cast(ref cast_exp, _) = expr.kind {
++        let pre_cast_ty = cx.tables.expr_ty(cast_exp);
++        let cast_ty = cx.tables.expr_ty(expr);
++        // if it's a cast from i32 to u32 wrapping will invalidate all these checks
++        if cx.layout_of(pre_cast_ty).ok().map(|l| l.size) == cx.layout_of(cast_ty).ok().map(|l| l.size) {
++            return None;
++        }
++        match pre_cast_ty.kind {
++            ty::Int(int_ty) => Some(match int_ty {
++                IntTy::I8 => (
++                    FullInt::S(i128::from(i8::min_value())),
++                    FullInt::S(i128::from(i8::max_value())),
++                ),
++                IntTy::I16 => (
++                    FullInt::S(i128::from(i16::min_value())),
++                    FullInt::S(i128::from(i16::max_value())),
++                ),
++                IntTy::I32 => (
++                    FullInt::S(i128::from(i32::min_value())),
++                    FullInt::S(i128::from(i32::max_value())),
++                ),
++                IntTy::I64 => (
++                    FullInt::S(i128::from(i64::min_value())),
++                    FullInt::S(i128::from(i64::max_value())),
++                ),
++                IntTy::I128 => (FullInt::S(i128::min_value()), FullInt::S(i128::max_value())),
++                IntTy::Isize => (
++                    FullInt::S(isize::min_value() as i128),
++                    FullInt::S(isize::max_value() as i128),
++                ),
++            }),
++            ty::Uint(uint_ty) => Some(match uint_ty {
++                UintTy::U8 => (
++                    FullInt::U(u128::from(u8::min_value())),
++                    FullInt::U(u128::from(u8::max_value())),
++                ),
++                UintTy::U16 => (
++                    FullInt::U(u128::from(u16::min_value())),
++                    FullInt::U(u128::from(u16::max_value())),
++                ),
++                UintTy::U32 => (
++                    FullInt::U(u128::from(u32::min_value())),
++                    FullInt::U(u128::from(u32::max_value())),
++                ),
++                UintTy::U64 => (
++                    FullInt::U(u128::from(u64::min_value())),
++                    FullInt::U(u128::from(u64::max_value())),
++                ),
++                UintTy::U128 => (FullInt::U(u128::min_value()), FullInt::U(u128::max_value())),
++                UintTy::Usize => (
++                    FullInt::U(usize::min_value() as u128),
++                    FullInt::U(usize::max_value() as u128),
++                ),
++            }),
++            _ => None,
++        }
++    } else {
++        None
++    }
++}
++
++fn node_as_const_fullint<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) -> Option<FullInt> {
++    let val = constant(cx, cx.tables, expr)?.0;
++    if let Constant::Int(const_int) = val {
++        match cx.tables.expr_ty(expr).kind {
++            ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))),
++            ty::Uint(_) => Some(FullInt::U(const_int)),
++            _ => None,
++        }
++    } else {
++        None
++    }
++}
++
++fn err_upcast_comparison(cx: &LateContext<'_, '_>, span: Span, expr: &Expr<'_>, always: bool) {
++    if let ExprKind::Cast(ref cast_val, _) = expr.kind {
++        span_lint(
++            cx,
++            INVALID_UPCAST_COMPARISONS,
++            span,
++            &format!(
++                "because of the numeric bounds on `{}` prior to casting, this expression is always {}",
++                snippet(cx, cast_val.span, "the expression"),
++                if always { "true" } else { "false" },
++            ),
++        );
++    }
++}
++
++fn upcast_comparison_bounds_err<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    span: Span,
++    rel: comparisons::Rel,
++    lhs_bounds: Option<(FullInt, FullInt)>,
++    lhs: &'tcx Expr<'_>,
++    rhs: &'tcx Expr<'_>,
++    invert: bool,
++) {
++    use crate::utils::comparisons::Rel;
++
++    if let Some((lb, ub)) = lhs_bounds {
++        if let Some(norm_rhs_val) = node_as_const_fullint(cx, rhs) {
++            if rel == Rel::Eq || rel == Rel::Ne {
++                if norm_rhs_val < lb || norm_rhs_val > ub {
++                    err_upcast_comparison(cx, span, lhs, rel == Rel::Ne);
++                }
++            } else if match rel {
++                Rel::Lt => {
++                    if invert {
++                        norm_rhs_val < lb
++                    } else {
++                        ub < norm_rhs_val
++                    }
++                },
++                Rel::Le => {
++                    if invert {
++                        norm_rhs_val <= lb
++                    } else {
++                        ub <= norm_rhs_val
++                    }
++                },
++                Rel::Eq | Rel::Ne => unreachable!(),
++            } {
++                err_upcast_comparison(cx, span, lhs, true)
++            } else if match rel {
++                Rel::Lt => {
++                    if invert {
++                        norm_rhs_val >= ub
++                    } else {
++                        lb >= norm_rhs_val
++                    }
++                },
++                Rel::Le => {
++                    if invert {
++                        norm_rhs_val > ub
++                    } else {
++                        lb > norm_rhs_val
++                    }
++                },
++                Rel::Eq | Rel::Ne => unreachable!(),
++            } {
++                err_upcast_comparison(cx, span, lhs, false)
++            }
++        }
++    }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidUpcastComparisons {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind {
++            let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs);
++            let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized {
++                val
++            } else {
++                return;
++            };
++
++            let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs);
++            let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs);
++
++            upcast_comparison_bounds_err(cx, expr.span, rel, lhs_bounds, normalized_lhs, normalized_rhs, false);
++            upcast_comparison_bounds_err(cx, expr.span, rel, rhs_bounds, normalized_rhs, normalized_lhs, true);
++        }
++    }
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for public `impl` or `fn` missing generalization
++    /// over different hashers and implicitly defaulting to the default hashing
++    /// algorithm (`SipHash`).
++    ///
++    /// **Why is this bad?** `HashMap` or `HashSet` with custom hashers cannot be
++    /// used with them.
++    ///
++    /// **Known problems:** Suggestions for replacing constructors can contain
++    /// false-positives. Also applying suggestions can require modification of other
++    /// pieces of code, possibly including external crates.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # use std::collections::HashMap;
++    /// # use std::hash::{Hash, BuildHasher};
++    /// # trait Serialize {};
++    /// impl<K: Hash + Eq, V> Serialize for HashMap<K, V> { }
++    ///
++    /// pub fn foo(map: &mut HashMap<i32, i32>) { }
++    /// ```
++    /// could be rewritten as
++    /// ```rust
++    /// # use std::collections::HashMap;
++    /// # use std::hash::{Hash, BuildHasher};
++    /// # trait Serialize {};
++    /// impl<K: Hash + Eq, V, S: BuildHasher> Serialize for HashMap<K, V, S> { }
++    ///
++    /// pub fn foo<S: BuildHasher>(map: &mut HashMap<i32, i32, S>) { }
++    /// ```
++    pub IMPLICIT_HASHER,
++    pedantic,
++    "missing generalization over different hashers"
++}
++
++declare_lint_pass!(ImplicitHasher => [IMPLICIT_HASHER]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitHasher {
++    #[allow(clippy::cast_possible_truncation, clippy::too_many_lines)]
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++        use rustc_span::BytePos;
++
++        fn suggestion<'a, 'tcx>(
++            cx: &LateContext<'a, 'tcx>,
++            diag: &mut DiagnosticBuilder<'_>,
++            generics_span: Span,
++            generics_suggestion_span: Span,
++            target: &ImplicitHasherType<'_>,
++            vis: ImplicitHasherConstructorVisitor<'_, '_, '_>,
++        ) {
++            let generics_snip = snippet(cx, generics_span, "");
++            // trim `<` `>`
++            let generics_snip = if generics_snip.is_empty() {
++                ""
++            } else {
++                &generics_snip[1..generics_snip.len() - 1]
++            };
++
++            multispan_sugg(
++                diag,
++                "consider adding a type parameter".to_string(),
++                vec![
++                    (
++                        generics_suggestion_span,
++                        format!(
++                            "<{}{}S: ::std::hash::BuildHasher{}>",
++                            generics_snip,
++                            if generics_snip.is_empty() { "" } else { ", " },
++                            if vis.suggestions.is_empty() {
++                                ""
++                            } else {
++                                // request users to add `Default` bound so that generic constructors can be used
++                                " + Default"
++                            },
++                        ),
++                    ),
++                    (
++                        target.span(),
++                        format!("{}<{}, S>", target.type_name(), target.type_arguments(),),
++                    ),
++                ],
++            );
++
++            if !vis.suggestions.is_empty() {
++                multispan_sugg(diag, "...and use generic constructor".into(), vis.suggestions);
++            }
++        }
++
++        if !cx.access_levels.is_exported(item.hir_id) {
++            return;
++        }
++
++        match item.kind {
++            ItemKind::Impl {
++                ref generics,
++                self_ty: ref ty,
++                ref items,
++                ..
++            } => {
++                let mut vis = ImplicitHasherTypeVisitor::new(cx);
++                vis.visit_ty(ty);
++
++                for target in &vis.found {
++                    if differing_macro_contexts(item.span, target.span()) {
++                        return;
++                    }
++
++                    let generics_suggestion_span = generics.span.substitute_dummy({
++                        let pos = snippet_opt(cx, item.span.until(target.span()))
++                            .and_then(|snip| Some(item.span.lo() + BytePos(snip.find("impl")? as u32 + 4)));
++                        if let Some(pos) = pos {
++                            Span::new(pos, pos, item.span.data().ctxt)
++                        } else {
++                            return;
++                        }
++                    });
++
++                    let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
++                    for item in items.iter().map(|item| cx.tcx.hir().impl_item(item.id)) {
++                        ctr_vis.visit_impl_item(item);
++                    }
++
++                    span_lint_and_then(
++                        cx,
++                        IMPLICIT_HASHER,
++                        target.span(),
++                        &format!(
++                            "impl for `{}` should be generalized over different hashers",
++                            target.type_name()
++                        ),
++                        move |diag| {
++                            suggestion(cx, diag, generics.span, generics_suggestion_span, target, ctr_vis);
++                        },
++                    );
++                }
++            },
++            ItemKind::Fn(ref sig, ref generics, body_id) => {
++                let body = cx.tcx.hir().body(body_id);
++
++                for ty in sig.decl.inputs {
++                    let mut vis = ImplicitHasherTypeVisitor::new(cx);
++                    vis.visit_ty(ty);
++
++                    for target in &vis.found {
++                        if in_external_macro(cx.sess(), generics.span) {
++                            continue;
++                        }
++                        let generics_suggestion_span = generics.span.substitute_dummy({
++                            let pos = snippet_opt(cx, item.span.until(body.params[0].pat.span))
++                                .and_then(|snip| {
++                                    let i = snip.find("fn")?;
++                                    Some(item.span.lo() + BytePos((i + (&snip[i..]).find('(')?) as u32))
++                                })
++                                .expect("failed to create span for type parameters");
++                            Span::new(pos, pos, item.span.data().ctxt)
++                        });
++
++                        let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
++                        ctr_vis.visit_body(body);
++
++                        span_lint_and_then(
++                            cx,
++                            IMPLICIT_HASHER,
++                            target.span(),
++                            &format!(
++                                "parameter of type `{}` should be generalized over different hashers",
++                                target.type_name()
++                            ),
++                            move |diag| {
++                                suggestion(cx, diag, generics.span, generics_suggestion_span, target, ctr_vis);
++                            },
++                        );
++                    }
++                }
++            },
++            _ => {},
++        }
++    }
++}
++
++enum ImplicitHasherType<'tcx> {
++    HashMap(Span, Ty<'tcx>, Cow<'static, str>, Cow<'static, str>),
++    HashSet(Span, Ty<'tcx>, Cow<'static, str>),
++}
++
++impl<'tcx> ImplicitHasherType<'tcx> {
++    /// Checks that `ty` is a target type without a `BuildHasher`.
++    fn new<'a>(cx: &LateContext<'a, 'tcx>, hir_ty: &hir::Ty<'_>) -> Option<Self> {
++        if let TyKind::Path(QPath::Resolved(None, ref path)) = hir_ty.kind {
++            let params: Vec<_> = path
++                .segments
++                .last()
++                .as_ref()?
++                .args
++                .as_ref()?
++                .args
++                .iter()
++                .filter_map(|arg| match arg {
++                    GenericArg::Type(ty) => Some(ty),
++                    _ => None,
++                })
++                .collect();
++            let params_len = params.len();
++
++            let ty = hir_ty_to_ty(cx.tcx, hir_ty);
++
++            if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) && params_len == 2 {
++                Some(ImplicitHasherType::HashMap(
++                    hir_ty.span,
++                    ty,
++                    snippet(cx, params[0].span, "K"),
++                    snippet(cx, params[1].span, "V"),
++                ))
++            } else if is_type_diagnostic_item(cx, ty, sym!(hashset_type)) && params_len == 1 {
++                Some(ImplicitHasherType::HashSet(
++                    hir_ty.span,
++                    ty,
++                    snippet(cx, params[0].span, "T"),
++                ))
++            } else {
++                None
++            }
++        } else {
++            None
++        }
++    }
++
++    fn type_name(&self) -> &'static str {
++        match *self {
++            ImplicitHasherType::HashMap(..) => "HashMap",
++            ImplicitHasherType::HashSet(..) => "HashSet",
++        }
++    }
++
++    fn type_arguments(&self) -> String {
++        match *self {
++            ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{}, {}", k, v),
++            ImplicitHasherType::HashSet(.., ref t) => format!("{}", t),
++        }
++    }
++
++    fn ty(&self) -> Ty<'tcx> {
++        match *self {
++            ImplicitHasherType::HashMap(_, ty, ..) | ImplicitHasherType::HashSet(_, ty, ..) => ty,
++        }
++    }
++
++    fn span(&self) -> Span {
++        match *self {
++            ImplicitHasherType::HashMap(span, ..) | ImplicitHasherType::HashSet(span, ..) => span,
++        }
++    }
++}
++
++struct ImplicitHasherTypeVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    found: Vec<ImplicitHasherType<'tcx>>,
++}
++
++impl<'a, 'tcx> ImplicitHasherTypeVisitor<'a, 'tcx> {
++    fn new(cx: &'a LateContext<'a, 'tcx>) -> Self {
++        Self { cx, found: vec![] }
++    }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_ty(&mut self, t: &'tcx hir::Ty<'_>) {
++        if let Some(target) = ImplicitHasherType::new(self.cx, t) {
++            self.found.push(target);
++        }
++
++        walk_ty(self, t);
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++/// Looks for default-hasher-dependent constructors like `HashMap::new`.
++struct ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    body: &'a TypeckTables<'tcx>,
++    target: &'b ImplicitHasherType<'tcx>,
++    suggestions: BTreeMap<Span, String>,
++}
++
++impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
++    fn new(cx: &'a LateContext<'a, 'tcx>, target: &'b ImplicitHasherType<'tcx>) -> Self {
++        Self {
++            cx,
++            body: cx.tables,
++            target,
++            suggestions: BTreeMap::new(),
++        }
++    }
++}
++
++impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_body(&mut self, body: &'tcx Body<'_>) {
++        let prev_body = self.body;
++        self.body = self.cx.tcx.body_tables(body.id());
++        walk_body(self, body);
++        self.body = prev_body;
++    }
++
++    fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
++        if_chain! {
++            if let ExprKind::Call(ref fun, ref args) = e.kind;
++            if let ExprKind::Path(QPath::TypeRelative(ref ty, ref method)) = fun.kind;
++            if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind;
++            then {
++                if !same_tys(self.cx, self.target.ty(), self.body.expr_ty(e)) {
++                    return;
++                }
++
++                if match_path(ty_path, &paths::HASHMAP) {
++                    if method.ident.name == sym!(new) {
++                        self.suggestions
++                            .insert(e.span, "HashMap::default()".to_string());
++                    } else if method.ident.name == sym!(with_capacity) {
++                        self.suggestions.insert(
++                            e.span,
++                            format!(
++                                "HashMap::with_capacity_and_hasher({}, Default::default())",
++                                snippet(self.cx, args[0].span, "capacity"),
++                            ),
++                        );
++                    }
++                } else if match_path(ty_path, &paths::HASHSET) {
++                    if method.ident.name == sym!(new) {
++                        self.suggestions
++                            .insert(e.span, "HashSet::default()".to_string());
++                    } else if method.ident.name == sym!(with_capacity) {
++                        self.suggestions.insert(
++                            e.span,
++                            format!(
++                                "HashSet::with_capacity_and_hasher({}, Default::default())",
++                                snippet(self.cx, args[0].span, "capacity"),
++                            ),
++                        );
++                    }
++                }
++            }
++        }
++
++        walk_expr(self, e);
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
++    }
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for casts of `&T` to `&mut T` anywhere in the code.
++    ///
++    /// **Why is this bad?** It’s basically guaranteed to be undefined behaviour.
++    /// `UnsafeCell` is the only way to obtain aliasable data that is considered
++    /// mutable.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// fn x(r: &i32) {
++    ///     unsafe {
++    ///         *(r as *const _ as *mut _) += 1;
++    ///     }
++    /// }
++    /// ```
++    ///
++    /// Instead consider using interior mutability types.
++    ///
++    /// ```rust
++    /// use std::cell::UnsafeCell;
++    ///
++    /// fn x(r: &UnsafeCell<i32>) {
++    ///     unsafe {
++    ///         *r.get() += 1;
++    ///     }
++    /// }
++    /// ```
++    pub CAST_REF_TO_MUT,
++    correctness,
++    "a cast of reference to a mutable pointer"
++}
++
++declare_lint_pass!(RefToMut => [CAST_REF_TO_MUT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RefToMut {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            if let ExprKind::Unary(UnOp::UnDeref, e) = &expr.kind;
++            if let ExprKind::Cast(e, t) = &e.kind;
++            if let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind;
++            if let ExprKind::Cast(e, t) = &e.kind;
++            if let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind;
++            if let ty::Ref(..) = cx.tables.node_type(e.hir_id).kind;
++            then {
++                span_lint(
++                    cx,
++                    CAST_REF_TO_MUT,
++                    expr.span,
++                    "casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`",
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d073c197656c61b5cac6e0d557f3ad3ae19ddd58
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,131 @@@
++use crate::utils::{is_allowed, snippet, span_lint_and_sugg};
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, HirId};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++use unicode_normalization::UnicodeNormalization;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for the Unicode zero-width space in the code.
++    ///
++    /// **Why is this bad?** Having an invisible character in the code makes for all
++    /// sorts of April fools, but otherwise is very much frowned upon.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:** You don't see it, but there may be a zero-width space
++    /// somewhere in this text.
++    pub ZERO_WIDTH_SPACE,
++    correctness,
++    "using a zero-width space in a string literal, which is confusing"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for non-ASCII characters in string literals.
++    ///
++    /// **Why is this bad?** Yeah, we know, the 90's called and wanted their charset
++    /// back. Even so, there still are editors and other programs out there that
++    /// don't work well with Unicode. So if the code is meant to be used
++    /// internationally, on multiple operating systems, or has other portability
++    /// requirements, activating this lint could be useful.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// let x = String::from("€");
++    /// ```
++    /// Could be written as:
++    /// ```rust
++    /// let x = String::from("\u{20ac}");
++    /// ```
++    pub NON_ASCII_LITERAL,
++    pedantic,
++    "using any literal non-ASCII chars in a string literal instead of using the `\\u` escape"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for string literals that contain Unicode in a form
++    /// that is not equal to its
++    /// [NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms).
++    ///
++    /// **Why is this bad?** If such a string is compared to another, the results
++    /// may be surprising.
++    ///
++    /// **Known problems** None.
++    ///
++    /// **Example:** You may not see it, but "à"" and "à"" aren't the same string. The
++    /// former when escaped is actually `"a\u{300}"` while the latter is `"\u{e0}"`.
++    pub UNICODE_NOT_NFC,
++    pedantic,
++    "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)"
++}
++
++declare_lint_pass!(Unicode => [ZERO_WIDTH_SPACE, NON_ASCII_LITERAL, UNICODE_NOT_NFC]);
++
++impl LateLintPass<'_, '_> for Unicode {
++    fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &'_ Expr<'_>) {
++        if let ExprKind::Lit(ref lit) = expr.kind {
++            if let LitKind::Str(_, _) = lit.node {
++                check_str(cx, lit.span, expr.hir_id)
++            }
++        }
++    }
++}
++
++fn escape<T: Iterator<Item = char>>(s: T) -> String {
++    let mut result = String::new();
++    for c in s {
++        if c as u32 > 0x7F {
++            for d in c.escape_unicode() {
++                result.push(d)
++            }
++        } else {
++            result.push(c);
++        }
++    }
++    result
++}
++
++fn check_str(cx: &LateContext<'_, '_>, span: Span, id: HirId) {
++    let string = snippet(cx, span, "");
++    if string.contains('\u{200B}') {
++        span_lint_and_sugg(
++            cx,
++            ZERO_WIDTH_SPACE,
++            span,
++            "zero-width space detected",
++            "consider replacing the string with",
++            string.replace("\u{200B}", "\\u{200B}"),
++            Applicability::MachineApplicable,
++        );
++    }
++    if string.chars().any(|c| c as u32 > 0x7F) {
++        span_lint_and_sugg(
++            cx,
++            NON_ASCII_LITERAL,
++            span,
++            "literal non-ASCII character detected",
++            "consider replacing the string with",
++            if is_allowed(cx, UNICODE_NOT_NFC, id) {
++                escape(string.chars())
++            } else {
++                escape(string.nfc())
++            },
++            Applicability::MachineApplicable,
++        );
++    }
++    if is_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
++        span_lint_and_sugg(
++            cx,
++            UNICODE_NOT_NFC,
++            span,
++            "non-NFC Unicode sequence detected",
++            "consider replacing the string with",
++            string.nfc().collect::<String>(),
++            Applicability::MachineApplicable,
++        );
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4e077b95b5c68024c9eaca1f78c343c62a2dfe9a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,135 @@@
++use crate::utils::{match_def_path, paths, span_lint, span_lint_and_help};
++use if_chain::if_chain;
++use rustc_hir::{BinOpKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for comparisons with an address of a function item.
++    ///
++    /// **Why is this bad?** Function item address is not guaranteed to be unique and could vary
++    /// between different code generation units. Furthermore different function items could have
++    /// the same address after being merged together.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// type F = fn();
++    /// fn a() {}
++    /// let f: F = a;
++    /// if f == a {
++    ///     // ...
++    /// }
++    /// ```
++    pub FN_ADDRESS_COMPARISONS,
++    correctness,
++    "comparison with an address of a function item"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for comparisons with an address of a trait vtable.
++    ///
++    /// **Why is this bad?** Comparing trait objects pointers compares an vtable addresses which
++    /// are not guaranteed to be unique and could vary between different code generation units.
++    /// Furthermore vtables for different types could have the same address after being merged
++    /// together.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust,ignore
++    /// let a: Rc<dyn Trait> = ...
++    /// let b: Rc<dyn Trait> = ...
++    /// if Rc::ptr_eq(&a, &b) {
++    ///     ...
++    /// }
++    /// ```
++    pub VTABLE_ADDRESS_COMPARISONS,
++    correctness,
++    "comparison with an address of a trait vtable"
++}
++
++declare_lint_pass!(UnnamedAddress => [FN_ADDRESS_COMPARISONS, VTABLE_ADDRESS_COMPARISONS]);
++
++impl LateLintPass<'_, '_> for UnnamedAddress {
++    fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
++        fn is_comparison(binop: BinOpKind) -> bool {
++            match binop {
++                BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt => true,
++                _ => false,
++            }
++        }
++
++        fn is_trait_ptr(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++            match cx.tables.expr_ty_adjusted(expr).kind {
++                ty::RawPtr(ty::TypeAndMut { ty, .. }) => ty.is_trait(),
++                _ => false,
++            }
++        }
++
++        fn is_fn_def(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++            if let ty::FnDef(..) = cx.tables.expr_ty(expr).kind {
++                true
++            } else {
++                false
++            }
++        }
++
++        if_chain! {
++            if let ExprKind::Binary(binop, ref left, ref right) = expr.kind;
++            if is_comparison(binop.node);
++            if is_trait_ptr(cx, left) && is_trait_ptr(cx, right);
++            then {
++                span_lint_and_help(
++                    cx,
++                    VTABLE_ADDRESS_COMPARISONS,
++                    expr.span,
++                    "comparing trait object pointers compares a non-unique vtable address",
++                    None,
++                    "consider extracting and comparing data pointers only",
++                );
++            }
++        }
++
++        if_chain! {
++            if let ExprKind::Call(ref func, [ref _left, ref _right]) = expr.kind;
++            if let ExprKind::Path(ref func_qpath) = func.kind;
++            if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id();
++            if match_def_path(cx, def_id, &paths::PTR_EQ) ||
++                match_def_path(cx, def_id, &paths::RC_PTR_EQ) ||
++                match_def_path(cx, def_id, &paths::ARC_PTR_EQ);
++            let ty_param = cx.tables.node_substs(func.hir_id).type_at(0);
++            if ty_param.is_trait();
++            then {
++                span_lint_and_help(
++                    cx,
++                    VTABLE_ADDRESS_COMPARISONS,
++                    expr.span,
++                    "comparing trait object pointers compares a non-unique vtable address",
++                    None,
++                    "consider extracting and comparing data pointers only",
++                );
++            }
++        }
++
++        if_chain! {
++            if let ExprKind::Binary(binop, ref left, ref right) = expr.kind;
++            if is_comparison(binop.node);
++            if cx.tables.expr_ty_adjusted(left).is_fn_ptr() &&
++                cx.tables.expr_ty_adjusted(right).is_fn_ptr();
++            if is_fn_def(cx, left) || is_fn_def(cx, right);
++            then {
++                span_lint(
++                    cx,
++                    FN_ADDRESS_COMPARISONS,
++                    expr.span,
++                    "comparing with a non-unique address of a function item",
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..86c469a4dccf7377b62b916eaadd375f19f13c8c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,78 @@@
++use crate::utils::span_lint;
++use rustc_ast::ast::{Ident, Item, ItemKind, UseTree, UseTreeKind};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++use rustc_span::symbol::SymbolStr;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for imports that remove "unsafe" from an item's
++    /// name.
++    ///
++    /// **Why is this bad?** Renaming makes it less clear which traits and
++    /// structures are unsafe.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// use std::cell::{UnsafeCell as TotallySafeCell};
++    ///
++    /// extern crate crossbeam;
++    /// use crossbeam::{spawn_unsafe as spawn};
++    /// ```
++    pub UNSAFE_REMOVED_FROM_NAME,
++    style,
++    "`unsafe` removed from API names on import"
++}
++
++declare_lint_pass!(UnsafeNameRemoval => [UNSAFE_REMOVED_FROM_NAME]);
++
++impl EarlyLintPass for UnsafeNameRemoval {
++    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
++        if let ItemKind::Use(ref use_tree) = item.kind {
++            check_use_tree(use_tree, cx, item.span);
++        }
++    }
++}
++
++fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) {
++    match use_tree.kind {
++        UseTreeKind::Simple(Some(new_name), ..) => {
++            let old_name = use_tree
++                .prefix
++                .segments
++                .last()
++                .expect("use paths cannot be empty")
++                .ident;
++            unsafe_to_safe_check(old_name, new_name, cx, span);
++        },
++        UseTreeKind::Simple(None, ..) | UseTreeKind::Glob => {},
++        UseTreeKind::Nested(ref nested_use_tree) => {
++            for &(ref use_tree, _) in nested_use_tree {
++                check_use_tree(use_tree, cx, span);
++            }
++        },
++    }
++}
++
++fn unsafe_to_safe_check(old_name: Ident, new_name: Ident, cx: &EarlyContext<'_>, span: Span) {
++    let old_str = old_name.name.as_str();
++    let new_str = new_name.name.as_str();
++    if contains_unsafe(&old_str) && !contains_unsafe(&new_str) {
++        span_lint(
++            cx,
++            UNSAFE_REMOVED_FROM_NAME,
++            span,
++            &format!(
++                "removed `unsafe` from the name of `{}` in use as `{}`",
++                old_str, new_str
++            ),
++        );
++    }
++}
++
++#[must_use]
++fn contains_unsafe(name: &SymbolStr) -> bool {
++    name.contains("Unsafe") || name.contains("unsafe")
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b85134e3d7a9aaa7a8f565f672408f2e3133061f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,91 @@@
++use crate::utils::{is_try, match_qpath, match_trait_method, paths, span_lint};
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for unused written/read amount.
++    ///
++    /// **Why is this bad?** `io::Write::write(_vectored)` and
++    /// `io::Read::read(_vectored)` are not guaranteed to
++    /// process the entire buffer. They return how many bytes were processed, which
++    /// might be smaller
++    /// than a given buffer's length. If you don't need to deal with
++    /// partial-write/read, use
++    /// `write_all`/`read_exact` instead.
++    ///
++    /// **Known problems:** Detects only common patterns.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// use std::io;
++    /// fn foo<W: io::Write>(w: &mut W) -> io::Result<()> {
++    ///     // must be `w.write_all(b"foo")?;`
++    ///     w.write(b"foo")?;
++    ///     Ok(())
++    /// }
++    /// ```
++    pub UNUSED_IO_AMOUNT,
++    correctness,
++    "unused written/read amount"
++}
++
++declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedIoAmount {
++    fn check_stmt(&mut self, cx: &LateContext<'_, '_>, s: &hir::Stmt<'_>) {
++        let expr = match s.kind {
++            hir::StmtKind::Semi(ref expr) | hir::StmtKind::Expr(ref expr) => &**expr,
++            _ => return,
++        };
++
++        match expr.kind {
++            hir::ExprKind::Match(ref res, _, _) if is_try(expr).is_some() => {
++                if let hir::ExprKind::Call(ref func, ref args) = res.kind {
++                    if let hir::ExprKind::Path(ref path) = func.kind {
++                        if match_qpath(path, &paths::TRY_INTO_RESULT) && args.len() == 1 {
++                            check_method_call(cx, &args[0], expr);
++                        }
++                    }
++                } else {
++                    check_method_call(cx, res, expr);
++                }
++            },
++
++            hir::ExprKind::MethodCall(ref path, _, ref args) => match &*path.ident.as_str() {
++                "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => {
++                    check_method_call(cx, &args[0], expr);
++                },
++                _ => (),
++            },
++
++            _ => (),
++        }
++    }
++}
++
++fn check_method_call(cx: &LateContext<'_, '_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
++    if let hir::ExprKind::MethodCall(ref path, _, _) = call.kind {
++        let symbol = &*path.ident.as_str();
++        let read_trait = match_trait_method(cx, call, &paths::IO_READ);
++        let write_trait = match_trait_method(cx, call, &paths::IO_WRITE);
++
++        match (read_trait, write_trait, symbol) {
++            (true, _, "read") => span_lint(
++                cx,
++                UNUSED_IO_AMOUNT,
++                expr.span,
++                "read amount is not handled. Use `Read::read_exact` instead",
++            ),
++            (true, _, "read_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"),
++            (_, true, "write") => span_lint(
++                cx,
++                UNUSED_IO_AMOUNT,
++                expr.span,
++                "written amount is not handled. Use `Write::write_all` instead",
++            ),
++            (_, true, "write_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"),
++            _ => (),
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3d5e2f9fd21556f33562794350a22915d1983d2c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,105 @@@
++use if_chain::if_chain;
++use rustc_hir::def::Res;
++use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor};
++use rustc_hir::{HirId, ImplItem, ImplItemKind, ItemKind, Path};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use crate::utils::span_lint_and_help;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks methods that contain a `self` argument but don't use it
++    ///
++    /// **Why is this bad?** It may be clearer to define the method as an associated function instead
++    /// of an instance method if it doesn't require `self`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// struct A;
++    /// impl A {
++    ///     fn method(&self) {}
++    /// }
++    /// ```
++    ///
++    /// Could be written:
++    ///
++    /// ```rust,ignore
++    /// struct A;
++    /// impl A {
++    ///     fn method() {}
++    /// }
++    /// ```
++    pub UNUSED_SELF,
++    pedantic,
++    "methods that contain a `self` argument but don't use it"
++}
++
++declare_lint_pass!(UnusedSelf => [UNUSED_SELF]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedSelf {
++    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &ImplItem<'_>) {
++        if impl_item.span.from_expansion() {
++            return;
++        }
++        let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id);
++        let parent_item = cx.tcx.hir().expect_item(parent);
++        let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
++        let assoc_item = cx.tcx.associated_item(def_id);
++        if_chain! {
++            if let ItemKind::Impl { of_trait: None, .. } = parent_item.kind;
++            if assoc_item.fn_has_self_parameter;
++            if let ImplItemKind::Fn(.., body_id) = &impl_item.kind;
++            let body = cx.tcx.hir().body(*body_id);
++            if !body.params.is_empty();
++            then {
++                let self_param = &body.params[0];
++                let self_hir_id = self_param.pat.hir_id;
++                let mut visitor = UnusedSelfVisitor {
++                    cx,
++                    uses_self: false,
++                    self_hir_id: &self_hir_id,
++                };
++                visitor.visit_body(body);
++                if !visitor.uses_self {
++                    span_lint_and_help(
++                        cx,
++                        UNUSED_SELF,
++                        self_param.span,
++                        "unused `self` argument",
++                        None,
++                        "consider refactoring to a associated function",
++                    );
++                    return;
++                }
++            }
++        }
++    }
++}
++
++struct UnusedSelfVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    uses_self: bool,
++    self_hir_id: &'a HirId,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for UnusedSelfVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
++        if self.uses_self {
++            // This function already uses `self`
++            return;
++        }
++        if let Res::Local(hir_id) = &path.res {
++            self.uses_self = self.self_hir_id == hir_id
++        }
++        walk_path(self, path);
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5235c98efab13d1fa40884bb513ba02f35064fe6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,216 @@@
++use crate::utils::{higher::if_block, is_type_diagnostic_item, span_lint_and_then, 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};
++use rustc_lint::{LateContext, LateLintPass};
++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::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail.
++    ///
++    /// **Why is this bad?** Using `if let` or `match` is more idiomatic.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let option = Some(0);
++    /// # fn do_something_with(_x: usize) {}
++    /// if option.is_some() {
++    ///     do_something_with(option.unwrap())
++    /// }
++    /// ```
++    ///
++    /// Could be written:
++    ///
++    /// ```rust
++    /// # let option = Some(0);
++    /// # fn do_something_with(_x: usize) {}
++    /// if let Some(value) = option {
++    ///     do_something_with(value)
++    /// }
++    /// ```
++    pub UNNECESSARY_UNWRAP,
++    complexity,
++    "checks for calls of `unwrap[_err]()` that cannot fail"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for calls of `unwrap[_err]()` that will always fail.
++    ///
++    /// **Why is this bad?** If panicking is desired, an explicit `panic!()` should be used.
++    ///
++    /// **Known problems:** This lint only checks `if` conditions not assignments.
++    /// So something like `let x: Option<()> = None; x.unwrap();` will not be recognized.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let option = Some(0);
++    /// # fn do_something_with(_x: usize) {}
++    /// if option.is_none() {
++    ///     do_something_with(option.unwrap())
++    /// }
++    /// ```
++    ///
++    /// This code will always panic. The if condition should probably be inverted.
++    pub PANICKING_UNWRAP,
++    correctness,
++    "checks for calls of `unwrap[_err]()` that will always fail"
++}
++
++/// Visitor that keeps track of which variables are unwrappable.
++struct UnwrappableVariablesVisitor<'a, 'tcx> {
++    unwrappables: Vec<UnwrapInfo<'tcx>>,
++    cx: &'a LateContext<'a, 'tcx>,
++}
++/// Contains information about whether a variable can be unwrapped.
++#[derive(Copy, Clone, Debug)]
++struct UnwrapInfo<'tcx> {
++    /// The variable that is checked
++    ident: &'tcx Path<'tcx>,
++    /// The check, like `x.is_ok()`
++    check: &'tcx Expr<'tcx>,
++    /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`).
++    safe_to_unwrap: bool,
++}
++
++/// Collects the information about unwrappable variables from an if condition
++/// The `invert` argument tells us whether the condition is negated.
++fn collect_unwrap_info<'a, 'tcx>(
++    cx: &'a LateContext<'a, 'tcx>,
++    expr: &'tcx Expr<'_>,
++    invert: bool,
++) -> Vec<UnwrapInfo<'tcx>> {
++    if let ExprKind::Binary(op, left, right) = &expr.kind {
++        match (invert, op.node) {
++            (false, BinOpKind::And) | (false, BinOpKind::BitAnd) | (true, BinOpKind::Or) | (true, BinOpKind::BitOr) => {
++                let mut unwrap_info = collect_unwrap_info(cx, left, invert);
++                unwrap_info.append(&mut collect_unwrap_info(cx, right, invert));
++                return unwrap_info;
++            },
++            _ => (),
++        }
++    } else if let ExprKind::Unary(UnOp::UnNot, expr) = &expr.kind {
++        return collect_unwrap_info(cx, expr, !invert);
++    } else {
++        if_chain! {
++            if let ExprKind::MethodCall(method_name, _, args) = &expr.kind;
++            if let ExprKind::Path(QPath::Resolved(None, path)) = &args[0].kind;
++            let ty = cx.tables.expr_ty(&args[0]);
++            if is_type_diagnostic_item(cx, ty, sym!(option_type)) || is_type_diagnostic_item(cx, ty, sym!(result_type));
++            let name = method_name.ident.as_str();
++            if ["is_some", "is_none", "is_ok", "is_err"].contains(&&*name);
++            then {
++                assert!(args.len() == 1);
++                let unwrappable = match name.as_ref() {
++                    "is_some" | "is_ok" => true,
++                    "is_err" | "is_none" => false,
++                    _ => unreachable!(),
++                };
++                let safe_to_unwrap = unwrappable != invert;
++                return vec![UnwrapInfo { ident: path, check: expr, safe_to_unwrap }];
++            }
++        }
++    }
++    Vec::new()
++}
++
++impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> {
++    fn visit_branch(&mut self, cond: &'tcx Expr<'_>, branch: &'tcx Expr<'_>, else_branch: bool) {
++        let prev_len = self.unwrappables.len();
++        for unwrap_info in collect_unwrap_info(self.cx, cond, else_branch) {
++            if is_potentially_mutated(unwrap_info.ident, cond, self.cx)
++                || is_potentially_mutated(unwrap_info.ident, branch, self.cx)
++            {
++                // if the variable is mutated, we don't know whether it can be unwrapped:
++                continue;
++            }
++            self.unwrappables.push(unwrap_info);
++        }
++        walk_expr(self, branch);
++        self.unwrappables.truncate(prev_len);
++    }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        // Shouldn't lint when `expr` is in macro.
++        if in_external_macro(self.cx.tcx.sess, expr.span) {
++            return;
++        }
++        if let Some((cond, then, els)) = if_block(&expr) {
++            walk_expr(self, cond);
++            self.visit_branch(cond, then, false);
++            if let Some(els) = els {
++                self.visit_branch(cond, els, true);
++            }
++        } else {
++            // find `unwrap[_err]()` calls:
++            if_chain! {
++                if let ExprKind::MethodCall(ref method_name, _, ref args) = expr.kind;
++                if let ExprKind::Path(QPath::Resolved(None, ref path)) = args[0].kind;
++                if [sym!(unwrap), sym!(unwrap_err)].contains(&method_name.ident.name);
++                let call_to_unwrap = method_name.ident.name == sym!(unwrap);
++                if let Some(unwrappable) = self.unwrappables.iter()
++                    .find(|u| u.ident.res == path.res);
++                then {
++                    if call_to_unwrap == unwrappable.safe_to_unwrap {
++                        span_lint_and_then(
++                            self.cx,
++                            UNNECESSARY_UNWRAP,
++                            expr.span,
++                            &format!("You checked before that `{}()` cannot fail. \
++                            Instead of checking and unwrapping, it's better to use `if let` or `match`.",
++                            method_name.ident.name),
++                            |diag| { diag.span_label(unwrappable.check.span, "the check is happening here"); },
++                        );
++                    } else {
++                        span_lint_and_then(
++                            self.cx,
++                            PANICKING_UNWRAP,
++                            expr.span,
++                            &format!("This call to `{}()` will always panic.",
++                            method_name.ident.name),
++                            |diag| { diag.span_label(unwrappable.check.span, "because of this check"); },
++                        );
++                    }
++                }
++            }
++            walk_expr(self, expr);
++        }
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
++    }
++}
++
++declare_lint_pass!(Unwrap => [PANICKING_UNWRAP, UNNECESSARY_UNWRAP]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Unwrap {
++    fn check_fn(
++        &mut self,
++        cx: &LateContext<'a, 'tcx>,
++        kind: FnKind<'tcx>,
++        decl: &'tcx FnDecl<'_>,
++        body: &'tcx Body<'_>,
++        span: Span,
++        fn_id: HirId,
++    ) {
++        if span.from_expansion() {
++            return;
++        }
++
++        let mut v = UnwrappableVariablesVisitor {
++            cx,
++            unwrappables: Vec::new(),
++        };
++
++        walk_fn(&mut v, kind, decl, body.id(), span, fn_id);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f8e1aff33e7731f5e887c6a2fa5958b2b7664076
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,272 @@@
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::intravisit::{walk_item, walk_path, walk_ty, NestedVisitorMap, Visitor};
++use rustc_hir::{
++    def, FnDecl, FnRetTy, FnSig, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Path, PathSegment, QPath,
++    TyKind,
++};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty;
++use rustc_middle::ty::{DefIdTree, Ty};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::symbol::kw;
++use rustc_typeck::hir_ty_to_ty;
++
++use crate::utils::{differing_macro_contexts, span_lint_and_sugg};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for unnecessary repetition of structure name when a
++    /// replacement with `Self` is applicable.
++    ///
++    /// **Why is this bad?** Unnecessary repetition. Mixed use of `Self` and struct
++    /// name
++    /// feels inconsistent.
++    ///
++    /// **Known problems:**
++    /// - False positive when using associated types (#2843)
++    /// - False positives in some situations when using generics (#3410)
++    ///
++    /// **Example:**
++    /// ```rust
++    /// struct Foo {}
++    /// impl Foo {
++    ///     fn new() -> Foo {
++    ///         Foo {}
++    ///     }
++    /// }
++    /// ```
++    /// could be
++    /// ```rust
++    /// struct Foo {}
++    /// impl Foo {
++    ///     fn new() -> Self {
++    ///         Self {}
++    ///     }
++    /// }
++    /// ```
++    pub USE_SELF,
++    nursery,
++    "Unnecessary structure name repetition whereas `Self` is applicable"
++}
++
++declare_lint_pass!(UseSelf => [USE_SELF]);
++
++const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
++
++fn span_use_self_lint(cx: &LateContext<'_, '_>, path: &Path<'_>, last_segment: Option<&PathSegment<'_>>) {
++    let last_segment = last_segment.unwrap_or_else(|| path.segments.last().expect(SEGMENTS_MSG));
++
++    // Path segments only include actual path, no methods or fields.
++    let last_path_span = last_segment.ident.span;
++
++    if differing_macro_contexts(path.span, last_path_span) {
++        return;
++    }
++
++    // Only take path up to the end of last_path_span.
++    let span = path.span.with_hi(last_path_span.hi());
++
++    span_lint_and_sugg(
++        cx,
++        USE_SELF,
++        span,
++        "unnecessary structure name repetition",
++        "use the applicable keyword",
++        "Self".to_owned(),
++        Applicability::MachineApplicable,
++    );
++}
++
++// FIXME: always use this (more correct) visitor, not just in method signatures.
++struct SemanticUseSelfVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    self_ty: Ty<'tcx>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for SemanticUseSelfVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'_>) {
++        if let TyKind::Path(QPath::Resolved(_, path)) = &hir_ty.kind {
++            match path.res {
++                def::Res::SelfTy(..) => {},
++                _ => {
++                    if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty {
++                        span_use_self_lint(self.cx, path, None);
++                    }
++                },
++            }
++        }
++
++        walk_ty(self, hir_ty)
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++fn check_trait_method_impl_decl<'a, 'tcx>(
++    cx: &'a LateContext<'a, 'tcx>,
++    impl_item: &ImplItem<'_>,
++    impl_decl: &'tcx FnDecl<'_>,
++    impl_trait_ref: ty::TraitRef<'tcx>,
++) {
++    let trait_method = cx
++        .tcx
++        .associated_items(impl_trait_ref.def_id)
++        .find_by_name_and_kind(cx.tcx, impl_item.ident, ty::AssocKind::Fn, impl_trait_ref.def_id)
++        .expect("impl method matches a trait method");
++
++    let trait_method_sig = cx.tcx.fn_sig(trait_method.def_id);
++    let trait_method_sig = cx.tcx.erase_late_bound_regions(&trait_method_sig);
++
++    let output_hir_ty = if let FnRetTy::Return(ty) = &impl_decl.output {
++        Some(&**ty)
++    } else {
++        None
++    };
++
++    // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
++    // `trait_ty` (of type `ty::Ty`) is the semantic type for the signature in the trait.
++    // We use `impl_hir_ty` to see if the type was written as `Self`,
++    // `hir_ty_to_ty(...)` to check semantic types of paths, and
++    // `trait_ty` to determine which parts of the signature in the trait, mention
++    // the type being implemented verbatim (as opposed to `Self`).
++    for (impl_hir_ty, trait_ty) in impl_decl
++        .inputs
++        .iter()
++        .chain(output_hir_ty)
++        .zip(trait_method_sig.inputs_and_output)
++    {
++        // Check if the input/output type in the trait method specifies the implemented
++        // type verbatim, and only suggest `Self` if that isn't the case.
++        // This avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`,
++        // in an `impl Trait for u8`, when the trait always uses `Vec<u8>`.
++        // See also https://github.com/rust-lang/rust-clippy/issues/2894.
++        let self_ty = impl_trait_ref.self_ty();
++        if !trait_ty.walk().any(|inner| inner == self_ty.into()) {
++            let mut visitor = SemanticUseSelfVisitor { cx, self_ty };
++
++            visitor.visit_ty(&impl_hir_ty);
++        }
++    }
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UseSelf {
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++        if in_external_macro(cx.sess(), item.span) {
++            return;
++        }
++        if_chain! {
++            if let ItemKind::Impl{ self_ty: ref item_type, items: refs, .. } = item.kind;
++            if let TyKind::Path(QPath::Resolved(_, ref item_path)) = item_type.kind;
++            then {
++                let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
++                let should_check = if let Some(ref params) = *parameters {
++                    !params.parenthesized && !params.args.iter().any(|arg| match arg {
++                        GenericArg::Lifetime(_) => true,
++                        _ => false,
++                    })
++                } else {
++                    true
++                };
++
++                if should_check {
++                    let visitor = &mut UseSelfVisitor {
++                        item_path,
++                        cx,
++                    };
++                    let impl_def_id = cx.tcx.hir().local_def_id(item.hir_id);
++                    let impl_trait_ref = cx.tcx.impl_trait_ref(impl_def_id);
++
++                    if let Some(impl_trait_ref) = impl_trait_ref {
++                        for impl_item_ref in refs {
++                            let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
++                            if let ImplItemKind::Fn(FnSig{ decl: impl_decl, .. }, impl_body_id)
++                                    = &impl_item.kind {
++                                check_trait_method_impl_decl(cx, impl_item, impl_decl, impl_trait_ref);
++
++                                let body = cx.tcx.hir().body(*impl_body_id);
++                                visitor.visit_body(body);
++                            } else {
++                                visitor.visit_impl_item(impl_item);
++                            }
++                        }
++                    } else {
++                        for impl_item_ref in refs {
++                            let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
++                            visitor.visit_impl_item(impl_item);
++                        }
++                    }
++                }
++            }
++        }
++    }
++}
++
++struct UseSelfVisitor<'a, 'tcx> {
++    item_path: &'a Path<'a>,
++    cx: &'a LateContext<'a, 'tcx>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for UseSelfVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
++        if !path.segments.iter().any(|p| p.ident.span.is_dummy()) {
++            if path.segments.len() >= 2 {
++                let last_but_one = &path.segments[path.segments.len() - 2];
++                if last_but_one.ident.name != kw::SelfUpper {
++                    let enum_def_id = match path.res {
++                        Res::Def(DefKind::Variant, variant_def_id) => self.cx.tcx.parent(variant_def_id),
++                        Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), ctor_def_id) => {
++                            let variant_def_id = self.cx.tcx.parent(ctor_def_id);
++                            variant_def_id.and_then(|def_id| self.cx.tcx.parent(def_id))
++                        },
++                        _ => None,
++                    };
++
++                    if self.item_path.res.opt_def_id() == enum_def_id {
++                        span_use_self_lint(self.cx, path, Some(last_but_one));
++                    }
++                }
++            }
++
++            if path.segments.last().expect(SEGMENTS_MSG).ident.name != kw::SelfUpper {
++                if self.item_path.res == path.res {
++                    span_use_self_lint(self.cx, path, None);
++                } else if let Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), ctor_def_id) = path.res {
++                    if self.item_path.res.opt_def_id() == self.cx.tcx.parent(ctor_def_id) {
++                        span_use_self_lint(self.cx, path, None);
++                    }
++                }
++            }
++        }
++
++        walk_path(self, path);
++    }
++
++    fn visit_item(&mut self, item: &'tcx Item<'_>) {
++        match item.kind {
++            ItemKind::Use(..)
++            | ItemKind::Static(..)
++            | ItemKind::Enum(..)
++            | ItemKind::Struct(..)
++            | ItemKind::Union(..)
++            | ItemKind::Impl { .. }
++            | ItemKind::Fn(..) => {
++                // Don't check statements that shadow `Self` or where `Self` can't be used
++            },
++            _ => walk_item(self, item),
++        }
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::All(self.cx.tcx.hir())
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4dcf6c105ec63666a7136c876b9b04d901cfbf52
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,128 @@@
++use rustc_ast::ast;
++use rustc_ast::expand::is_proc_macro_attr;
++use rustc_errors::Applicability;
++use rustc_session::Session;
++use std::str::FromStr;
++
++/// Deprecation status of attributes known by Clippy.
++#[allow(dead_code)]
++pub enum DeprecationStatus {
++    /// Attribute is deprecated
++    Deprecated,
++    /// Attribute is deprecated and was replaced by the named attribute
++    Replaced(&'static str),
++    None,
++}
++
++pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[
++    ("author", DeprecationStatus::None),
++    ("cognitive_complexity", DeprecationStatus::None),
++    (
++        "cyclomatic_complexity",
++        DeprecationStatus::Replaced("cognitive_complexity"),
++    ),
++    ("dump", DeprecationStatus::None),
++];
++
++pub struct LimitStack {
++    stack: Vec<u64>,
++}
++
++impl Drop for LimitStack {
++    fn drop(&mut self) {
++        assert_eq!(self.stack.len(), 1);
++    }
++}
++
++impl LimitStack {
++    #[must_use]
++    pub fn new(limit: u64) -> Self {
++        Self { stack: vec![limit] }
++    }
++    pub fn limit(&self) -> u64 {
++        *self.stack.last().expect("there should always be a value in the stack")
++    }
++    pub fn push_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
++        let stack = &mut self.stack;
++        parse_attrs(sess, attrs, name, |val| stack.push(val));
++    }
++    pub fn pop_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
++        let stack = &mut self.stack;
++        parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val)));
++    }
++}
++
++pub fn get_attr<'a>(
++    sess: &'a Session,
++    attrs: &'a [ast::Attribute],
++    name: &'static str,
++) -> impl Iterator<Item = &'a ast::Attribute> {
++    attrs.iter().filter(move |attr| {
++        let attr = if let ast::AttrKind::Normal(ref attr) = attr.kind {
++            attr
++        } else {
++            return false;
++        };
++        let attr_segments = &attr.path.segments;
++        if attr_segments.len() == 2 && attr_segments[0].ident.to_string() == "clippy" {
++            if let Some(deprecation_status) =
++                BUILTIN_ATTRIBUTES
++                    .iter()
++                    .find_map(|(builtin_name, deprecation_status)| {
++                        if *builtin_name == attr_segments[1].ident.to_string() {
++                            Some(deprecation_status)
++                        } else {
++                            None
++                        }
++                    })
++            {
++                let mut diag = sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute");
++                match *deprecation_status {
++                    DeprecationStatus::Deprecated => {
++                        diag.emit();
++                        false
++                    },
++                    DeprecationStatus::Replaced(new_name) => {
++                        diag.span_suggestion(
++                            attr_segments[1].ident.span,
++                            "consider using",
++                            new_name.to_string(),
++                            Applicability::MachineApplicable,
++                        );
++                        diag.emit();
++                        false
++                    },
++                    DeprecationStatus::None => {
++                        diag.cancel();
++                        attr_segments[1].ident.to_string() == name
++                    },
++                }
++            } else {
++                sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute");
++                false
++            }
++        } else {
++            false
++        }
++    })
++}
++
++fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'static str, mut f: F) {
++    for attr in get_attr(sess, attrs, name) {
++        if let Some(ref value) = attr.value_str() {
++            if let Ok(value) = FromStr::from_str(&value.as_str()) {
++                f(value)
++            } else {
++                sess.span_err(attr.span, "not a number");
++            }
++        } else {
++            sess.span_err(attr.span, "bad clippy attribute");
++        }
++    }
++}
++
++/// Return true if the attributes contain any of `proc_macro`,
++/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
++pub fn is_proc_macro(attrs: &[ast::Attribute]) -> bool {
++    attrs.iter().any(is_proc_macro_attr)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..74601008dca41e8ea1b74169af87ae55d653793f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,757 @@@
++//! 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, higher};
++use rustc_ast::ast::{Attribute, LitFloatType, LitKind};
++use rustc_ast::walk_list;
++use rustc_data_structures::fx::FxHashMap;
++use rustc_hir as hir;
++use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
++use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind, QPath, Stmt, StmtKind, TyKind};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_session::Session;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Generates clippy code that detects the offending pattern
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// // ./tests/ui/my_lint.rs
++    /// fn foo() {
++    ///     // detect the following pattern
++    ///     #[clippy::author]
++    ///     if x == 42 {
++    ///         // but ignore everything from here on
++    ///         #![clippy::author = "ignore"]
++    ///     }
++    ///     ()
++    /// }
++    /// ```
++    ///
++    /// Running `TESTNAME=ui/my_lint cargo uitest` will produce
++    /// a `./tests/ui/new_lint.stdout` file with the generated code:
++    ///
++    /// ```rust,ignore
++    /// // ./tests/ui/new_lint.stdout
++    /// if_chain! {
++    ///     if let ExprKind::If(ref cond, ref then, None) = item.kind,
++    ///     if let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind,
++    ///     if let ExprKind::Path(ref path) = left.kind,
++    ///     if let ExprKind::Lit(ref lit) = right.kind,
++    ///     if let LitKind::Int(42, _) = lit.node,
++    ///     then {
++    ///         // report your lint here
++    ///     }
++    /// }
++    /// ```
++    pub LINT_AUTHOR,
++    internal_warn,
++    "helper for writing lints"
++}
++
++declare_lint_pass!(Author => [LINT_AUTHOR]);
++
++fn prelude() {
++    println!("if_chain! {{");
++}
++
++fn done() {
++    println!("    then {{");
++    println!("        // report your lint here");
++    println!("    }}");
++    println!("}}");
++}
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Author {
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) {
++        if !has_attr(cx.sess(), &item.attrs) {
++            return;
++        }
++        prelude();
++        PrintVisitor::new("item").visit_item(item);
++        done();
++    }
++
++    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem<'_>) {
++        if !has_attr(cx.sess(), &item.attrs) {
++            return;
++        }
++        prelude();
++        PrintVisitor::new("item").visit_impl_item(item);
++        done();
++    }
++
++    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem<'_>) {
++        if !has_attr(cx.sess(), &item.attrs) {
++            return;
++        }
++        prelude();
++        PrintVisitor::new("item").visit_trait_item(item);
++        done();
++    }
++
++    fn check_variant(&mut self, cx: &LateContext<'a, 'tcx>, var: &'tcx hir::Variant<'_>) {
++        if !has_attr(cx.sess(), &var.attrs) {
++            return;
++        }
++        prelude();
++        let parent_hir_id = cx.tcx.hir().get_parent_node(var.id);
++        PrintVisitor::new("var").visit_variant(var, &hir::Generics::empty(), parent_hir_id);
++        done();
++    }
++
++    fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, field: &'tcx hir::StructField<'_>) {
++        if !has_attr(cx.sess(), &field.attrs) {
++            return;
++        }
++        prelude();
++        PrintVisitor::new("field").visit_struct_field(field);
++        done();
++    }
++
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if !has_attr(cx.sess(), &expr.attrs) {
++            return;
++        }
++        prelude();
++        PrintVisitor::new("expr").visit_expr(expr);
++        done();
++    }
++
++    fn check_arm(&mut self, cx: &LateContext<'a, 'tcx>, arm: &'tcx hir::Arm<'_>) {
++        if !has_attr(cx.sess(), &arm.attrs) {
++            return;
++        }
++        prelude();
++        PrintVisitor::new("arm").visit_arm(arm);
++        done();
++    }
++
++    fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx hir::Stmt<'_>) {
++        if !has_attr(cx.sess(), stmt.kind.attrs()) {
++            return;
++        }
++        prelude();
++        PrintVisitor::new("stmt").visit_stmt(stmt);
++        done();
++    }
++
++    fn check_foreign_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ForeignItem<'_>) {
++        if !has_attr(cx.sess(), &item.attrs) {
++            return;
++        }
++        prelude();
++        PrintVisitor::new("item").visit_foreign_item(item);
++        done();
++    }
++}
++
++impl PrintVisitor {
++    #[must_use]
++    fn new(s: &'static str) -> Self {
++        Self {
++            ids: FxHashMap::default(),
++            current: s.to_owned(),
++        }
++    }
++
++    fn next(&mut self, s: &'static str) -> String {
++        use std::collections::hash_map::Entry::{Occupied, Vacant};
++        match self.ids.entry(s) {
++            // already there: start numbering from `1`
++            Occupied(mut occ) => {
++                let val = occ.get_mut();
++                *val += 1;
++                format!("{}{}", s, *val)
++            },
++            // not there: insert and return name as given
++            Vacant(vac) => {
++                vac.insert(0);
++                s.to_owned()
++            },
++        }
++    }
++
++    fn print_qpath(&mut self, path: &QPath<'_>) {
++        print!("    if match_qpath({}, &[", self.current);
++        print_path(path, &mut true);
++        println!("]);");
++    }
++}
++
++struct PrintVisitor {
++    /// Fields are the current index that needs to be appended to pattern
++    /// binding names
++    ids: FxHashMap<&'static str, usize>,
++    /// the name that needs to be destructured
++    current: String,
++}
++
++impl<'tcx> Visitor<'tcx> for PrintVisitor {
++    type Map = Map<'tcx>;
++
++    #[allow(clippy::too_many_lines)]
++    fn visit_expr(&mut self, expr: &Expr<'_>) {
++        // handle if desugarings
++        // TODO add more desugarings here
++        if let Some((cond, then, opt_else)) = higher::if_block(&expr) {
++            let cond_pat = self.next("cond");
++            let then_pat = self.next("then");
++            if let Some(else_) = opt_else {
++                let else_pat = self.next("else_");
++                println!(
++                    "    if let Some((ref {}, ref {}, Some({}))) = higher::if_block(&{});",
++                    cond_pat, then_pat, else_pat, self.current
++                );
++                self.current = else_pat;
++                self.visit_expr(else_);
++            } else {
++                println!(
++                    "    if let Some((ref {}, ref {}, None)) = higher::if_block(&{});",
++                    cond_pat, then_pat, self.current
++                );
++            }
++            self.current = cond_pat;
++            self.visit_expr(cond);
++            self.current = then_pat;
++            self.visit_expr(then);
++            return;
++        }
++
++        print!("    if let ExprKind::");
++        let current = format!("{}.kind", self.current);
++        match expr.kind {
++            ExprKind::Box(ref inner) => {
++                let inner_pat = self.next("inner");
++                println!("Box(ref {}) = {};", inner_pat, current);
++                self.current = inner_pat;
++                self.visit_expr(inner);
++            },
++            ExprKind::Array(ref elements) => {
++                let elements_pat = self.next("elements");
++                println!("Array(ref {}) = {};", elements_pat, current);
++                println!("    if {}.len() == {};", elements_pat, elements.len());
++                for (i, element) in elements.iter().enumerate() {
++                    self.current = format!("{}[{}]", elements_pat, i);
++                    self.visit_expr(element);
++                }
++            },
++            ExprKind::Call(ref func, ref args) => {
++                let func_pat = self.next("func");
++                let args_pat = self.next("args");
++                println!("Call(ref {}, ref {}) = {};", func_pat, args_pat, current);
++                self.current = func_pat;
++                self.visit_expr(func);
++                println!("    if {}.len() == {};", args_pat, args.len());
++                for (i, arg) in args.iter().enumerate() {
++                    self.current = format!("{}[{}]", args_pat, i);
++                    self.visit_expr(arg);
++                }
++            },
++            ExprKind::MethodCall(ref _method_name, ref _generics, ref _args) => {
++                println!("MethodCall(ref method_name, ref generics, ref args) = {};", current);
++                println!("    // unimplemented: `ExprKind::MethodCall` is not further destructured at the moment");
++            },
++            ExprKind::Tup(ref elements) => {
++                let elements_pat = self.next("elements");
++                println!("Tup(ref {}) = {};", elements_pat, current);
++                println!("    if {}.len() == {};", elements_pat, elements.len());
++                for (i, element) in elements.iter().enumerate() {
++                    self.current = format!("{}[{}]", elements_pat, i);
++                    self.visit_expr(element);
++                }
++            },
++            ExprKind::Binary(ref op, ref left, ref right) => {
++                let op_pat = self.next("op");
++                let left_pat = self.next("left");
++                let right_pat = self.next("right");
++                println!(
++                    "Binary(ref {}, ref {}, ref {}) = {};",
++                    op_pat, left_pat, right_pat, current
++                );
++                println!("    if BinOpKind::{:?} == {}.node;", op.node, op_pat);
++                self.current = left_pat;
++                self.visit_expr(left);
++                self.current = right_pat;
++                self.visit_expr(right);
++            },
++            ExprKind::Unary(ref op, ref inner) => {
++                let inner_pat = self.next("inner");
++                println!("Unary(UnOp::{:?}, ref {}) = {};", op, inner_pat, current);
++                self.current = inner_pat;
++                self.visit_expr(inner);
++            },
++            ExprKind::Lit(ref lit) => {
++                let lit_pat = self.next("lit");
++                println!("Lit(ref {}) = {};", lit_pat, current);
++                match lit.node {
++                    LitKind::Bool(val) => println!("    if let LitKind::Bool({:?}) = {}.node;", val, lit_pat),
++                    LitKind::Char(c) => println!("    if let LitKind::Char({:?}) = {}.node;", c, lit_pat),
++                    LitKind::Err(val) => println!("    if let LitKind::Err({}) = {}.node;", val, lit_pat),
++                    LitKind::Byte(b) => println!("    if let LitKind::Byte({}) = {}.node;", b, lit_pat),
++                    // FIXME: also check int type
++                    LitKind::Int(i, _) => println!("    if let LitKind::Int({}, _) = {}.node;", i, lit_pat),
++                    LitKind::Float(_, LitFloatType::Suffixed(_)) => println!(
++                        "    if let LitKind::Float(_, LitFloatType::Suffixed(_)) = {}.node;",
++                        lit_pat
++                    ),
++                    LitKind::Float(_, LitFloatType::Unsuffixed) => println!(
++                        "    if let LitKind::Float(_, LitFloatType::Unsuffixed) = {}.node;",
++                        lit_pat
++                    ),
++                    LitKind::ByteStr(ref vec) => {
++                        let vec_pat = self.next("vec");
++                        println!("    if let LitKind::ByteStr(ref {}) = {}.node;", vec_pat, lit_pat);
++                        println!("    if let [{:?}] = **{};", vec, vec_pat);
++                    },
++                    LitKind::Str(ref text, _) => {
++                        let str_pat = self.next("s");
++                        println!("    if let LitKind::Str(ref {}, _) = {}.node;", str_pat, lit_pat);
++                        println!("    if {}.as_str() == {:?}", str_pat, &*text.as_str())
++                    },
++                }
++            },
++            ExprKind::Cast(ref expr, ref ty) => {
++                let cast_pat = self.next("expr");
++                let cast_ty = self.next("cast_ty");
++                let qp_label = self.next("qp");
++
++                println!("Cast(ref {}, ref {}) = {};", cast_pat, cast_ty, current);
++                if let TyKind::Path(ref qp) = ty.kind {
++                    println!("    if let TyKind::Path(ref {}) = {}.kind;", qp_label, cast_ty);
++                    self.current = qp_label;
++                    self.print_qpath(qp);
++                }
++                self.current = cast_pat;
++                self.visit_expr(expr);
++            },
++            ExprKind::Type(ref expr, ref _ty) => {
++                let cast_pat = self.next("expr");
++                println!("Type(ref {}, _) = {};", cast_pat, current);
++                self.current = cast_pat;
++                self.visit_expr(expr);
++            },
++            ExprKind::Loop(ref body, _, desugaring) => {
++                let body_pat = self.next("body");
++                let des = loop_desugaring_name(desugaring);
++                let label_pat = self.next("label");
++                println!("Loop(ref {}, ref {}, {}) = {};", body_pat, label_pat, des, current);
++                self.current = body_pat;
++                self.visit_block(body);
++            },
++            ExprKind::Match(ref expr, ref arms, desugaring) => {
++                let des = desugaring_name(desugaring);
++                let expr_pat = self.next("expr");
++                let arms_pat = self.next("arms");
++                println!("Match(ref {}, ref {}, {}) = {};", expr_pat, arms_pat, des, current);
++                self.current = expr_pat;
++                self.visit_expr(expr);
++                println!("    if {}.len() == {};", arms_pat, arms.len());
++                for (i, arm) in arms.iter().enumerate() {
++                    self.current = format!("{}[{}].body", arms_pat, i);
++                    self.visit_expr(&arm.body);
++                    if let Some(ref guard) = arm.guard {
++                        let guard_pat = self.next("guard");
++                        println!("    if let Some(ref {}) = {}[{}].guard;", guard_pat, arms_pat, i);
++                        match guard {
++                            hir::Guard::If(ref if_expr) => {
++                                let if_expr_pat = self.next("expr");
++                                println!("    if let Guard::If(ref {}) = {};", if_expr_pat, guard_pat);
++                                self.current = if_expr_pat;
++                                self.visit_expr(if_expr);
++                            },
++                        }
++                    }
++                    self.current = format!("{}[{}].pat", arms_pat, i);
++                    self.visit_pat(&arm.pat);
++                }
++            },
++            ExprKind::Closure(ref _capture_clause, ref _func, _, _, _) => {
++                println!("Closure(ref capture_clause, ref func, _, _, _) = {};", current);
++                println!("    // unimplemented: `ExprKind::Closure` is not further destructured at the moment");
++            },
++            ExprKind::Yield(ref sub, _) => {
++                let sub_pat = self.next("sub");
++                println!("Yield(ref sub) = {};", current);
++                self.current = sub_pat;
++                self.visit_expr(sub);
++            },
++            ExprKind::Block(ref block, _) => {
++                let block_pat = self.next("block");
++                println!("Block(ref {}) = {};", block_pat, current);
++                self.current = block_pat;
++                self.visit_block(block);
++            },
++            ExprKind::Assign(ref target, ref value, _) => {
++                let target_pat = self.next("target");
++                let value_pat = self.next("value");
++                println!(
++                    "Assign(ref {}, ref {}, ref _span) = {};",
++                    target_pat, value_pat, current
++                );
++                self.current = target_pat;
++                self.visit_expr(target);
++                self.current = value_pat;
++                self.visit_expr(value);
++            },
++            ExprKind::AssignOp(ref op, ref target, ref value) => {
++                let op_pat = self.next("op");
++                let target_pat = self.next("target");
++                let value_pat = self.next("value");
++                println!(
++                    "AssignOp(ref {}, ref {}, ref {}) = {};",
++                    op_pat, target_pat, value_pat, current
++                );
++                println!("    if BinOpKind::{:?} == {}.node;", op.node, op_pat);
++                self.current = target_pat;
++                self.visit_expr(target);
++                self.current = value_pat;
++                self.visit_expr(value);
++            },
++            ExprKind::Field(ref object, ref field_ident) => {
++                let obj_pat = self.next("object");
++                let field_name_pat = self.next("field_name");
++                println!("Field(ref {}, ref {}) = {};", obj_pat, field_name_pat, current);
++                println!("    if {}.as_str() == {:?}", field_name_pat, field_ident.as_str());
++                self.current = obj_pat;
++                self.visit_expr(object);
++            },
++            ExprKind::Index(ref object, ref index) => {
++                let object_pat = self.next("object");
++                let index_pat = self.next("index");
++                println!("Index(ref {}, ref {}) = {};", object_pat, index_pat, current);
++                self.current = object_pat;
++                self.visit_expr(object);
++                self.current = index_pat;
++                self.visit_expr(index);
++            },
++            ExprKind::Path(ref path) => {
++                let path_pat = self.next("path");
++                println!("Path(ref {}) = {};", path_pat, current);
++                self.current = path_pat;
++                self.print_qpath(path);
++            },
++            ExprKind::AddrOf(kind, mutability, ref inner) => {
++                let inner_pat = self.next("inner");
++                println!(
++                    "AddrOf(BorrowKind::{:?}, Mutability::{:?}, ref {}) = {};",
++                    kind, mutability, inner_pat, current
++                );
++                self.current = inner_pat;
++                self.visit_expr(inner);
++            },
++            ExprKind::Break(ref _destination, ref opt_value) => {
++                let destination_pat = self.next("destination");
++                if let Some(ref value) = *opt_value {
++                    let value_pat = self.next("value");
++                    println!("Break(ref {}, Some(ref {})) = {};", destination_pat, value_pat, current);
++                    self.current = value_pat;
++                    self.visit_expr(value);
++                } else {
++                    println!("Break(ref {}, None) = {};", destination_pat, current);
++                }
++                // FIXME: implement label printing
++            },
++            ExprKind::Continue(ref _destination) => {
++                let destination_pat = self.next("destination");
++                println!("Again(ref {}) = {};", destination_pat, current);
++                // FIXME: implement label printing
++            },
++            ExprKind::Ret(ref opt_value) => {
++                if let Some(ref value) = *opt_value {
++                    let value_pat = self.next("value");
++                    println!("Ret(Some(ref {})) = {};", value_pat, current);
++                    self.current = value_pat;
++                    self.visit_expr(value);
++                } else {
++                    println!("Ret(None) = {};", current);
++                }
++            },
++            ExprKind::LlvmInlineAsm(_) => {
++                println!("LlvmInlineAsm(_) = {};", current);
++                println!("    // unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment");
++            },
++            ExprKind::Struct(ref path, ref fields, ref opt_base) => {
++                let path_pat = self.next("path");
++                let fields_pat = self.next("fields");
++                if let Some(ref base) = *opt_base {
++                    let base_pat = self.next("base");
++                    println!(
++                        "Struct(ref {}, ref {}, Some(ref {})) = {};",
++                        path_pat, fields_pat, base_pat, current
++                    );
++                    self.current = base_pat;
++                    self.visit_expr(base);
++                } else {
++                    println!("Struct(ref {}, ref {}, None) = {};", path_pat, fields_pat, current);
++                }
++                self.current = path_pat;
++                self.print_qpath(path);
++                println!("    if {}.len() == {};", fields_pat, fields.len());
++                println!("    // unimplemented: field checks");
++            },
++            // FIXME: compute length (needs type info)
++            ExprKind::Repeat(ref value, _) => {
++                let value_pat = self.next("value");
++                println!("Repeat(ref {}, _) = {};", value_pat, current);
++                println!("// unimplemented: repeat count check");
++                self.current = value_pat;
++                self.visit_expr(value);
++            },
++            ExprKind::Err => {
++                println!("Err = {}", current);
++            },
++            ExprKind::DropTemps(ref expr) => {
++                let expr_pat = self.next("expr");
++                println!("DropTemps(ref {}) = {};", expr_pat, current);
++                self.current = expr_pat;
++                self.visit_expr(expr);
++            },
++        }
++    }
++
++    fn visit_block(&mut self, block: &Block<'_>) {
++        let trailing_pat = self.next("trailing_expr");
++        println!("    if let Some({}) = &{}.expr;", trailing_pat, self.current);
++        println!("    if {}.stmts.len() == {};", self.current, block.stmts.len());
++        let current = self.current.clone();
++        for (i, stmt) in block.stmts.iter().enumerate() {
++            self.current = format!("{}.stmts[{}]", current, i);
++            self.visit_stmt(stmt);
++        }
++    }
++
++    #[allow(clippy::too_many_lines)]
++    fn visit_pat(&mut self, pat: &Pat<'_>) {
++        print!("    if let PatKind::");
++        let current = format!("{}.kind", self.current);
++        match pat.kind {
++            PatKind::Wild => println!("Wild = {};", current),
++            PatKind::Binding(anno, .., ident, ref sub) => {
++                let anno_pat = match anno {
++                    BindingAnnotation::Unannotated => "BindingAnnotation::Unannotated",
++                    BindingAnnotation::Mutable => "BindingAnnotation::Mutable",
++                    BindingAnnotation::Ref => "BindingAnnotation::Ref",
++                    BindingAnnotation::RefMut => "BindingAnnotation::RefMut",
++                };
++                let name_pat = self.next("name");
++                if let Some(ref sub) = *sub {
++                    let sub_pat = self.next("sub");
++                    println!(
++                        "Binding({}, _, {}, Some(ref {})) = {};",
++                        anno_pat, name_pat, sub_pat, current
++                    );
++                    self.current = sub_pat;
++                    self.visit_pat(sub);
++                } else {
++                    println!("Binding({}, _, {}, None) = {};", anno_pat, name_pat, current);
++                }
++                println!("    if {}.as_str() == \"{}\";", name_pat, ident.as_str());
++            },
++            PatKind::Struct(ref path, ref fields, ignore) => {
++                let path_pat = self.next("path");
++                let fields_pat = self.next("fields");
++                println!(
++                    "Struct(ref {}, ref {}, {}) = {};",
++                    path_pat, fields_pat, ignore, current
++                );
++                self.current = path_pat;
++                self.print_qpath(path);
++                println!("    if {}.len() == {};", fields_pat, fields.len());
++                println!("    // unimplemented: field checks");
++            },
++            PatKind::Or(ref fields) => {
++                let fields_pat = self.next("fields");
++                println!("Or(ref {}) = {};", fields_pat, current);
++                println!("    if {}.len() == {};", fields_pat, fields.len());
++                println!("    // unimplemented: field checks");
++            },
++            PatKind::TupleStruct(ref path, ref fields, skip_pos) => {
++                let path_pat = self.next("path");
++                let fields_pat = self.next("fields");
++                println!(
++                    "TupleStruct(ref {}, ref {}, {:?}) = {};",
++                    path_pat, fields_pat, skip_pos, current
++                );
++                self.current = path_pat;
++                self.print_qpath(path);
++                println!("    if {}.len() == {};", fields_pat, fields.len());
++                println!("    // unimplemented: field checks");
++            },
++            PatKind::Path(ref path) => {
++                let path_pat = self.next("path");
++                println!("Path(ref {}) = {};", path_pat, current);
++                self.current = path_pat;
++                self.print_qpath(path);
++            },
++            PatKind::Tuple(ref fields, skip_pos) => {
++                let fields_pat = self.next("fields");
++                println!("Tuple(ref {}, {:?}) = {};", fields_pat, skip_pos, current);
++                println!("    if {}.len() == {};", fields_pat, fields.len());
++                println!("    // unimplemented: field checks");
++            },
++            PatKind::Box(ref pat) => {
++                let pat_pat = self.next("pat");
++                println!("Box(ref {}) = {};", pat_pat, current);
++                self.current = pat_pat;
++                self.visit_pat(pat);
++            },
++            PatKind::Ref(ref pat, muta) => {
++                let pat_pat = self.next("pat");
++                println!("Ref(ref {}, Mutability::{:?}) = {};", pat_pat, muta, current);
++                self.current = pat_pat;
++                self.visit_pat(pat);
++            },
++            PatKind::Lit(ref lit_expr) => {
++                let lit_expr_pat = self.next("lit_expr");
++                println!("Lit(ref {}) = {}", lit_expr_pat, current);
++                self.current = lit_expr_pat;
++                self.visit_expr(lit_expr);
++            },
++            PatKind::Range(ref start, ref end, end_kind) => {
++                let start_pat = self.next("start");
++                let end_pat = self.next("end");
++                println!(
++                    "Range(ref {}, ref {}, RangeEnd::{:?}) = {};",
++                    start_pat, end_pat, end_kind, current
++                );
++                self.current = start_pat;
++                walk_list!(self, visit_expr, start);
++                self.current = end_pat;
++                walk_list!(self, visit_expr, end);
++            },
++            PatKind::Slice(ref start, ref middle, ref end) => {
++                let start_pat = self.next("start");
++                let end_pat = self.next("end");
++                if let Some(ref middle) = middle {
++                    let middle_pat = self.next("middle");
++                    println!(
++                        "Slice(ref {}, Some(ref {}), ref {}) = {};",
++                        start_pat, middle_pat, end_pat, current
++                    );
++                    self.current = middle_pat;
++                    self.visit_pat(middle);
++                } else {
++                    println!("Slice(ref {}, None, ref {}) = {};", start_pat, end_pat, current);
++                }
++                println!("    if {}.len() == {};", start_pat, start.len());
++                for (i, pat) in start.iter().enumerate() {
++                    self.current = format!("{}[{}]", start_pat, i);
++                    self.visit_pat(pat);
++                }
++                println!("    if {}.len() == {};", end_pat, end.len());
++                for (i, pat) in end.iter().enumerate() {
++                    self.current = format!("{}[{}]", end_pat, i);
++                    self.visit_pat(pat);
++                }
++            },
++        }
++    }
++
++    fn visit_stmt(&mut self, s: &Stmt<'_>) {
++        print!("    if let StmtKind::");
++        let current = format!("{}.kind", self.current);
++        match s.kind {
++            // A local (let) binding:
++            StmtKind::Local(ref local) => {
++                let local_pat = self.next("local");
++                println!("Local(ref {}) = {};", local_pat, current);
++                if let Some(ref init) = local.init {
++                    let init_pat = self.next("init");
++                    println!("    if let Some(ref {}) = {}.init;", init_pat, local_pat);
++                    self.current = init_pat;
++                    self.visit_expr(init);
++                }
++                self.current = format!("{}.pat", local_pat);
++                self.visit_pat(&local.pat);
++            },
++            // An item binding:
++            StmtKind::Item(_) => {
++                println!("Item(item_id) = {};", current);
++            },
++
++            // Expr without trailing semi-colon (must have unit type):
++            StmtKind::Expr(ref e) => {
++                let e_pat = self.next("e");
++                println!("Expr(ref {}, _) = {}", e_pat, current);
++                self.current = e_pat;
++                self.visit_expr(e);
++            },
++
++            // Expr with trailing semi-colon (may have any type):
++            StmtKind::Semi(ref e) => {
++                let e_pat = self.next("e");
++                println!("Semi(ref {}, _) = {}", e_pat, current);
++                self.current = e_pat;
++                self.visit_expr(e);
++            },
++        }
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool {
++    get_attr(sess, attrs, "author").count() > 0
++}
++
++#[must_use]
++fn desugaring_name(des: hir::MatchSource) -> String {
++    match des {
++        hir::MatchSource::ForLoopDesugar => "MatchSource::ForLoopDesugar".to_string(),
++        hir::MatchSource::TryDesugar => "MatchSource::TryDesugar".to_string(),
++        hir::MatchSource::WhileDesugar => "MatchSource::WhileDesugar".to_string(),
++        hir::MatchSource::WhileLetDesugar => "MatchSource::WhileLetDesugar".to_string(),
++        hir::MatchSource::Normal => "MatchSource::Normal".to_string(),
++        hir::MatchSource::IfLetDesugar { contains_else_clause } => format!(
++            "MatchSource::IfLetDesugar {{ contains_else_clause: {} }}",
++            contains_else_clause
++        ),
++        hir::MatchSource::IfDesugar { contains_else_clause } => format!(
++            "MatchSource::IfDesugar {{ contains_else_clause: {} }}",
++            contains_else_clause
++        ),
++        hir::MatchSource::AwaitDesugar => "MatchSource::AwaitDesugar".to_string(),
++    }
++}
++
++#[must_use]
++fn loop_desugaring_name(des: hir::LoopSource) -> &'static str {
++    match des {
++        hir::LoopSource::ForLoop => "LoopSource::ForLoop",
++        hir::LoopSource::Loop => "LoopSource::Loop",
++        hir::LoopSource::While => "LoopSource::While",
++        hir::LoopSource::WhileLet => "LoopSource::WhileLet",
++    }
++}
++
++fn print_path(path: &QPath<'_>, first: &mut bool) {
++    match *path {
++        QPath::Resolved(_, ref path) => {
++            for segment in path.segments {
++                if *first {
++                    *first = false;
++                } else {
++                    print!(", ");
++                }
++                print!("{:?}", segment.ident.as_str());
++            }
++        },
++        QPath::TypeRelative(ref ty, ref segment) => match ty.kind {
++            hir::TyKind::Path(ref inner_path) => {
++                print_path(inner_path, first);
++                if *first {
++                    *first = false;
++                } else {
++                    print!(", ");
++                }
++                print!("{:?}", segment.ident.as_str());
++            },
++            ref other => print!("/* unimplemented: {:?}*/", other),
++        },
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4192a26d3c80040ed440dd74f355db993cf39e67
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,115 @@@
++/// Returns the index of the character after the first camel-case component of `s`.
++#[must_use]
++pub fn until(s: &str) -> usize {
++    let mut iter = s.char_indices();
++    if let Some((_, first)) = iter.next() {
++        if !first.is_uppercase() {
++            return 0;
++        }
++    } else {
++        return 0;
++    }
++    let mut up = true;
++    let mut last_i = 0;
++    for (i, c) in iter {
++        if up {
++            if c.is_lowercase() {
++                up = false;
++            } else {
++                return last_i;
++            }
++        } else if c.is_uppercase() {
++            up = true;
++            last_i = i;
++        } else if !c.is_lowercase() {
++            return i;
++        }
++    }
++    if up {
++        last_i
++    } else {
++        s.len()
++    }
++}
++
++/// Returns index of the last camel-case component of `s`.
++#[must_use]
++pub fn from(s: &str) -> usize {
++    let mut iter = s.char_indices().rev();
++    if let Some((_, first)) = iter.next() {
++        if !first.is_lowercase() {
++            return s.len();
++        }
++    } else {
++        return s.len();
++    }
++    let mut down = true;
++    let mut last_i = s.len();
++    for (i, c) in iter {
++        if down {
++            if c.is_uppercase() {
++                down = false;
++                last_i = i;
++            } else if !c.is_lowercase() {
++                return last_i;
++            }
++        } else if c.is_lowercase() {
++            down = true;
++        } else {
++            return last_i;
++        }
++    }
++    last_i
++}
++
++#[cfg(test)]
++mod test {
++    use super::{from, until};
++
++    #[test]
++    fn from_full() {
++        assert_eq!(from("AbcDef"), 0);
++        assert_eq!(from("Abc"), 0);
++    }
++
++    #[test]
++    fn from_partial() {
++        assert_eq!(from("abcDef"), 3);
++        assert_eq!(from("aDbc"), 1);
++    }
++
++    #[test]
++    fn from_not() {
++        assert_eq!(from("AbcDef_"), 7);
++        assert_eq!(from("AbcDD"), 5);
++    }
++
++    #[test]
++    fn from_caps() {
++        assert_eq!(from("ABCD"), 4);
++    }
++
++    #[test]
++    fn until_full() {
++        assert_eq!(until("AbcDef"), 6);
++        assert_eq!(until("Abc"), 3);
++    }
++
++    #[test]
++    fn until_not() {
++        assert_eq!(until("abcDef"), 0);
++        assert_eq!(until("aDbc"), 0);
++    }
++
++    #[test]
++    fn until_partial() {
++        assert_eq!(until("AbcDef_"), 6);
++        assert_eq!(until("CallTypeC"), 8);
++        assert_eq!(until("AbcDD"), 3);
++    }
++
++    #[test]
++    fn until_caps() {
++        assert_eq!(until("ABCD"), 0);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7a18d5e818fb1c607298209b6b344ef6a484021d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++//! Utility functions about comparison operators.
++
++#![deny(clippy::missing_docs_in_private_items)]
++
++use rustc_hir::{BinOpKind, Expr};
++
++#[derive(PartialEq, Eq, Debug, Copy, Clone)]
++/// Represent a normalized comparison operator.
++pub enum Rel {
++    /// `<`
++    Lt,
++    /// `<=`
++    Le,
++    /// `==`
++    Eq,
++    /// `!=`
++    Ne,
++}
++
++/// Put the expression in the form  `lhs < rhs`, `lhs <= rhs`, `lhs == rhs` or
++/// `lhs != rhs`.
++pub fn normalize_comparison<'a>(
++    op: BinOpKind,
++    lhs: &'a Expr<'a>,
++    rhs: &'a Expr<'a>,
++) -> Option<(Rel, &'a Expr<'a>, &'a Expr<'a>)> {
++    match op {
++        BinOpKind::Lt => Some((Rel::Lt, lhs, rhs)),
++        BinOpKind::Le => Some((Rel::Le, lhs, rhs)),
++        BinOpKind::Gt => Some((Rel::Lt, rhs, lhs)),
++        BinOpKind::Ge => Some((Rel::Le, rhs, lhs)),
++        BinOpKind::Eq => Some((Rel::Eq, rhs, lhs)),
++        BinOpKind::Ne => Some((Rel::Ne, rhs, lhs)),
++        _ => None,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4b81ff33495c7c089c0ad55ffc96f5336958aa8a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,240 @@@
++//! Read configurations files.
++
++#![deny(clippy::missing_docs_in_private_items)]
++
++use lazy_static::lazy_static;
++use rustc_ast::ast::{LitKind, MetaItemKind, NestedMetaItem};
++use rustc_span::source_map;
++use source_map::Span;
++use std::path::{Path, PathBuf};
++use std::sync::Mutex;
++use std::{env, fmt, fs, io};
++
++/// Gets the configuration file from arguments.
++pub fn file_from_args(args: &[NestedMetaItem]) -> Result<Option<PathBuf>, (&'static str, Span)> {
++    for arg in args.iter().filter_map(NestedMetaItem::meta_item) {
++        if arg.check_name(sym!(conf_file)) {
++            return match arg.kind {
++                MetaItemKind::Word | MetaItemKind::List(_) => Err(("`conf_file` must be a named value", arg.span)),
++                MetaItemKind::NameValue(ref value) => {
++                    if let LitKind::Str(ref file, _) = value.kind {
++                        Ok(Some(file.to_string().into()))
++                    } else {
++                        Err(("`conf_file` value must be a string", value.span))
++                    }
++                },
++            };
++        }
++    }
++
++    Ok(None)
++}
++
++/// Error from reading a configuration file.
++#[derive(Debug)]
++pub enum Error {
++    /// An I/O error.
++    Io(io::Error),
++    /// Not valid toml or doesn't fit the expected config format
++    Toml(String),
++}
++
++impl fmt::Display for Error {
++    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++        match self {
++            Self::Io(err) => err.fmt(f),
++            Self::Toml(err) => err.fmt(f),
++        }
++    }
++}
++
++impl From<io::Error> for Error {
++    fn from(e: io::Error) -> Self {
++        Self::Io(e)
++    }
++}
++
++lazy_static! {
++    static ref ERRORS: Mutex<Vec<Error>> = Mutex::new(Vec::new());
++}
++
++macro_rules! define_Conf {
++    ($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => {
++        mod helpers {
++            use serde::Deserialize;
++            /// Type used to store lint configuration.
++            #[derive(Deserialize)]
++            #[serde(rename_all = "kebab-case", deny_unknown_fields)]
++            pub struct Conf {
++                $(
++                    #[$doc]
++                    #[serde(default = $config_str)]
++                    #[serde(with = $config_str)]
++                    pub $config: $Ty,
++                )+
++                #[allow(dead_code)]
++                #[serde(default)]
++                third_party: Option<::toml::Value>,
++            }
++
++            $(
++                mod $config {
++                    use serde::Deserialize;
++                    pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<$Ty, D::Error> {
++                        use super::super::{ERRORS, Error};
++                        Ok(
++                            <$Ty>::deserialize(deserializer).unwrap_or_else(|e| {
++                                ERRORS
++                                    .lock()
++                                    .expect("no threading here")
++                                    .push(Error::Toml(e.to_string()));
++                                super::$config()
++                            })
++                        )
++                    }
++                }
++
++                #[must_use]
++                fn $config() -> $Ty {
++                    let x = $default;
++                    x
++                }
++            )+
++        }
++    };
++}
++
++pub use self::helpers::Conf;
++define_Conf! {
++    /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about
++    (blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "bar", "baz", "quux"].iter().map(ToString::to_string).collect()),
++    /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
++    (cognitive_complexity_threshold, "cognitive_complexity_threshold": u64, 25),
++    /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead.
++    (cyclomatic_complexity_threshold, "cyclomatic_complexity_threshold": Option<u64>, None),
++    /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
++    (doc_valid_idents, "doc_valid_idents": Vec<String>, [
++        "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
++        "DirectX",
++        "ECMAScript",
++        "GPLv2", "GPLv3",
++        "GitHub", "GitLab",
++        "IPv4", "IPv6",
++        "JavaScript",
++        "NaN", "NaNs",
++        "OAuth",
++        "OpenGL", "OpenSSH", "OpenSSL", "OpenStreetMap",
++        "TrueType",
++        "iOS", "macOS",
++        "TeX", "LaTeX", "BibTeX", "BibLaTeX",
++        "MinGW",
++        "CamelCase",
++    ].iter().map(ToString::to_string).collect()),
++    /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
++    (too_many_arguments_threshold, "too_many_arguments_threshold": u64, 7),
++    /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
++    (type_complexity_threshold, "type_complexity_threshold": u64, 250),
++    /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
++    (single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4),
++    /// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
++    (too_large_for_stack, "too_large_for_stack": u64, 200),
++    /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
++    (enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3),
++    /// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion
++    (enum_variant_size_threshold, "enum_variant_size_threshold": u64, 200),
++    /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
++    (verbose_bit_mask_threshold, "verbose_bit_mask_threshold": u64, 1),
++    /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals
++    (literal_representation_threshold, "literal_representation_threshold": u64, 16384),
++    /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
++    (trivial_copy_size_limit, "trivial_copy_size_limit": Option<u64>, None),
++    /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
++    (too_many_lines_threshold, "too_many_lines_threshold": u64, 100),
++    /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack
++    (array_size_threshold, "array_size_threshold": u64, 512_000),
++    /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
++    (vec_box_size_threshold, "vec_box_size_threshold": u64, 4096),
++    /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
++    (max_struct_bools, "max_struct_bools": u64, 3),
++    /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
++    (max_fn_params_bools, "max_fn_params_bools": u64, 3),
++}
++
++impl Default for Conf {
++    #[must_use]
++    fn default() -> Self {
++        toml::from_str("").expect("we never error on empty config files")
++    }
++}
++
++/// Search for the configuration file.
++pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
++    /// Possible filename to search for.
++    const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
++
++    // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
++    // If neither of those exist, use ".".
++    let mut current = env::var_os("CLIPPY_CONF_DIR")
++        .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
++        .map_or_else(|| PathBuf::from("."), PathBuf::from);
++    loop {
++        for config_file_name in &CONFIG_FILE_NAMES {
++            let config_file = current.join(config_file_name);
++            match fs::metadata(&config_file) {
++                // Only return if it's a file to handle the unlikely situation of a directory named
++                // `clippy.toml`.
++                Ok(ref md) if !md.is_dir() => return Ok(Some(config_file)),
++                // Return the error if it's something other than `NotFound`; otherwise we didn't
++                // find the project file yet, and continue searching.
++                Err(e) if e.kind() != io::ErrorKind::NotFound => return Err(e),
++                _ => {},
++            }
++        }
++
++        // If the current directory has no parent, we're done searching.
++        if !current.pop() {
++            return Ok(None);
++        }
++    }
++}
++
++/// Produces a `Conf` filled with the default values and forwards the errors
++///
++/// Used internally for convenience
++fn default(errors: Vec<Error>) -> (Conf, Vec<Error>) {
++    (Conf::default(), errors)
++}
++
++/// Read the `toml` configuration file.
++///
++/// In case of error, the function tries to continue as much as possible.
++pub fn read(path: &Path) -> (Conf, Vec<Error>) {
++    let content = match fs::read_to_string(path) {
++        Ok(content) => content,
++        Err(err) => return default(vec![err.into()]),
++    };
++
++    assert!(ERRORS.lock().expect("no threading -> mutex always safe").is_empty());
++    match toml::from_str(&content) {
++        Ok(toml) => {
++            let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);
++
++            let toml_ref: &Conf = &toml;
++
++            let cyc_field: Option<u64> = toml_ref.cyclomatic_complexity_threshold;
++
++            if cyc_field.is_some() {
++                let cyc_err = "found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.".to_string();
++                errors.push(Error::Toml(cyc_err));
++            }
++
++            (toml, errors)
++        },
++        Err(e) => {
++            let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);
++            errors.push(Error::Toml(e.to_string()));
++
++            default(errors)
++        },
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..522932f054d894eef06d81b7a53203fc255f9d89
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++//! This module contains some useful constants.
++
++#![deny(clippy::missing_docs_in_private_items)]
++
++/// List of the built-in types names.
++///
++/// See also [the reference][reference-types] for a list of such types.
++///
++/// [reference-types]: https://doc.rust-lang.org/reference/types.html
++pub const BUILTIN_TYPES: &[&str] = &[
++    "i8", "u8", "i16", "u16", "i32", "u32", "i64", "u64", "i128", "u128", "isize", "usize", "f32", "f64", "bool",
++    "str", "char",
++];
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..24a1bdf1883f6b86edbe53f755d8a26821ca4265
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,217 @@@
++//! Clippy wrappers around rustc's diagnostic functions.
++
++use rustc_errors::{Applicability, CodeSuggestion, DiagnosticBuilder, Substitution, SubstitutionPart, SuggestionStyle};
++use rustc_hir::HirId;
++use rustc_lint::{LateContext, Lint, LintContext};
++use rustc_span::source_map::{MultiSpan, Span};
++use std::env;
++
++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)
++        ));
++    }
++}
++
++/// Emit a basic lint message with a `msg` and a `span`.
++///
++/// This is the most primitive of our lint emission methods and can
++/// be a good way to get a new lint started.
++///
++/// Usually it's nicer to provide more context for lint messages.
++/// Be sure the output is understandable when you use this method.
++///
++/// # Example
++///
++/// ```ignore
++/// error: usage of mem::forget on Drop type
++///   --> $DIR/mem_forget.rs:17:5
++///    |
++/// 17 |     std::mem::forget(seven);
++///    |     ^^^^^^^^^^^^^^^^^^^^^^^
++/// ```
++pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<MultiSpan>, msg: &str) {
++    cx.struct_span_lint(lint, sp, |diag| {
++        let mut diag = diag.build(msg);
++        docs_link(&mut diag, lint);
++        diag.emit();
++    });
++}
++
++/// Same as `span_lint` but with an extra `help` message.
++///
++/// Use this if you want to provide some general help but
++/// can't provide a specific machine applicable suggestion.
++///
++/// The `help` message can be optionally attached to a `Span`.
++///
++/// # Example
++///
++/// ```ignore
++/// error: constant division of 0.0 with 0.0 will always result in NaN
++///   --> $DIR/zero_div_zero.rs:6:25
++///    |
++/// 6  |     let other_f64_nan = 0.0f64 / 0.0;
++///    |                         ^^^^^^^^^^^^
++///    |
++///    = help: Consider using `f64::NAN` if you would like a constant representing NaN
++/// ```
++pub fn span_lint_and_help<'a, T: LintContext>(
++    cx: &'a T,
++    lint: &'static Lint,
++    span: Span,
++    msg: &str,
++    help_span: Option<Span>,
++    help: &str,
++) {
++    cx.struct_span_lint(lint, span, |diag| {
++        let mut diag = diag.build(msg);
++        if let Some(help_span) = help_span {
++            diag.span_help(help_span, help);
++        } else {
++            diag.help(help);
++        }
++        docs_link(&mut diag, lint);
++        diag.emit();
++    });
++}
++
++/// Like `span_lint` but with a `note` section instead of a `help` message.
++///
++/// The `note` message is presented separately from the main lint message
++/// and is attached to a specific span:
++///
++/// # Example
++///
++/// ```ignore
++/// error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++///   --> $DIR/drop_forget_ref.rs:10:5
++///    |
++/// 10 |     forget(&SomeStruct);
++///    |     ^^^^^^^^^^^^^^^^^^^
++///    |
++///    = note: `-D clippy::forget-ref` implied by `-D warnings`
++/// note: argument has type &SomeStruct
++///   --> $DIR/drop_forget_ref.rs:10:12
++///    |
++/// 10 |     forget(&SomeStruct);
++///    |            ^^^^^^^^^^^
++/// ```
++pub fn span_lint_and_note<'a, T: LintContext>(
++    cx: &'a T,
++    lint: &'static Lint,
++    span: Span,
++    msg: &str,
++    note_span: Option<Span>,
++    note: &str,
++) {
++    cx.struct_span_lint(lint, span, |diag| {
++        let mut diag = diag.build(msg);
++        if let Some(note_span) = note_span {
++            diag.span_note(note_span, note);
++        } else {
++            diag.note(note);
++        }
++        docs_link(&mut diag, lint);
++        diag.emit();
++    });
++}
++
++/// Like `span_lint` but allows to add notes, help and suggestions using a closure.
++///
++/// If you need to customize your lint output a lot, use this function.
++pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
++where
++    F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
++{
++    cx.struct_span_lint(lint, sp, |diag| {
++        let mut diag = diag.build(msg);
++        f(&mut diag);
++        docs_link(&mut diag, lint);
++        diag.emit();
++    });
++}
++
++pub fn span_lint_hir(cx: &LateContext<'_, '_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) {
++    cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| {
++        let mut diag = diag.build(msg);
++        docs_link(&mut diag, lint);
++        diag.emit();
++    });
++}
++
++pub fn span_lint_hir_and_then(
++    cx: &LateContext<'_, '_>,
++    lint: &'static Lint,
++    hir_id: HirId,
++    sp: Span,
++    msg: &str,
++    f: impl FnOnce(&mut DiagnosticBuilder<'_>),
++) {
++    cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| {
++        let mut diag = diag.build(msg);
++        f(&mut diag);
++        docs_link(&mut diag, lint);
++        diag.emit();
++    });
++}
++
++/// Add a span lint with a suggestion on how to fix it.
++///
++/// These suggestions can be parsed by rustfix to allow it to automatically fix your code.
++/// In the example below, `help` is `"try"` and `sugg` is the suggested replacement `".any(|x| x >
++/// 2)"`.
++///
++/// ```ignore
++/// error: This `.fold` can be more succinctly expressed as `.any`
++/// --> $DIR/methods.rs:390:13
++///     |
++/// 390 |     let _ = (0..3).fold(false, |acc, x| acc || x > 2);
++///     |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
++///     |
++///     = note: `-D fold-any` implied by `-D warnings`
++/// ```
++#[allow(clippy::collapsible_span_lint_calls)]
++pub fn span_lint_and_sugg<'a, T: LintContext>(
++    cx: &'a T,
++    lint: &'static Lint,
++    sp: Span,
++    msg: &str,
++    help: &str,
++    sugg: String,
++    applicability: Applicability,
++) {
++    span_lint_and_then(cx, lint, sp, msg, |diag| {
++        diag.span_suggestion(sp, help, sugg, applicability);
++    });
++}
++
++/// Create a suggestion made from several `span → replacement`.
++///
++/// Note: in the JSON format (used by `compiletest_rs`), the help message will
++/// appear once per
++/// replacement. In human-readable format though, it only appears once before
++/// the whole suggestion.
++pub fn multispan_sugg<I>(diag: &mut DiagnosticBuilder<'_>, help_msg: String, sugg: I)
++where
++    I: IntoIterator<Item = (Span, String)>,
++{
++    let sugg = CodeSuggestion {
++        substitutions: vec![Substitution {
++            parts: sugg
++                .into_iter()
++                .map(|(span, snippet)| SubstitutionPart { snippet, span })
++                .collect(),
++        }],
++        msg: help_msg,
++        style: SuggestionStyle::ShowCode,
++        applicability: Applicability::Unspecified,
++    };
++    diag.suggestions.push(sugg);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..33fba7df8d33660c13f03e7d397f586f76b09e63
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,293 @@@
++//! This module contains functions for retrieve the original AST from lowered
++//! `hir`.
++
++#![deny(clippy::missing_docs_in_private_items)]
++
++use crate::utils::{is_expn_of, match_def_path, match_qpath, paths};
++use if_chain::if_chain;
++use rustc_ast::ast;
++use rustc_hir as hir;
++use rustc_lint::LateContext;
++use rustc_middle::ty;
++
++/// Converts a hir binary operator to the corresponding `ast` type.
++#[must_use]
++pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind {
++    match op {
++        hir::BinOpKind::Eq => ast::BinOpKind::Eq,
++        hir::BinOpKind::Ge => ast::BinOpKind::Ge,
++        hir::BinOpKind::Gt => ast::BinOpKind::Gt,
++        hir::BinOpKind::Le => ast::BinOpKind::Le,
++        hir::BinOpKind::Lt => ast::BinOpKind::Lt,
++        hir::BinOpKind::Ne => ast::BinOpKind::Ne,
++        hir::BinOpKind::Or => ast::BinOpKind::Or,
++        hir::BinOpKind::Add => ast::BinOpKind::Add,
++        hir::BinOpKind::And => ast::BinOpKind::And,
++        hir::BinOpKind::BitAnd => ast::BinOpKind::BitAnd,
++        hir::BinOpKind::BitOr => ast::BinOpKind::BitOr,
++        hir::BinOpKind::BitXor => ast::BinOpKind::BitXor,
++        hir::BinOpKind::Div => ast::BinOpKind::Div,
++        hir::BinOpKind::Mul => ast::BinOpKind::Mul,
++        hir::BinOpKind::Rem => ast::BinOpKind::Rem,
++        hir::BinOpKind::Shl => ast::BinOpKind::Shl,
++        hir::BinOpKind::Shr => ast::BinOpKind::Shr,
++        hir::BinOpKind::Sub => ast::BinOpKind::Sub,
++    }
++}
++
++/// Represent a range akin to `ast::ExprKind::Range`.
++#[derive(Debug, Copy, Clone)]
++pub struct Range<'a> {
++    /// The lower bound of the range, or `None` for ranges such as `..X`.
++    pub start: Option<&'a hir::Expr<'a>>,
++    /// The upper bound of the range, or `None` for ranges such as `X..`.
++    pub end: Option<&'a hir::Expr<'a>>,
++    /// Whether the interval is open or closed.
++    pub limits: ast::RangeLimits,
++}
++
++/// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
++pub fn range<'a, 'b, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'b hir::Expr<'_>) -> Option<Range<'b>> {
++    /// Finds the field named `name` in the field. Always return `Some` for
++    /// convenience.
++    fn get_field<'c>(name: &str, fields: &'c [hir::Field<'_>]) -> Option<&'c hir::Expr<'c>> {
++        let expr = &fields.iter().find(|field| field.ident.name.as_str() == name)?.expr;
++
++        Some(expr)
++    }
++
++    let def_path = match cx.tables.expr_ty(expr).kind {
++        ty::Adt(def, _) => cx.tcx.def_path(def.did),
++        _ => return None,
++    };
++
++    // sanity checks for std::ops::RangeXXXX
++    if def_path.data.len() != 3 {
++        return None;
++    }
++    if def_path.data.get(0)?.data.as_symbol() != sym!(ops) {
++        return None;
++    }
++    if def_path.data.get(1)?.data.as_symbol() != sym!(range) {
++        return None;
++    }
++    let type_name = def_path.data.get(2)?.data.as_symbol();
++    let range_types = [
++        "RangeFrom",
++        "RangeFull",
++        "RangeInclusive",
++        "Range",
++        "RangeTo",
++        "RangeToInclusive",
++    ];
++    if !range_types.contains(&&*type_name.as_str()) {
++        return None;
++    }
++
++    // The range syntax is expanded to literal paths starting with `core` or `std`
++    // depending on
++    // `#[no_std]`. Testing both instead of resolving the paths.
++
++    match expr.kind {
++        hir::ExprKind::Path(ref path) => {
++            if match_qpath(path, &paths::RANGE_FULL_STD) || match_qpath(path, &paths::RANGE_FULL) {
++                Some(Range {
++                    start: None,
++                    end: None,
++                    limits: ast::RangeLimits::HalfOpen,
++                })
++            } else {
++                None
++            }
++        },
++        hir::ExprKind::Call(ref path, ref args) => {
++            if let hir::ExprKind::Path(ref path) = path.kind {
++                if match_qpath(path, &paths::RANGE_INCLUSIVE_STD_NEW) || match_qpath(path, &paths::RANGE_INCLUSIVE_NEW)
++                {
++                    Some(Range {
++                        start: Some(&args[0]),
++                        end: Some(&args[1]),
++                        limits: ast::RangeLimits::Closed,
++                    })
++                } else {
++                    None
++                }
++            } else {
++                None
++            }
++        },
++        hir::ExprKind::Struct(ref path, ref fields, None) => {
++            if match_qpath(path, &paths::RANGE_FROM_STD) || match_qpath(path, &paths::RANGE_FROM) {
++                Some(Range {
++                    start: Some(get_field("start", fields)?),
++                    end: None,
++                    limits: ast::RangeLimits::HalfOpen,
++                })
++            } else if match_qpath(path, &paths::RANGE_STD) || match_qpath(path, &paths::RANGE) {
++                Some(Range {
++                    start: Some(get_field("start", fields)?),
++                    end: Some(get_field("end", fields)?),
++                    limits: ast::RangeLimits::HalfOpen,
++                })
++            } else if match_qpath(path, &paths::RANGE_TO_INCLUSIVE_STD) || match_qpath(path, &paths::RANGE_TO_INCLUSIVE)
++            {
++                Some(Range {
++                    start: None,
++                    end: Some(get_field("end", fields)?),
++                    limits: ast::RangeLimits::Closed,
++                })
++            } else if match_qpath(path, &paths::RANGE_TO_STD) || match_qpath(path, &paths::RANGE_TO) {
++                Some(Range {
++                    start: None,
++                    end: Some(get_field("end", fields)?),
++                    limits: ast::RangeLimits::HalfOpen,
++                })
++            } else {
++                None
++            }
++        },
++        _ => None,
++    }
++}
++
++/// Checks if a `let` statement is from a `for` loop desugaring.
++pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool {
++    // This will detect plain for-loops without an actual variable binding:
++    //
++    // ```
++    // for x in some_vec {
++    //     // do stuff
++    // }
++    // ```
++    if_chain! {
++        if let Some(ref expr) = local.init;
++        if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind;
++        then {
++            return true;
++        }
++    }
++
++    // This detects a variable binding in for loop to avoid `let_unit_value`
++    // lint (see issue #1964).
++    //
++    // ```
++    // for _ in vec![()] {
++    //     // anything
++    // }
++    // ```
++    if let hir::LocalSource::ForLoopDesugar = local.source {
++        return true;
++    }
++
++    false
++}
++
++/// Recover the essential nodes of a desugared for loop:
++/// `for pat in arg { body }` becomes `(pat, arg, body)`.
++pub fn for_loop<'tcx>(
++    expr: &'tcx hir::Expr<'tcx>,
++) -> Option<(&hir::Pat<'_>, &'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> {
++    if_chain! {
++        if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind;
++        if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind;
++        if iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none();
++        if let hir::ExprKind::Loop(ref block, _, _) = arms[0].body.kind;
++        if block.expr.is_none();
++        if let [ _, _, ref let_stmt, ref body ] = *block.stmts;
++        if let hir::StmtKind::Local(ref local) = let_stmt.kind;
++        if let hir::StmtKind::Expr(ref expr) = body.kind;
++        then {
++            return Some((&*local.pat, &iterargs[0], expr));
++        }
++    }
++    None
++}
++
++/// Recover the essential nodes of a desugared while loop:
++/// `while cond { body }` becomes `(cond, body)`.
++pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> {
++    if_chain! {
++        if let hir::ExprKind::Loop(block, _, hir::LoopSource::While) = &expr.kind;
++        if let hir::Block { expr: Some(expr), .. } = &**block;
++        if let hir::ExprKind::Match(cond, arms, hir::MatchSource::WhileDesugar) = &expr.kind;
++        if let hir::ExprKind::DropTemps(cond) = &cond.kind;
++        if let [arm, ..] = &arms[..];
++        if let hir::Arm { body, .. } = arm;
++        then {
++            return Some((cond, body));
++        }
++    }
++    None
++}
++
++/// Recover the essential nodes of a desugared if block
++/// `if cond { then } else { els }` becomes `(cond, then, Some(els))`
++pub fn if_block<'tcx>(
++    expr: &'tcx hir::Expr<'tcx>,
++) -> Option<(
++    &'tcx hir::Expr<'tcx>,
++    &'tcx hir::Expr<'tcx>,
++    Option<&'tcx hir::Expr<'tcx>>,
++)> {
++    if let hir::ExprKind::Match(ref cond, ref arms, hir::MatchSource::IfDesugar { contains_else_clause }) = expr.kind {
++        let cond = if let hir::ExprKind::DropTemps(ref cond) = cond.kind {
++            cond
++        } else {
++            panic!("If block desugar must contain DropTemps");
++        };
++        let then = &arms[0].body;
++        let els = if contains_else_clause {
++            Some(&*arms[1].body)
++        } else {
++            None
++        };
++        Some((cond, then, els))
++    } else {
++        None
++    }
++}
++
++/// Represent the pre-expansion arguments of a `vec!` invocation.
++pub enum VecArgs<'a> {
++    /// `vec![elem; len]`
++    Repeat(&'a hir::Expr<'a>, &'a hir::Expr<'a>),
++    /// `vec![a, b, c]`
++    Vec(&'a [hir::Expr<'a>]),
++}
++
++/// Returns the arguments of the `vec!` macro if this expression was expanded
++/// from `vec!`.
++pub fn vec_macro<'e>(cx: &LateContext<'_, '_>, expr: &'e hir::Expr<'_>) -> Option<VecArgs<'e>> {
++    if_chain! {
++        if let hir::ExprKind::Call(ref fun, ref args) = expr.kind;
++        if let hir::ExprKind::Path(ref qpath) = fun.kind;
++        if is_expn_of(fun.span, "vec").is_some();
++        if let Some(fun_def_id) = cx.tables.qpath_res(qpath, fun.hir_id).opt_def_id();
++        then {
++            return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 {
++                // `vec![elem; size]` case
++                Some(VecArgs::Repeat(&args[0], &args[1]))
++            }
++            else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 {
++                // `vec![a, b, c]` case
++                if_chain! {
++                    if let hir::ExprKind::Box(ref boxed) = args[0].kind;
++                    if let hir::ExprKind::Array(ref args) = boxed.kind;
++                    then {
++                        return Some(VecArgs::Vec(&*args));
++                    }
++                }
++
++                None
++            }
++            else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() {
++                Some(VecArgs::Vec(&[]))
++            }
++            else {
++                None
++            };
++        }
++    }
++
++    None
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..02b721fd378ff5afb1137f60e16e1e60711e77bc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,694 @@@
++use crate::consts::{constant_context, constant_simple};
++use crate::utils::differing_macro_contexts;
++use rustc_ast::ast::Name;
++use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
++use rustc_hir::{
++    BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FnRetTy, GenericArg,
++    GenericArgs, Guard, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, Ty,
++    TyKind, TypeBinding,
++};
++use rustc_lint::LateContext;
++use rustc_middle::ich::StableHashingContextProvider;
++use rustc_middle::ty::TypeckTables;
++use std::hash::Hash;
++
++/// Type used to check whether two ast are the same. This is different from the
++/// operator
++/// `==` on ast types as this operator would compare true equality with ID and
++/// span.
++///
++/// Note that some expressions kinds are not considered but could be added.
++pub struct SpanlessEq<'a, 'tcx> {
++    /// Context used to evaluate constant expressions.
++    cx: &'a LateContext<'a, 'tcx>,
++    tables: &'a TypeckTables<'tcx>,
++    /// If is true, never consider as equal expressions containing function
++    /// calls.
++    ignore_fn: bool,
++}
++
++impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
++    pub fn new(cx: &'a LateContext<'a, 'tcx>) -> Self {
++        Self {
++            cx,
++            tables: cx.tables,
++            ignore_fn: false,
++        }
++    }
++
++    pub fn ignore_fn(self) -> Self {
++        Self {
++            cx: self.cx,
++            tables: self.cx.tables,
++            ignore_fn: true,
++        }
++    }
++
++    /// Checks whether two statements are the same.
++    pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
++        match (&left.kind, &right.kind) {
++            (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => {
++                self.eq_pat(&l.pat, &r.pat)
++                    && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r))
++                    && both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
++            },
++            (&StmtKind::Expr(ref l), &StmtKind::Expr(ref r)) | (&StmtKind::Semi(ref l), &StmtKind::Semi(ref r)) => {
++                self.eq_expr(l, r)
++            },
++            _ => false,
++        }
++    }
++
++    /// Checks whether two blocks are the same.
++    pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
++        over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r))
++            && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r))
++    }
++
++    #[allow(clippy::similar_names)]
++    pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
++        if self.ignore_fn && differing_macro_contexts(left.span, right.span) {
++            return false;
++        }
++
++        if let (Some(l), Some(r)) = (
++            constant_simple(self.cx, self.tables, left),
++            constant_simple(self.cx, self.tables, right),
++        ) {
++            if l == r {
++                return true;
++            }
++        }
++
++        match (&left.kind, &right.kind) {
++            (&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => {
++                lb == rb && l_mut == r_mut && self.eq_expr(le, re)
++            },
++            (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => {
++                both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str())
++            },
++            (&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => {
++                self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
++            },
++            (&ExprKind::AssignOp(ref lo, ref ll, ref lr), &ExprKind::AssignOp(ref ro, ref rl, ref rr)) => {
++                lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
++            },
++            (&ExprKind::Block(ref l, _), &ExprKind::Block(ref r, _)) => self.eq_block(l, r),
++            (&ExprKind::Binary(l_op, ref ll, ref lr), &ExprKind::Binary(r_op, ref rl, ref rr)) => {
++                l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
++                    || swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| {
++                        l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
++                    })
++            },
++            (&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => {
++                both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str())
++                    && both(le, re, |l, r| self.eq_expr(l, r))
++            },
++            (&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r),
++            (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => {
++                !self.ignore_fn && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
++            },
++            (&ExprKind::Cast(ref lx, ref lt), &ExprKind::Cast(ref rx, ref rt))
++            | (&ExprKind::Type(ref lx, ref lt), &ExprKind::Type(ref rx, ref rt)) => {
++                self.eq_expr(lx, rx) && self.eq_ty(lt, rt)
++            },
++            (&ExprKind::Field(ref l_f_exp, ref l_f_ident), &ExprKind::Field(ref r_f_exp, ref r_f_ident)) => {
++                l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp)
++            },
++            (&ExprKind::Index(ref la, ref li), &ExprKind::Index(ref ra, ref ri)) => {
++                self.eq_expr(la, ra) && self.eq_expr(li, ri)
++            },
++            (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node,
++            (&ExprKind::Loop(ref lb, ref ll, ref lls), &ExprKind::Loop(ref rb, ref rl, ref rls)) => {
++                lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.as_str() == r.ident.as_str())
++            },
++            (&ExprKind::Match(ref le, ref la, ref ls), &ExprKind::Match(ref re, ref ra, ref rs)) => {
++                ls == rs
++                    && self.eq_expr(le, re)
++                    && over(la, ra, |l, r| {
++                        self.eq_expr(&l.body, &r.body)
++                            && both(&l.guard, &r.guard, |l, r| self.eq_guard(l, r))
++                            && self.eq_pat(&l.pat, &r.pat)
++                    })
++            },
++            (&ExprKind::MethodCall(l_path, _, l_args), &ExprKind::MethodCall(r_path, _, r_args)) => {
++                !self.ignore_fn && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args)
++            },
++            (&ExprKind::Repeat(ref le, ref ll_id), &ExprKind::Repeat(ref re, ref rl_id)) => {
++                let mut celcx = constant_context(self.cx, self.cx.tcx.body_tables(ll_id.body));
++                let ll = celcx.expr(&self.cx.tcx.hir().body(ll_id.body).value);
++                let mut celcx = constant_context(self.cx, self.cx.tcx.body_tables(rl_id.body));
++                let rl = celcx.expr(&self.cx.tcx.hir().body(rl_id.body).value);
++
++                self.eq_expr(le, re) && ll == rl
++            },
++            (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)),
++            (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r),
++            (&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))
++            },
++            (&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),
++            (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
++            (&ExprKind::DropTemps(ref le), &ExprKind::DropTemps(ref re)) => self.eq_expr(le, re),
++            _ => false,
++        }
++    }
++
++    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: &Field<'_>, right: &Field<'_>) -> bool {
++        left.ident.name == right.ident.name && self.eq_expr(&left.expr, &right.expr)
++    }
++
++    fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool {
++        match (left, right) {
++            (Guard::If(l), Guard::If(r)) => self.eq_expr(l, r),
++        }
++    }
++
++    fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool {
++        match (left, right) {
++            (GenericArg::Lifetime(l_lt), GenericArg::Lifetime(r_lt)) => Self::eq_lifetime(l_lt, r_lt),
++            (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty, r_ty),
++            _ => false,
++        }
++    }
++
++    fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool {
++        left.name == right.name
++    }
++
++    /// Checks whether two patterns are the same.
++    pub 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::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
++            },
++            (&PatKind::Binding(ref lb, .., ref li, ref lp), &PatKind::Binding(ref rb, .., ref ri, ref rp)) => {
++                lb == rb && li.name.as_str() == ri.name.as_str() && both(lp, rp, |l, r| self.eq_pat(l, r))
++            },
++            (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r),
++            (&PatKind::Lit(ref l), &PatKind::Lit(ref r)) => self.eq_expr(l, r),
++            (&PatKind::Tuple(ref l, ls), &PatKind::Tuple(ref r, rs)) => {
++                ls == rs && over(l, r, |l, r| self.eq_pat(l, r))
++            },
++            (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => {
++                both(ls, rs, |a, b| self.eq_expr(a, b)) && both(le, re, |a, b| self.eq_expr(a, b)) && (li == ri)
++            },
++            (&PatKind::Ref(ref le, ref lm), &PatKind::Ref(ref re, ref rm)) => lm == rm && self.eq_pat(le, re),
++            (&PatKind::Slice(ref ls, ref li, ref le), &PatKind::Slice(ref rs, ref ri, ref re)) => {
++                over(ls, rs, |l, r| self.eq_pat(l, r))
++                    && over(le, re, |l, r| self.eq_pat(l, r))
++                    && both(li, ri, |l, r| self.eq_pat(l, r))
++            },
++            (&PatKind::Wild, &PatKind::Wild) => true,
++            _ => false,
++        }
++    }
++
++    #[allow(clippy::similar_names)]
++    fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool {
++        match (left, right) {
++            (&QPath::Resolved(ref lty, ref lpath), &QPath::Resolved(ref rty, ref rpath)) => {
++                both(lty, rty, |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath)
++            },
++            (&QPath::TypeRelative(ref lty, ref lseg), &QPath::TypeRelative(ref rty, ref rseg)) => {
++                self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
++            },
++            _ => false,
++        }
++    }
++
++    fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
++        left.is_global() == right.is_global()
++            && over(&left.segments, &right.segments, |l, r| self.eq_path_segment(l, r))
++    }
++
++    fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool {
++        if !(left.parenthesized || right.parenthesized) {
++            over(&left.args, &right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work
++                && over(&left.bindings, &right.bindings, |l, r| self.eq_type_binding(l, r))
++        } else if left.parenthesized && right.parenthesized {
++            over(left.inputs(), right.inputs(), |l, r| self.eq_ty(l, r))
++                && both(&Some(&left.bindings[0].ty()), &Some(&right.bindings[0].ty()), |l, r| {
++                    self.eq_ty(l, r)
++                })
++        } else {
++            false
++        }
++    }
++
++    pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
++        left.len() == right.len() && left.iter().zip(right).all(|(l, r)| self.eq_path_segment(l, r))
++    }
++
++    pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
++        // The == of idents doesn't work with different contexts,
++        // we have to be explicit about hygiene
++        if left.ident.as_str() != right.ident.as_str() {
++            return false;
++        }
++        match (&left.args, &right.args) {
++            (&None, &None) => true,
++            (&Some(ref l), &Some(ref r)) => self.eq_path_parameters(l, r),
++            _ => false,
++        }
++    }
++
++    pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
++        self.eq_ty_kind(&left.kind, &right.kind)
++    }
++
++    #[allow(clippy::similar_names)]
++    pub fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool {
++        match (left, right) {
++            (&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 full_table = self.tables;
++
++                let mut celcx = constant_context(self.cx, self.cx.tcx.body_tables(ll_id.body));
++                self.tables = self.cx.tcx.body_tables(ll_id.body);
++                let ll = celcx.expr(&self.cx.tcx.hir().body(ll_id.body).value);
++
++                let mut celcx = constant_context(self.cx, self.cx.tcx.body_tables(rl_id.body));
++                self.tables = self.cx.tcx.body_tables(rl_id.body);
++                let rl = celcx.expr(&self.cx.tcx.hir().body(rl_id.body).value);
++
++                let eq_ty = self.eq_ty(lt, rt);
++                self.tables = full_table;
++                eq_ty && ll == rl
++            },
++            (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => {
++                l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty)
++            },
++            (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => {
++                l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(&*l_rmut.ty, &*r_rmut.ty)
++            },
++            (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r),
++            (&TyKind::Tup(ref l), &TyKind::Tup(ref r)) => over(l, r, |l, r| self.eq_ty(l, r)),
++            (&TyKind::Infer, &TyKind::Infer) => true,
++            _ => false,
++        }
++    }
++
++    fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool {
++        left.ident.name == right.ident.name && self.eq_ty(&left.ty(), &right.ty())
++    }
++}
++
++fn swap_binop<'a>(
++    binop: BinOpKind,
++    lhs: &'a Expr<'a>,
++    rhs: &'a Expr<'a>,
++) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> {
++    match binop {
++        BinOpKind::Add
++        | BinOpKind::Mul
++        | BinOpKind::Eq
++        | BinOpKind::Ne
++        | BinOpKind::BitAnd
++        | BinOpKind::BitXor
++        | BinOpKind::BitOr => Some((binop, rhs, lhs)),
++        BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)),
++        BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)),
++        BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)),
++        BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)),
++        BinOpKind::Shl
++        | BinOpKind::Shr
++        | BinOpKind::Rem
++        | BinOpKind::Sub
++        | BinOpKind::Div
++        | BinOpKind::And
++        | BinOpKind::Or => None,
++    }
++}
++
++/// Checks if the two `Option`s are both `None` or some equal values as per
++/// `eq_fn`.
++fn both<X, F>(l: &Option<X>, r: &Option<X>, mut eq_fn: F) -> bool
++where
++    F: FnMut(&X, &X) -> bool,
++{
++    l.as_ref()
++        .map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y)))
++}
++
++/// Checks if two slices are equal as per `eq_fn`.
++fn over<X, F>(left: &[X], right: &[X], mut eq_fn: F) -> bool
++where
++    F: FnMut(&X, &X) -> bool,
++{
++    left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
++}
++
++/// Type used to hash an ast element. This is different from the `Hash` trait
++/// on ast types as this
++/// trait would consider IDs and spans.
++///
++/// All expressions kind are hashed, but some might have a weaker hash.
++pub struct SpanlessHash<'a, 'tcx> {
++    /// Context used to evaluate constant expressions.
++    cx: &'a LateContext<'a, 'tcx>,
++    tables: &'a TypeckTables<'tcx>,
++    s: StableHasher,
++}
++
++impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
++    pub fn new(cx: &'a LateContext<'a, 'tcx>, tables: &'a TypeckTables<'tcx>) -> Self {
++        Self {
++            cx,
++            tables,
++            s: StableHasher::new(),
++        }
++    }
++
++    pub fn finish(self) -> u64 {
++        self.s.finish()
++    }
++
++    pub fn hash_block(&mut self, b: &Block<'_>) {
++        for s in b.stmts {
++            self.hash_stmt(s);
++        }
++
++        if let Some(ref e) = b.expr {
++            self.hash_expr(e);
++        }
++
++        match b.rules {
++            BlockCheckMode::DefaultBlock => 0,
++            BlockCheckMode::UnsafeBlock(_) => 1,
++            BlockCheckMode::PushUnsafeBlock(_) => 2,
++            BlockCheckMode::PopUnsafeBlock(_) => 3,
++        }
++        .hash(&mut self.s);
++    }
++
++    #[allow(clippy::many_single_char_names, clippy::too_many_lines)]
++    pub fn hash_expr(&mut self, e: &Expr<'_>) {
++        let simple_const = constant_simple(self.cx, self.tables, e);
++
++        // const hashing may result in the same hash as some unrelated node, so add a sort of
++        // discriminant depending on which path we're choosing next
++        simple_const.is_some().hash(&mut self.s);
++
++        if let Some(e) = simple_const {
++            return e.hash(&mut self.s);
++        }
++
++        std::mem::discriminant(&e.kind).hash(&mut self.s);
++
++        match e.kind {
++            ExprKind::AddrOf(kind, m, ref e) => {
++                match kind {
++                    BorrowKind::Ref => 0,
++                    BorrowKind::Raw => 1,
++                }
++                .hash(&mut self.s);
++                m.hash(&mut self.s);
++                self.hash_expr(e);
++            },
++            ExprKind::Continue(i) => {
++                if let Some(i) = i.label {
++                    self.hash_name(i.ident.name);
++                }
++            },
++            ExprKind::Assign(ref l, ref r, _) => {
++                self.hash_expr(l);
++                self.hash_expr(r);
++            },
++            ExprKind::AssignOp(ref o, ref l, ref r) => {
++                o.node
++                    .hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
++                self.hash_expr(l);
++                self.hash_expr(r);
++            },
++            ExprKind::Block(ref b, _) => {
++                self.hash_block(b);
++            },
++            ExprKind::Binary(op, ref l, ref r) => {
++                op.node
++                    .hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
++                self.hash_expr(l);
++                self.hash_expr(r);
++            },
++            ExprKind::Break(i, ref j) => {
++                if let Some(i) = i.label {
++                    self.hash_name(i.ident.name);
++                }
++                if let Some(ref j) = *j {
++                    self.hash_expr(&*j);
++                }
++            },
++            ExprKind::Box(ref e) | ExprKind::DropTemps(ref e) | ExprKind::Yield(ref e, _) => {
++                self.hash_expr(e);
++            },
++            ExprKind::Call(ref fun, args) => {
++                self.hash_expr(fun);
++                self.hash_exprs(args);
++            },
++            ExprKind::Cast(ref e, ref ty) | ExprKind::Type(ref e, ref ty) => {
++                self.hash_expr(e);
++                self.hash_ty(ty);
++            },
++            ExprKind::Closure(cap, _, eid, _, _) => {
++                match cap {
++                    CaptureBy::Value => 0,
++                    CaptureBy::Ref => 1,
++                }
++                .hash(&mut self.s);
++                // closures inherit TypeckTables
++                self.hash_expr(&self.cx.tcx.hir().body(eid).value);
++            },
++            ExprKind::Field(ref e, ref f) => {
++                self.hash_expr(e);
++                self.hash_name(f.name);
++            },
++            ExprKind::Index(ref a, ref i) => {
++                self.hash_expr(a);
++                self.hash_expr(i);
++            },
++            ExprKind::LlvmInlineAsm(..) | ExprKind::Err => {},
++            ExprKind::Lit(ref l) => {
++                l.node.hash(&mut self.s);
++            },
++            ExprKind::Loop(ref b, ref i, _) => {
++                self.hash_block(b);
++                if let Some(i) = *i {
++                    self.hash_name(i.ident.name);
++                }
++            },
++            ExprKind::Match(ref e, arms, ref s) => {
++                self.hash_expr(e);
++
++                for arm in arms {
++                    // TODO: arm.pat?
++                    if let Some(ref e) = arm.guard {
++                        self.hash_guard(e);
++                    }
++                    self.hash_expr(&arm.body);
++                }
++
++                s.hash(&mut self.s);
++            },
++            ExprKind::MethodCall(ref path, ref _tys, args) => {
++                self.hash_name(path.ident.name);
++                self.hash_exprs(args);
++            },
++            ExprKind::Repeat(ref e, ref l_id) => {
++                self.hash_expr(e);
++                self.hash_body(l_id.body);
++            },
++            ExprKind::Ret(ref e) => {
++                if let Some(ref e) = *e {
++                    self.hash_expr(e);
++                }
++            },
++            ExprKind::Path(ref qpath) => {
++                self.hash_qpath(qpath);
++            },
++            ExprKind::Struct(ref path, fields, ref expr) => {
++                self.hash_qpath(path);
++
++                for f in fields {
++                    self.hash_name(f.ident.name);
++                    self.hash_expr(&f.expr);
++                }
++
++                if let Some(ref e) = *expr {
++                    self.hash_expr(e);
++                }
++            },
++            ExprKind::Tup(tup) => {
++                self.hash_exprs(tup);
++            },
++            ExprKind::Array(v) => {
++                self.hash_exprs(v);
++            },
++            ExprKind::Unary(lop, ref le) => {
++                lop.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
++                self.hash_expr(le);
++            },
++        }
++    }
++
++    pub fn hash_exprs(&mut self, e: &[Expr<'_>]) {
++        for e in e {
++            self.hash_expr(e);
++        }
++    }
++
++    pub fn hash_name(&mut self, n: Name) {
++        n.as_str().hash(&mut self.s);
++    }
++
++    pub fn hash_qpath(&mut self, p: &QPath<'_>) {
++        match *p {
++            QPath::Resolved(_, ref path) => {
++                self.hash_path(path);
++            },
++            QPath::TypeRelative(_, ref path) => {
++                self.hash_name(path.ident.name);
++            },
++        }
++        // self.cx.tables.qpath_res(p, id).hash(&mut self.s);
++    }
++
++    pub fn hash_path(&mut self, p: &Path<'_>) {
++        p.is_global().hash(&mut self.s);
++        for p in p.segments {
++            self.hash_name(p.ident.name);
++        }
++    }
++
++    pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
++        std::mem::discriminant(&b.kind).hash(&mut self.s);
++
++        match &b.kind {
++            StmtKind::Local(local) => {
++                if let Some(ref init) = local.init {
++                    self.hash_expr(init);
++                }
++            },
++            StmtKind::Item(..) => {},
++            StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
++                self.hash_expr(expr);
++            },
++        }
++    }
++
++    pub fn hash_guard(&mut self, g: &Guard<'_>) {
++        match g {
++            Guard::If(ref expr) => {
++                self.hash_expr(expr);
++            },
++        }
++    }
++
++    pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
++        std::mem::discriminant(&lifetime.name).hash(&mut self.s);
++        if let LifetimeName::Param(ref name) = lifetime.name {
++            std::mem::discriminant(name).hash(&mut self.s);
++            match name {
++                ParamName::Plain(ref ident) => {
++                    ident.name.hash(&mut self.s);
++                },
++                ParamName::Fresh(ref size) => {
++                    size.hash(&mut self.s);
++                },
++                ParamName::Error => {},
++            }
++        }
++    }
++
++    pub fn hash_ty(&mut self, ty: &Ty<'_>) {
++        self.hash_tykind(&ty.kind);
++    }
++
++    pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
++        std::mem::discriminant(ty).hash(&mut self.s);
++        match ty {
++            TyKind::Slice(ty) => {
++                self.hash_ty(ty);
++            },
++            TyKind::Array(ty, anon_const) => {
++                self.hash_ty(ty);
++                self.hash_body(anon_const.body);
++            },
++            TyKind::Ptr(mut_ty) => {
++                self.hash_ty(&mut_ty.ty);
++                mut_ty.mutbl.hash(&mut self.s);
++            },
++            TyKind::Rptr(lifetime, mut_ty) => {
++                self.hash_lifetime(lifetime);
++                self.hash_ty(&mut_ty.ty);
++                mut_ty.mutbl.hash(&mut self.s);
++            },
++            TyKind::BareFn(bfn) => {
++                bfn.unsafety.hash(&mut self.s);
++                bfn.abi.hash(&mut self.s);
++                for arg in bfn.decl.inputs {
++                    self.hash_ty(&arg);
++                }
++                match bfn.decl.output {
++                    FnRetTy::DefaultReturn(_) => {
++                        ().hash(&mut self.s);
++                    },
++                    FnRetTy::Return(ref ty) => {
++                        self.hash_ty(ty);
++                    },
++                }
++                bfn.decl.c_variadic.hash(&mut self.s);
++            },
++            TyKind::Tup(ty_list) => {
++                for ty in *ty_list {
++                    self.hash_ty(ty);
++                }
++            },
++            TyKind::Path(qpath) => match qpath {
++                QPath::Resolved(ref maybe_ty, ref path) => {
++                    if let Some(ref ty) = maybe_ty {
++                        self.hash_ty(ty);
++                    }
++                    for segment in path.segments {
++                        segment.ident.name.hash(&mut self.s);
++                    }
++                },
++                QPath::TypeRelative(ref ty, ref segment) => {
++                    self.hash_ty(ty);
++                    segment.ident.name.hash(&mut self.s);
++                },
++            },
++            TyKind::Def(_, arg_list) => {
++                for arg in *arg_list {
++                    match arg {
++                        GenericArg::Lifetime(ref l) => self.hash_lifetime(l),
++                        GenericArg::Type(ref ty) => self.hash_ty(&ty),
++                        GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
++                    }
++                }
++            },
++            TyKind::TraitObject(_, lifetime) => {
++                self.hash_lifetime(lifetime);
++            },
++            TyKind::Typeof(anon_const) => {
++                self.hash_body(anon_const.body);
++            },
++            TyKind::Err | TyKind::Infer | TyKind::Never => {},
++        }
++    }
++
++    pub fn hash_body(&mut self, body_id: BodyId) {
++        // swap out TypeckTables when hashing a body
++        let old_tables = self.tables;
++        self.tables = self.cx.tcx.body_tables(body_id);
++        self.hash_expr(&self.cx.tcx.hir().body(body_id).value);
++        self.tables = old_tables;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7e8c61ba24a22a5c5aa0d812f75b080ec0b50830
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,525 @@@
++//! checks for attributes
++
++use crate::utils::get_attr;
++use rustc_ast::ast::Attribute;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_session::Session;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Dumps every ast/hir node which has the `#[clippy::dump]`
++    /// attribute
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// #[clippy::dump]
++    /// extern crate foo;
++    /// ```
++    ///
++    /// prints
++    ///
++    /// ```text
++    /// item `foo`
++    /// visibility inherited from outer item
++    /// extern crate dylib source: "/path/to/foo.so"
++    /// ```
++    pub DEEP_CODE_INSPECTION,
++    internal_warn,
++    "helper to dump info about code"
++}
++
++declare_lint_pass!(DeepCodeInspector => [DEEP_CODE_INSPECTION]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DeepCodeInspector {
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>) {
++        if !has_attr(cx.sess(), &item.attrs) {
++            return;
++        }
++        print_item(cx, item);
++    }
++
++    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem<'_>) {
++        if !has_attr(cx.sess(), &item.attrs) {
++            return;
++        }
++        println!("impl item `{}`", item.ident.name);
++        match item.vis.node {
++            hir::VisibilityKind::Public => println!("public"),
++            hir::VisibilityKind::Crate(_) => println!("visible crate wide"),
++            hir::VisibilityKind::Restricted { ref path, .. } => println!(
++                "visible in module `{}`",
++                rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false))
++            ),
++            hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"),
++        }
++        if item.defaultness.is_default() {
++            println!("default");
++        }
++        match item.kind {
++            hir::ImplItemKind::Const(_, body_id) => {
++                println!("associated constant");
++                print_expr(cx, &cx.tcx.hir().body(body_id).value, 1);
++            },
++            hir::ImplItemKind::Fn(..) => println!("method"),
++            hir::ImplItemKind::TyAlias(_) => println!("associated type"),
++            hir::ImplItemKind::OpaqueTy(_) => println!("existential type"),
++        }
++    }
++    // fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx
++    // hir::TraitItem) {
++    // if !has_attr(&item.attrs) {
++    // return;
++    // }
++    // }
++    //
++    // fn check_variant(&mut self, cx: &LateContext<'a, 'tcx>, var: &'tcx
++    // hir::Variant, _:
++    // &hir::Generics) {
++    // if !has_attr(&var.node.attrs) {
++    // return;
++    // }
++    // }
++    //
++    // fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, field: &'tcx
++    // hir::StructField) {
++    // if !has_attr(&field.attrs) {
++    // return;
++    // }
++    // }
++    //
++
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if !has_attr(cx.sess(), &expr.attrs) {
++            return;
++        }
++        print_expr(cx, expr, 0);
++    }
++
++    fn check_arm(&mut self, cx: &LateContext<'a, 'tcx>, arm: &'tcx hir::Arm<'_>) {
++        if !has_attr(cx.sess(), &arm.attrs) {
++            return;
++        }
++        print_pat(cx, &arm.pat, 1);
++        if let Some(ref guard) = arm.guard {
++            println!("guard:");
++            print_guard(cx, guard, 1);
++        }
++        println!("body:");
++        print_expr(cx, &arm.body, 1);
++    }
++
++    fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx hir::Stmt<'_>) {
++        if !has_attr(cx.sess(), stmt.kind.attrs()) {
++            return;
++        }
++        match stmt.kind {
++            hir::StmtKind::Local(ref local) => {
++                println!("local variable of type {}", cx.tables.node_type(local.hir_id));
++                println!("pattern:");
++                print_pat(cx, &local.pat, 0);
++                if let Some(ref e) = local.init {
++                    println!("init expression:");
++                    print_expr(cx, e, 0);
++                }
++            },
++            hir::StmtKind::Item(_) => println!("item decl"),
++            hir::StmtKind::Expr(ref e) | hir::StmtKind::Semi(ref e) => print_expr(cx, e, 0),
++        }
++    }
++    // fn check_foreign_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx
++    // hir::ForeignItem) {
++    // if !has_attr(&item.attrs) {
++    // return;
++    // }
++    // }
++    //
++}
++
++fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool {
++    get_attr(sess, attrs, "dump").count() > 0
++}
++
++#[allow(clippy::similar_names)]
++#[allow(clippy::too_many_lines)]
++fn print_expr(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, indent: usize) {
++    let ind = "  ".repeat(indent);
++    println!("{}+", ind);
++    println!("{}ty: {}", ind, cx.tables.expr_ty(expr));
++    println!("{}adjustments: {:?}", ind, cx.tables.adjustments().get(expr.hir_id));
++    match expr.kind {
++        hir::ExprKind::Box(ref e) => {
++            println!("{}Box", ind);
++            print_expr(cx, e, indent + 1);
++        },
++        hir::ExprKind::Array(v) => {
++            println!("{}Array", ind);
++            for e in v {
++                print_expr(cx, e, indent + 1);
++            }
++        },
++        hir::ExprKind::Call(ref func, args) => {
++            println!("{}Call", ind);
++            println!("{}function:", ind);
++            print_expr(cx, func, indent + 1);
++            println!("{}arguments:", ind);
++            for arg in args {
++                print_expr(cx, arg, indent + 1);
++            }
++        },
++        hir::ExprKind::MethodCall(ref path, _, args) => {
++            println!("{}MethodCall", ind);
++            println!("{}method name: {}", ind, path.ident.name);
++            for arg in args {
++                print_expr(cx, arg, indent + 1);
++            }
++        },
++        hir::ExprKind::Tup(v) => {
++            println!("{}Tup", ind);
++            for e in v {
++                print_expr(cx, e, indent + 1);
++            }
++        },
++        hir::ExprKind::Binary(op, ref lhs, ref rhs) => {
++            println!("{}Binary", ind);
++            println!("{}op: {:?}", ind, op.node);
++            println!("{}lhs:", ind);
++            print_expr(cx, lhs, indent + 1);
++            println!("{}rhs:", ind);
++            print_expr(cx, rhs, indent + 1);
++        },
++        hir::ExprKind::Unary(op, ref inner) => {
++            println!("{}Unary", ind);
++            println!("{}op: {:?}", ind, op);
++            print_expr(cx, inner, indent + 1);
++        },
++        hir::ExprKind::Lit(ref lit) => {
++            println!("{}Lit", ind);
++            println!("{}{:?}", ind, lit);
++        },
++        hir::ExprKind::Cast(ref e, ref target) => {
++            println!("{}Cast", ind);
++            print_expr(cx, e, indent + 1);
++            println!("{}target type: {:?}", ind, target);
++        },
++        hir::ExprKind::Type(ref e, ref target) => {
++            println!("{}Type", ind);
++            print_expr(cx, e, indent + 1);
++            println!("{}target type: {:?}", ind, target);
++        },
++        hir::ExprKind::Loop(..) => {
++            println!("{}Loop", ind);
++        },
++        hir::ExprKind::Match(ref cond, _, ref source) => {
++            println!("{}Match", ind);
++            println!("{}condition:", ind);
++            print_expr(cx, cond, indent + 1);
++            println!("{}source: {:?}", ind, source);
++        },
++        hir::ExprKind::Closure(ref clause, _, _, _, _) => {
++            println!("{}Closure", ind);
++            println!("{}clause: {:?}", ind, clause);
++        },
++        hir::ExprKind::Yield(ref sub, _) => {
++            println!("{}Yield", ind);
++            print_expr(cx, sub, indent + 1);
++        },
++        hir::ExprKind::Block(_, _) => {
++            println!("{}Block", ind);
++        },
++        hir::ExprKind::Assign(ref lhs, ref rhs, _) => {
++            println!("{}Assign", ind);
++            println!("{}lhs:", ind);
++            print_expr(cx, lhs, indent + 1);
++            println!("{}rhs:", ind);
++            print_expr(cx, rhs, indent + 1);
++        },
++        hir::ExprKind::AssignOp(ref binop, ref lhs, ref rhs) => {
++            println!("{}AssignOp", ind);
++            println!("{}op: {:?}", ind, binop.node);
++            println!("{}lhs:", ind);
++            print_expr(cx, lhs, indent + 1);
++            println!("{}rhs:", ind);
++            print_expr(cx, rhs, indent + 1);
++        },
++        hir::ExprKind::Field(ref e, ident) => {
++            println!("{}Field", ind);
++            println!("{}field name: {}", ind, ident.name);
++            println!("{}struct expr:", ind);
++            print_expr(cx, e, indent + 1);
++        },
++        hir::ExprKind::Index(ref arr, ref idx) => {
++            println!("{}Index", ind);
++            println!("{}array expr:", ind);
++            print_expr(cx, arr, indent + 1);
++            println!("{}index expr:", ind);
++            print_expr(cx, idx, indent + 1);
++        },
++        hir::ExprKind::Path(hir::QPath::Resolved(ref ty, ref path)) => {
++            println!("{}Resolved Path, {:?}", ind, ty);
++            println!("{}path: {:?}", ind, path);
++        },
++        hir::ExprKind::Path(hir::QPath::TypeRelative(ref ty, ref seg)) => {
++            println!("{}Relative Path, {:?}", ind, ty);
++            println!("{}seg: {:?}", ind, seg);
++        },
++        hir::ExprKind::AddrOf(kind, ref muta, ref e) => {
++            println!("{}AddrOf", ind);
++            println!("kind: {:?}", kind);
++            println!("mutability: {:?}", muta);
++            print_expr(cx, e, indent + 1);
++        },
++        hir::ExprKind::Break(_, ref e) => {
++            println!("{}Break", ind);
++            if let Some(ref e) = *e {
++                print_expr(cx, e, indent + 1);
++            }
++        },
++        hir::ExprKind::Continue(_) => println!("{}Again", ind),
++        hir::ExprKind::Ret(ref e) => {
++            println!("{}Ret", ind);
++            if let Some(ref e) = *e {
++                print_expr(cx, e, indent + 1);
++            }
++        },
++        hir::ExprKind::LlvmInlineAsm(ref asm) => {
++            let inputs = &asm.inputs_exprs;
++            let outputs = &asm.outputs_exprs;
++            println!("{}LlvmInlineAsm", ind);
++            println!("{}inputs:", ind);
++            for e in inputs.iter() {
++                print_expr(cx, e, indent + 1);
++            }
++            println!("{}outputs:", ind);
++            for e in outputs.iter() {
++                print_expr(cx, e, indent + 1);
++            }
++        },
++        hir::ExprKind::Struct(ref path, fields, ref base) => {
++            println!("{}Struct", ind);
++            println!("{}path: {:?}", ind, path);
++            for field in fields {
++                println!("{}field \"{}\":", ind, field.ident.name);
++                print_expr(cx, &field.expr, indent + 1);
++            }
++            if let Some(ref base) = *base {
++                println!("{}base:", ind);
++                print_expr(cx, base, indent + 1);
++            }
++        },
++        hir::ExprKind::Repeat(ref val, ref anon_const) => {
++            println!("{}Repeat", ind);
++            println!("{}value:", ind);
++            print_expr(cx, val, indent + 1);
++            println!("{}repeat count:", ind);
++            print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
++        },
++        hir::ExprKind::Err => {
++            println!("{}Err", ind);
++        },
++        hir::ExprKind::DropTemps(ref e) => {
++            println!("{}DropTemps", ind);
++            print_expr(cx, e, indent + 1);
++        },
++    }
++}
++
++fn print_item(cx: &LateContext<'_, '_>, item: &hir::Item<'_>) {
++    let did = cx.tcx.hir().local_def_id(item.hir_id);
++    println!("item `{}`", item.ident.name);
++    match item.vis.node {
++        hir::VisibilityKind::Public => println!("public"),
++        hir::VisibilityKind::Crate(_) => println!("visible crate wide"),
++        hir::VisibilityKind::Restricted { ref path, .. } => println!(
++            "visible in module `{}`",
++            rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false))
++        ),
++        hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"),
++    }
++    match item.kind {
++        hir::ItemKind::ExternCrate(ref _renamed_from) => {
++            let def_id = cx.tcx.hir().local_def_id(item.hir_id);
++            if let Some(crate_id) = cx.tcx.extern_mod_stmt_cnum(def_id) {
++                let source = cx.tcx.used_crate_source(crate_id);
++                if let Some(ref src) = source.dylib {
++                    println!("extern crate dylib source: {:?}", src.0);
++                }
++                if let Some(ref src) = source.rlib {
++                    println!("extern crate rlib source: {:?}", src.0);
++                }
++            } else {
++                println!("weird extern crate without a crate id");
++            }
++        },
++        hir::ItemKind::Use(ref path, ref kind) => println!("{:?}, {:?}", path, kind),
++        hir::ItemKind::Static(..) => println!("static item of type {:#?}", cx.tcx.type_of(did)),
++        hir::ItemKind::Const(..) => println!("const item of type {:#?}", cx.tcx.type_of(did)),
++        hir::ItemKind::Fn(..) => {
++            let item_ty = cx.tcx.type_of(did);
++            println!("function of type {:#?}", item_ty);
++        },
++        hir::ItemKind::Mod(..) => println!("module"),
++        hir::ItemKind::ForeignMod(ref fm) => println!("foreign module with abi: {}", fm.abi),
++        hir::ItemKind::GlobalAsm(ref asm) => println!("global asm: {:?}", asm),
++        hir::ItemKind::TyAlias(..) => {
++            println!("type alias for {:?}", cx.tcx.type_of(did));
++        },
++        hir::ItemKind::OpaqueTy(..) => {
++            println!("existential type with real type {:?}", cx.tcx.type_of(did));
++        },
++        hir::ItemKind::Enum(..) => {
++            println!("enum definition of type {:?}", cx.tcx.type_of(did));
++        },
++        hir::ItemKind::Struct(..) => {
++            println!("struct definition of type {:?}", cx.tcx.type_of(did));
++        },
++        hir::ItemKind::Union(..) => {
++            println!("union definition of type {:?}", cx.tcx.type_of(did));
++        },
++        hir::ItemKind::Trait(..) => {
++            println!("trait decl");
++            if cx.tcx.trait_is_auto(did.to_def_id()) {
++                println!("trait is auto");
++            } else {
++                println!("trait is not auto");
++            }
++        },
++        hir::ItemKind::TraitAlias(..) => {
++            println!("trait alias");
++        },
++        hir::ItemKind::Impl {
++            of_trait: Some(ref _trait_ref),
++            ..
++        } => {
++            println!("trait impl");
++        },
++        hir::ItemKind::Impl { of_trait: None, .. } => {
++            println!("impl");
++        },
++    }
++}
++
++#[allow(clippy::similar_names)]
++#[allow(clippy::too_many_lines)]
++fn print_pat(cx: &LateContext<'_, '_>, pat: &hir::Pat<'_>, indent: usize) {
++    let ind = "  ".repeat(indent);
++    println!("{}+", ind);
++    match pat.kind {
++        hir::PatKind::Wild => println!("{}Wild", ind),
++        hir::PatKind::Binding(ref mode, .., ident, ref inner) => {
++            println!("{}Binding", ind);
++            println!("{}mode: {:?}", ind, mode);
++            println!("{}name: {}", ind, ident.name);
++            if let Some(ref inner) = *inner {
++                println!("{}inner:", ind);
++                print_pat(cx, inner, indent + 1);
++            }
++        },
++        hir::PatKind::Or(fields) => {
++            println!("{}Or", ind);
++            for field in fields {
++                print_pat(cx, field, indent + 1);
++            }
++        },
++        hir::PatKind::Struct(ref path, fields, ignore) => {
++            println!("{}Struct", ind);
++            println!(
++                "{}name: {}",
++                ind,
++                rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
++            );
++            println!("{}ignore leftover fields: {}", ind, ignore);
++            println!("{}fields:", ind);
++            for field in fields {
++                println!("{}  field name: {}", ind, field.ident.name);
++                if field.is_shorthand {
++                    println!("{}  in shorthand notation", ind);
++                }
++                print_pat(cx, &field.pat, indent + 1);
++            }
++        },
++        hir::PatKind::TupleStruct(ref path, fields, opt_dots_position) => {
++            println!("{}TupleStruct", ind);
++            println!(
++                "{}path: {}",
++                ind,
++                rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
++            );
++            if let Some(dot_position) = opt_dots_position {
++                println!("{}dot position: {}", ind, dot_position);
++            }
++            for field in fields {
++                print_pat(cx, field, indent + 1);
++            }
++        },
++        hir::PatKind::Path(hir::QPath::Resolved(ref ty, ref path)) => {
++            println!("{}Resolved Path, {:?}", ind, ty);
++            println!("{}path: {:?}", ind, path);
++        },
++        hir::PatKind::Path(hir::QPath::TypeRelative(ref ty, ref seg)) => {
++            println!("{}Relative Path, {:?}", ind, ty);
++            println!("{}seg: {:?}", ind, seg);
++        },
++        hir::PatKind::Tuple(pats, opt_dots_position) => {
++            println!("{}Tuple", ind);
++            if let Some(dot_position) = opt_dots_position {
++                println!("{}dot position: {}", ind, dot_position);
++            }
++            for field in pats {
++                print_pat(cx, field, indent + 1);
++            }
++        },
++        hir::PatKind::Box(ref inner) => {
++            println!("{}Box", ind);
++            print_pat(cx, inner, indent + 1);
++        },
++        hir::PatKind::Ref(ref inner, ref muta) => {
++            println!("{}Ref", ind);
++            println!("{}mutability: {:?}", ind, muta);
++            print_pat(cx, inner, indent + 1);
++        },
++        hir::PatKind::Lit(ref e) => {
++            println!("{}Lit", ind);
++            print_expr(cx, e, indent + 1);
++        },
++        hir::PatKind::Range(ref l, ref r, ref range_end) => {
++            println!("{}Range", ind);
++            if let Some(expr) = l {
++                print_expr(cx, expr, indent + 1);
++            }
++            if let Some(expr) = r {
++                print_expr(cx, expr, indent + 1);
++            }
++            match *range_end {
++                hir::RangeEnd::Included => println!("{} end included", ind),
++                hir::RangeEnd::Excluded => println!("{} end excluded", ind),
++            }
++        },
++        hir::PatKind::Slice(first_pats, ref range, last_pats) => {
++            println!("{}Slice [a, b, ..i, y, z]", ind);
++            println!("[a, b]:");
++            for pat in first_pats {
++                print_pat(cx, pat, indent + 1);
++            }
++            println!("i:");
++            if let Some(ref pat) = *range {
++                print_pat(cx, pat, indent + 1);
++            }
++            println!("[y, z]:");
++            for pat in last_pats {
++                print_pat(cx, pat, indent + 1);
++            }
++        },
++    }
++}
++
++fn print_guard(cx: &LateContext<'_, '_>, guard: &hir::Guard<'_>, indent: usize) {
++    let ind = "  ".repeat(indent);
++    println!("{}+", ind);
++    match guard {
++        hir::Guard::If(expr) => {
++            println!("{}If", ind);
++            print_expr(cx, expr, indent + 1);
++        },
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5bf9acdc5f7ce21e25144e2aa0d3b6cc3055ff58
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,658 @@@
++use crate::utils::SpanlessEq;
++use crate::utils::{
++    is_expn_of, match_def_path, match_qpath, match_type, method_calls, paths, run_lints, snippet, span_lint,
++    span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty,
++};
++use if_chain::if_chain;
++use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, Name, NodeId};
++use rustc_ast::visit::FnKind;
++use rustc_data_structures::fx::{FxHashMap, FxHashSet};
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::hir_id::CRATE_HIR_ID;
++use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
++use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Path, StmtKind, Ty, TyKind};
++use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
++use rustc_span::source_map::{Span, Spanned};
++use rustc_span::symbol::SymbolStr;
++
++use std::borrow::{Borrow, Cow};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for various things we like to keep tidy in clippy.
++    ///
++    /// **Why is this bad?** We like to pretend we're an example of tidy code.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:** Wrong ordering of the util::paths constants.
++    pub CLIPPY_LINTS_INTERNAL,
++    internal,
++    "various things that will negatively affect your clippy experience"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Ensures every lint is associated to a `LintPass`.
++    ///
++    /// **Why is this bad?** The compiler only knows lints via a `LintPass`. Without
++    /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
++    /// know the name of the lint.
++    ///
++    /// **Known problems:** Only checks for lints associated using the
++    /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// declare_lint! { pub LINT_1, ... }
++    /// declare_lint! { pub LINT_2, ... }
++    /// declare_lint! { pub FORGOTTEN_LINT, ... }
++    /// // ...
++    /// declare_lint_pass!(Pass => [LINT_1, LINT_2]);
++    /// // missing FORGOTTEN_LINT
++    /// ```
++    pub LINT_WITHOUT_LINT_PASS,
++    internal,
++    "declaring a lint without associating it in a LintPass"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for calls to `cx.span_lint*` and suggests to use the `utils::*`
++    /// variant of the function.
++    ///
++    /// **Why is this bad?** The `utils::*` variants also add a link to the Clippy documentation to the
++    /// warning/error messages.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// Bad:
++    /// ```rust,ignore
++    /// cx.span_lint(LINT_NAME, "message");
++    /// ```
++    ///
++    /// Good:
++    /// ```rust,ignore
++    /// utils::span_lint(cx, LINT_NAME, "message");
++    /// ```
++    pub COMPILER_LINT_FUNCTIONS,
++    internal,
++    "usage of the lint functions of the compiler instead of the utils::* variant"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for calls to `cx.outer().expn_data()` and suggests to use
++    /// the `cx.outer_expn_data()`
++    ///
++    /// **Why is this bad?** `cx.outer_expn_data()` is faster and more concise.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// Bad:
++    /// ```rust,ignore
++    /// expr.span.ctxt().outer().expn_data()
++    /// ```
++    ///
++    /// Good:
++    /// ```rust,ignore
++    /// expr.span.ctxt().outer_expn_data()
++    /// ```
++    pub OUTER_EXPN_EXPN_DATA,
++    internal,
++    "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Not an actual lint. This lint is only meant for testing our customized internal compiler
++    /// error message by calling `panic`.
++    ///
++    /// **Why is this bad?** ICE in large quantities can damage your teeth
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// Bad:
++    /// ```rust,ignore
++    /// 🍦🍦🍦🍦🍦
++    /// ```
++    pub PRODUCE_ICE,
++    internal,
++    "this message should not appear anywhere as we ICE before and don't emit the lint"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for cases of an auto-generated lint without an updated description,
++    /// i.e. `default lint description`.
++    ///
++    /// **Why is this bad?** Indicates that the lint is not finished.
++    ///
++    /// **Known problems:** None
++    ///
++    /// **Example:**
++    /// Bad:
++    /// ```rust,ignore
++    /// declare_lint! { pub COOL_LINT, nursery, "default lint description" }
++    /// ```
++    ///
++    /// Good:
++    /// ```rust,ignore
++    /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" }
++    /// ```
++    pub DEFAULT_LINT,
++    internal,
++    "found 'default lint description' in a lint declaration"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Lints `span_lint_and_then` function calls, where the
++    /// closure argument has only one statement and that statement is a method
++    /// call to `span_suggestion`, `span_help`, `span_note` (using the same
++    /// span), `help` or `note`.
++    ///
++    /// These usages of `span_lint_and_then` should be replaced with one of the
++    /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or
++    /// `span_lint_and_note`.
++    ///
++    /// **Why is this bad?** Using the wrapper `span_lint_and_*` functions, is more
++    /// convenient, readable and less error prone.
++    ///
++    /// **Known problems:** None
++    ///
++    /// *Example:**
++    /// Bad:
++    /// ```rust,ignore
++    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
++    ///     diag.span_suggestion(
++    ///         expr.span,
++    ///         help_msg,
++    ///         sugg.to_string(),
++    ///         Applicability::MachineApplicable,
++    ///     );
++    /// });
++    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
++    ///     diag.span_help(expr.span, help_msg);
++    /// });
++    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
++    ///     diag.help(help_msg);
++    /// });
++    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
++    ///     diag.span_note(expr.span, note_msg);
++    /// });
++    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
++    ///     diag.note(note_msg);
++    /// });
++    /// ```
++    ///
++    /// Good:
++    /// ```rust,ignore
++    /// span_lint_and_sugg(
++    ///     cx,
++    ///     TEST_LINT,
++    ///     expr.span,
++    ///     lint_msg,
++    ///     help_msg,
++    ///     sugg.to_string(),
++    ///     Applicability::MachineApplicable,
++    /// );
++    /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg);
++    /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg);
++    /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg);
++    /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg);
++    /// ```
++    pub COLLAPSIBLE_SPAN_LINT_CALLS,
++    internal,
++    "found collapsible `span_lint_and_then` calls"
++}
++
++declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
++
++impl EarlyLintPass for ClippyLintsInternal {
++    fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &AstCrate) {
++        if let Some(utils) = krate
++            .module
++            .items
++            .iter()
++            .find(|item| item.ident.name.as_str() == "utils")
++        {
++            if let ItemKind::Mod(ref utils_mod) = utils.kind {
++                if let Some(paths) = utils_mod.items.iter().find(|item| item.ident.name.as_str() == "paths") {
++                    if let ItemKind::Mod(ref paths_mod) = paths.kind {
++                        let mut last_name: Option<SymbolStr> = None;
++                        for item in &*paths_mod.items {
++                            let name = item.ident.as_str();
++                            if let Some(ref last_name) = last_name {
++                                if **last_name > *name {
++                                    span_lint(
++                                        cx,
++                                        CLIPPY_LINTS_INTERNAL,
++                                        item.span,
++                                        "this constant should be before the previous constant due to lexical \
++                                         ordering",
++                                    );
++                                }
++                            }
++                            last_name = Some(name);
++                        }
++                    }
++                }
++            }
++        }
++    }
++}
++
++#[derive(Clone, Debug, Default)]
++pub struct LintWithoutLintPass {
++    declared_lints: FxHashMap<Name, Span>,
++    registered_lints: FxHashSet<Name>,
++}
++
++impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LintWithoutLintPass {
++    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
++        if !run_lints(cx, &[DEFAULT_LINT], item.hir_id) {
++            return;
++        }
++
++        if let hir::ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind {
++            if is_lint_ref_type(cx, ty) {
++                let expr = &cx.tcx.hir().body(body_id).value;
++                if_chain! {
++                    if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind;
++                    if let ExprKind::Struct(_, ref fields, _) = inner_exp.kind;
++                    let field = fields
++                        .iter()
++                        .find(|f| f.ident.as_str() == "desc")
++                        .expect("lints must have a description field");
++                    if let ExprKind::Lit(Spanned {
++                        node: LitKind::Str(ref sym, _),
++                        ..
++                    }) = field.expr.kind;
++                    if sym.as_str() == "default lint description";
++
++                    then {
++                        span_lint(
++                            cx,
++                            DEFAULT_LINT,
++                            item.span,
++                            &format!("the lint `{}` has the default lint description", item.ident.name),
++                        );
++                    }
++                }
++                self.declared_lints.insert(item.ident.name, item.span);
++            }
++        } else if is_expn_of(item.span, "impl_lint_pass").is_some()
++            || is_expn_of(item.span, "declare_lint_pass").is_some()
++        {
++            if let hir::ItemKind::Impl {
++                of_trait: None,
++                items: ref impl_item_refs,
++                ..
++            } = item.kind
++            {
++                let mut collector = LintCollector {
++                    output: &mut self.registered_lints,
++                    cx,
++                };
++                let body_id = cx.tcx.hir().body_owned_by(
++                    impl_item_refs
++                        .iter()
++                        .find(|iiref| iiref.ident.as_str() == "get_lints")
++                        .expect("LintPass needs to implement get_lints")
++                        .id
++                        .hir_id,
++                );
++                collector.visit_expr(&cx.tcx.hir().body(body_id).value);
++            }
++        }
++    }
++
++    fn check_crate_post(&mut self, cx: &LateContext<'a, 'tcx>, _: &'tcx Crate<'_>) {
++        if !run_lints(cx, &[LINT_WITHOUT_LINT_PASS], CRATE_HIR_ID) {
++            return;
++        }
++
++        for (lint_name, &lint_span) in &self.declared_lints {
++            // When using the `declare_tool_lint!` macro, the original `lint_span`'s
++            // file points to "<rustc macros>".
++            // `compiletest-rs` thinks that's an error in a different file and
++            // just ignores it. This causes the test in compile-fail/lint_pass
++            // not able to capture the error.
++            // Therefore, we need to climb the macro expansion tree and find the
++            // actual span that invoked `declare_tool_lint!`:
++            let lint_span = lint_span.ctxt().outer_expn_data().call_site;
++
++            if !self.registered_lints.contains(lint_name) {
++                span_lint(
++                    cx,
++                    LINT_WITHOUT_LINT_PASS,
++                    lint_span,
++                    &format!("the lint `{}` is not added to any `LintPass`", lint_name),
++                );
++            }
++        }
++    }
++}
++
++fn is_lint_ref_type<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &Ty<'_>) -> bool {
++    if let TyKind::Rptr(
++        _,
++        MutTy {
++            ty: ref inner,
++            mutbl: Mutability::Not,
++        },
++    ) = ty.kind
++    {
++        if let TyKind::Path(ref path) = inner.kind {
++            if let Res::Def(DefKind::Struct, def_id) = cx.tables.qpath_res(path, inner.hir_id) {
++                return match_def_path(cx, def_id, &paths::LINT);
++            }
++        }
++    }
++
++    false
++}
++
++struct LintCollector<'a, 'tcx> {
++    output: &'a mut FxHashSet<Name>,
++    cx: &'a LateContext<'a, 'tcx>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
++        if path.segments.len() == 1 {
++            self.output.insert(path.segments[0].ident.name);
++        }
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::All(self.cx.tcx.hir())
++    }
++}
++
++#[derive(Clone, Default)]
++pub struct CompilerLintFunctions {
++    map: FxHashMap<&'static str, &'static str>,
++}
++
++impl CompilerLintFunctions {
++    #[must_use]
++    pub fn new() -> Self {
++        let mut map = FxHashMap::default();
++        map.insert("span_lint", "utils::span_lint");
++        map.insert("struct_span_lint", "utils::span_lint");
++        map.insert("lint", "utils::span_lint");
++        map.insert("span_lint_note", "utils::span_lint_and_note");
++        map.insert("span_lint_help", "utils::span_lint_and_help");
++        Self { map }
++    }
++}
++
++impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CompilerLintFunctions {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        if !run_lints(cx, &[COMPILER_LINT_FUNCTIONS], expr.hir_id) {
++            return;
++        }
++
++        if_chain! {
++            if let ExprKind::MethodCall(ref path, _, ref args) = expr.kind;
++            let fn_name = path.ident;
++            if let Some(sugg) = self.map.get(&*fn_name.as_str());
++            let ty = walk_ptrs_ty(cx.tables.expr_ty(&args[0]));
++            if match_type(cx, ty, &paths::EARLY_CONTEXT)
++                || match_type(cx, ty, &paths::LATE_CONTEXT);
++            then {
++                span_lint_and_help(
++                    cx,
++                    COMPILER_LINT_FUNCTIONS,
++                    path.ident.span,
++                    "usage of a compiler lint function",
++                    None,
++                    &format!("please use the Clippy variant of this function: `{}`", sugg),
++                );
++            }
++        }
++    }
++}
++
++declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OuterExpnDataPass {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if !run_lints(cx, &[OUTER_EXPN_EXPN_DATA], expr.hir_id) {
++            return;
++        }
++
++        let (method_names, arg_lists, spans) = method_calls(expr, 2);
++        let method_names: Vec<SymbolStr> = method_names.iter().map(|s| s.as_str()).collect();
++        let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect();
++        if_chain! {
++            if let ["expn_data", "outer_expn"] = method_names.as_slice();
++            let args = arg_lists[1];
++            if args.len() == 1;
++            let self_arg = &args[0];
++            let self_ty = walk_ptrs_ty(cx.tables.expr_ty(self_arg));
++            if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
++            then {
++                span_lint_and_sugg(
++                    cx,
++                    OUTER_EXPN_EXPN_DATA,
++                    spans[1].with_hi(expr.span.hi()),
++                    "usage of `outer_expn().expn_data()`",
++                    "try",
++                    "outer_expn_data()".to_string(),
++                    Applicability::MachineApplicable,
++                );
++            }
++        }
++    }
++}
++
++declare_lint_pass!(ProduceIce => [PRODUCE_ICE]);
++
++impl EarlyLintPass for ProduceIce {
++    fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
++        if is_trigger_fn(fn_kind) {
++            panic!("Would you like some help with that?");
++        }
++    }
++}
++
++fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
++    match fn_kind {
++        FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy",
++        FnKind::Closure(..) => false,
++    }
++}
++
++declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CollapsibleCalls {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if !run_lints(cx, &[COLLAPSIBLE_SPAN_LINT_CALLS], expr.hir_id) {
++            return;
++        }
++
++        if_chain! {
++            if let ExprKind::Call(ref func, ref and_then_args) = expr.kind;
++            if let ExprKind::Path(ref path) = func.kind;
++            if match_qpath(path, &["span_lint_and_then"]);
++            if and_then_args.len() == 5;
++            if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind;
++            let body = cx.tcx.hir().body(*body_id);
++            if let ExprKind::Block(block, _) = &body.value.kind;
++            let stmts = &block.stmts;
++            if stmts.len() == 1 && block.expr.is_none();
++            if let StmtKind::Semi(only_expr) = &stmts[0].kind;
++            if let ExprKind::MethodCall(ref ps, _, ref span_call_args) = &only_expr.kind;
++            let and_then_snippets = get_and_then_snippets(cx, and_then_args);
++            let mut sle = SpanlessEq::new(cx).ignore_fn();
++            then {
++                match &*ps.ident.as_str() {
++                    "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
++                        suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args));
++                    },
++                    "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
++                        let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
++                        suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
++                    },
++                    "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
++                        let note_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
++                        suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
++                    },
++                    "help" => {
++                        let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
++                        suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
++                    }
++                    "note" => {
++                        let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
++                        suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false);
++                    }
++                    _  => (),
++                }
++            }
++        }
++    }
++}
++
++struct AndThenSnippets<'a> {
++    cx: Cow<'a, str>,
++    lint: Cow<'a, str>,
++    span: Cow<'a, str>,
++    msg: Cow<'a, str>,
++}
++
++fn get_and_then_snippets<'a, 'hir>(
++    cx: &LateContext<'_, '_>,
++    and_then_snippets: &'hir [Expr<'hir>],
++) -> AndThenSnippets<'a> {
++    let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx");
++    let lint_snippet = snippet(cx, and_then_snippets[1].span, "..");
++    let span_snippet = snippet(cx, and_then_snippets[2].span, "span");
++    let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#);
++
++    AndThenSnippets {
++        cx: cx_snippet,
++        lint: lint_snippet,
++        span: span_snippet,
++        msg: msg_snippet,
++    }
++}
++
++struct SpanSuggestionSnippets<'a> {
++    help: Cow<'a, str>,
++    sugg: Cow<'a, str>,
++    applicability: Cow<'a, str>,
++}
++
++fn span_suggestion_snippets<'a, 'hir>(
++    cx: &LateContext<'_, '_>,
++    span_call_args: &'hir [Expr<'hir>],
++) -> SpanSuggestionSnippets<'a> {
++    let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
++    let sugg_snippet = snippet(cx, span_call_args[3].span, "..");
++    let applicability_snippet = snippet(cx, span_call_args[4].span, "Applicability::MachineApplicable");
++
++    SpanSuggestionSnippets {
++        help: help_snippet,
++        sugg: sugg_snippet,
++        applicability: applicability_snippet,
++    }
++}
++
++fn suggest_suggestion(
++    cx: &LateContext<'_, '_>,
++    expr: &Expr<'_>,
++    and_then_snippets: &AndThenSnippets<'_>,
++    span_suggestion_snippets: &SpanSuggestionSnippets<'_>,
++) {
++    span_lint_and_sugg(
++        cx,
++        COLLAPSIBLE_SPAN_LINT_CALLS,
++        expr.span,
++        "this call is collapsible",
++        "collapse into",
++        format!(
++            "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})",
++            and_then_snippets.cx,
++            and_then_snippets.lint,
++            and_then_snippets.span,
++            and_then_snippets.msg,
++            span_suggestion_snippets.help,
++            span_suggestion_snippets.sugg,
++            span_suggestion_snippets.applicability
++        ),
++        Applicability::MachineApplicable,
++    );
++}
++
++fn suggest_help(
++    cx: &LateContext<'_, '_>,
++    expr: &Expr<'_>,
++    and_then_snippets: &AndThenSnippets<'_>,
++    help: &str,
++    with_span: bool,
++) {
++    let option_span = if with_span {
++        format!("Some({})", and_then_snippets.span)
++    } else {
++        "None".to_string()
++    };
++
++    span_lint_and_sugg(
++        cx,
++        COLLAPSIBLE_SPAN_LINT_CALLS,
++        expr.span,
++        "this call is collapsible",
++        "collapse into",
++        format!(
++            "span_lint_and_help({}, {}, {}, {}, {}, {})",
++            and_then_snippets.cx,
++            and_then_snippets.lint,
++            and_then_snippets.span,
++            and_then_snippets.msg,
++            &option_span,
++            help
++        ),
++        Applicability::MachineApplicable,
++    );
++}
++
++fn suggest_note(
++    cx: &LateContext<'_, '_>,
++    expr: &Expr<'_>,
++    and_then_snippets: &AndThenSnippets<'_>,
++    note: &str,
++    with_span: bool,
++) {
++    let note_span = if with_span {
++        format!("Some({})", and_then_snippets.span)
++    } else {
++        "None".to_string()
++    };
++
++    span_lint_and_sugg(
++        cx,
++        COLLAPSIBLE_SPAN_LINT_CALLS,
++        expr.span,
++        "this call is collspible",
++        "collapse into",
++        format!(
++            "span_lint_and_note({}, {}, {}, {}, {}, {})",
++            and_then_snippets.cx,
++            and_then_snippets.lint,
++            and_then_snippets.span,
++            and_then_snippets.msg,
++            note_span,
++            note
++        ),
++        Applicability::MachineApplicable,
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..04b4b42376193c7dd31c1ce552db5bf49888a89e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1491 @@@
++#[macro_use]
++pub mod sym;
++
++pub mod attrs;
++pub mod author;
++pub mod camel_case;
++pub mod comparisons;
++pub mod conf;
++pub mod constants;
++mod diagnostics;
++pub mod higher;
++mod hir_utils;
++pub mod inspector;
++pub mod internal_lints;
++pub mod numeric_literal;
++pub mod paths;
++pub mod ptr;
++pub mod sugg;
++pub mod usage;
++pub use self::attrs::*;
++pub use self::diagnostics::*;
++pub use self::hir_utils::{SpanlessEq, SpanlessHash};
++
++use std::borrow::Cow;
++use std::mem;
++
++use if_chain::if_chain;
++use rustc_ast::ast::{self, Attribute, LitKind};
++use rustc_attr as attr;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
++use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
++use rustc_hir::Node;
++use rustc_hir::{
++    def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, HirId, ImplItem, ImplItemKind, Item, ItemKind,
++    MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, TraitRef, TyKind, Unsafety,
++};
++use rustc_infer::infer::TyCtxtInferExt;
++use rustc_lint::{LateContext, Level, Lint, LintContext};
++use rustc_middle::hir::map::Map;
++use rustc_middle::traits;
++use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Binder, Ty, TyCtxt, TypeFoldable};
++use rustc_span::hygiene::{ExpnKind, MacroKind};
++use rustc_span::source_map::original_sp;
++use rustc_span::symbol::{self, kw, Symbol};
++use rustc_span::{BytePos, Pos, Span, DUMMY_SP};
++use rustc_target::abi::Integer;
++use rustc_trait_selection::traits::predicate_for_trait_def;
++use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
++use rustc_trait_selection::traits::query::normalize::AtExt;
++use smallvec::SmallVec;
++
++use crate::consts::{constant, Constant};
++use crate::reexport::Name;
++
++/// Returns `true` if the two spans come from differing expansions (i.e., one is
++/// from a macro and one isn't).
++#[must_use]
++pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
++    rhs.ctxt() != lhs.ctxt()
++}
++
++/// Returns `true` if the given `NodeId` is inside a constant context
++///
++/// # Example
++///
++/// ```rust,ignore
++/// if in_constant(cx, expr.hir_id) {
++///     // Do something
++/// }
++/// ```
++pub fn in_constant(cx: &LateContext<'_, '_>, id: HirId) -> bool {
++    let parent_id = cx.tcx.hir().get_parent_item(id);
++    match cx.tcx.hir().get(parent_id) {
++        Node::Item(&Item {
++            kind: ItemKind::Const(..),
++            ..
++        })
++        | Node::TraitItem(&TraitItem {
++            kind: TraitItemKind::Const(..),
++            ..
++        })
++        | Node::ImplItem(&ImplItem {
++            kind: ImplItemKind::Const(..),
++            ..
++        })
++        | Node::AnonConst(_)
++        | Node::Item(&Item {
++            kind: ItemKind::Static(..),
++            ..
++        }) => true,
++        Node::Item(&Item {
++            kind: ItemKind::Fn(ref sig, ..),
++            ..
++        })
++        | Node::ImplItem(&ImplItem {
++            kind: ImplItemKind::Fn(ref sig, _),
++            ..
++        }) => sig.header.constness == Constness::Const,
++        _ => false,
++    }
++}
++
++/// Returns `true` if this `span` was expanded by any macro.
++#[must_use]
++pub fn in_macro(span: Span) -> bool {
++    if span.from_expansion() {
++        if let ExpnKind::Desugaring(..) = span.ctxt().outer_expn_data().kind {
++            false
++        } else {
++            true
++        }
++    } else {
++        false
++    }
++}
++// 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 {
++    match pat.kind {
++        PatKind::Wild => true,
++        _ => false,
++    }
++}
++
++/// Checks if type is struct, enum or union type with the given def path.
++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
++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 method call given in `expr` belongs to the given trait.
++pub fn match_trait_method(cx: &LateContext<'_, '_>, expr: &Expr<'_>, path: &[&str]) -> bool {
++    let def_id = cx.tables.type_dependent_def_id(expr.hir_id).unwrap();
++    let trt_id = cx.tcx.trait_of_item(def_id);
++    if let Some(trt_id) = trt_id {
++        match_def_path(cx, trt_id, path)
++    } else {
++        false
++    }
++}
++
++/// Checks if an expression references a variable of the given name.
++pub fn match_var(expr: &Expr<'_>, var: Name) -> bool {
++    if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
++        if path.segments.len() == 1 && path.segments[0].ident.name == var {
++            return true;
++        }
++    }
++    false
++}
++
++pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
++    match *path {
++        QPath::Resolved(_, ref path) => path.segments.last().expect("A path must have at least one segment"),
++        QPath::TypeRelative(_, ref seg) => seg,
++    }
++}
++
++pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
++    match *path {
++        QPath::Resolved(_, ref path) if path.segments.len() == 1 => Some(&path.segments[0]),
++        QPath::Resolved(..) => None,
++        QPath::TypeRelative(_, ref seg) => Some(seg),
++    }
++}
++
++/// Matches a `QPath` against a slice of segment string literals.
++///
++/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
++/// `rustc_hir::QPath`.
++///
++/// # Examples
++/// ```rust,ignore
++/// match_qpath(path, &["std", "rt", "begin_unwind"])
++/// ```
++pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
++    match *path {
++        QPath::Resolved(_, ref path) => match_path(path, segments),
++        QPath::TypeRelative(ref ty, ref segment) => match ty.kind {
++            TyKind::Path(ref inner_path) => {
++                !segments.is_empty()
++                    && match_qpath(inner_path, &segments[..(segments.len() - 1)])
++                    && segment.ident.name.as_str() == segments[segments.len() - 1]
++            },
++            _ => false,
++        },
++    }
++}
++
++/// Matches a `Path` against a slice of segment string literals.
++///
++/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
++/// `rustc_hir::Path`.
++///
++/// # Examples
++///
++/// ```rust,ignore
++/// if match_path(&trait_ref.path, &paths::HASH) {
++///     // This is the `std::hash::Hash` trait.
++/// }
++///
++/// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
++///     // This is a `rustc_middle::lint::Lint`.
++/// }
++/// ```
++pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
++    path.segments
++        .iter()
++        .rev()
++        .zip(segments.iter().rev())
++        .all(|(a, b)| a.ident.name.as_str() == *b)
++}
++
++/// Matches a `Path` against a slice of segment string literals, e.g.
++///
++/// # Examples
++/// ```rust,ignore
++/// match_path_ast(path, &["std", "rt", "begin_unwind"])
++/// ```
++pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool {
++    path.segments
++        .iter()
++        .rev()
++        .zip(segments.iter().rev())
++        .all(|(a, b)| a.ident.name.as_str() == *b)
++}
++
++/// Gets the definition associated to a path.
++pub fn path_to_res(cx: &LateContext<'_, '_>, path: &[&str]) -> Option<def::Res> {
++    let crates = cx.tcx.crates();
++    let krate = crates
++        .iter()
++        .find(|&&krate| cx.tcx.crate_name(krate).as_str() == path[0]);
++    if let Some(krate) = krate {
++        let krate = DefId {
++            krate: *krate,
++            index: CRATE_DEF_INDEX,
++        };
++        let mut items = cx.tcx.item_children(krate);
++        let mut path_it = path.iter().skip(1).peekable();
++
++        loop {
++            let segment = match path_it.next() {
++                Some(segment) => segment,
++                None => return None,
++            };
++
++            let result = SmallVec::<[_; 8]>::new();
++            for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() {
++                if item.ident.name.as_str() == *segment {
++                    if path_it.peek().is_none() {
++                        return Some(item.res);
++                    }
++
++                    items = cx.tcx.item_children(item.res.def_id());
++                    break;
++                }
++            }
++        }
++    } else {
++        None
++    }
++}
++
++pub fn qpath_res(cx: &LateContext<'_, '_>, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res {
++    match qpath {
++        hir::QPath::Resolved(_, path) => path.res,
++        hir::QPath::TypeRelative(..) => {
++            if cx.tcx.has_typeck_tables(id.owner.to_def_id()) {
++                cx.tcx
++                    .typeck_tables_of(id.owner.to_def_id().expect_local())
++                    .qpath_res(qpath, id)
++            } else {
++                Res::Err
++            }
++        },
++    }
++}
++
++/// Convenience function to get the `DefId` of a trait by path.
++/// It could be a trait or trait alias.
++pub fn get_trait_def_id(cx: &LateContext<'_, '_>, path: &[&str]) -> Option<DefId> {
++    let res = match path_to_res(cx, path) {
++        Some(res) => res,
++        None => return None,
++    };
++
++    match res {
++        Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
++        Res::Err => unreachable!("this trait resolution is impossible: {:?}", &path),
++        _ => None,
++    }
++}
++
++/// Checks whether a type implements a trait.
++/// See also `get_trait_def_id`.
++pub fn implements_trait<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    ty: Ty<'tcx>,
++    trait_id: DefId,
++    ty_params: &[GenericArg<'tcx>],
++) -> bool {
++    let ty = cx.tcx.erase_regions(&ty);
++    let obligation = predicate_for_trait_def(
++        cx.tcx,
++        cx.param_env,
++        traits::ObligationCause::dummy(),
++        trait_id,
++        0,
++        ty,
++        ty_params,
++    );
++    cx.tcx
++        .infer_ctxt()
++        .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
++}
++
++/// 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:
++///
++/// ```rust
++/// struct Point(isize, isize);
++///
++/// impl std::ops::Add for Point {
++///     type Output = Self;
++///
++///     fn add(self, other: Self) -> Self {
++///         Point(0, 0)
++///     }
++/// }
++/// ```
++pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'_, 'tcx>, hir_id: HirId) -> Option<&'tcx TraitRef<'tcx>> {
++    // Get the implemented trait for the current function
++    let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
++    if_chain! {
++        if parent_impl != hir::CRATE_HIR_ID;
++        if let hir::Node::Item(item) = cx.tcx.hir().get(parent_impl);
++        if let hir::ItemKind::Impl{ of_trait: trait_ref, .. } = &item.kind;
++        then { return trait_ref.as_ref(); }
++    }
++    None
++}
++
++/// Checks whether this type implements `Drop`.
++pub fn has_drop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
++    match ty.ty_adt_def() {
++        Some(def) => def.has_dtor(cx.tcx),
++        _ => false,
++    }
++}
++
++/// 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>(
++    expr: &'tcx Expr<'tcx>,
++    max_depth: usize,
++) -> (Vec<Symbol>, Vec<&'tcx [Expr<'tcx>]>, Vec<Span>) {
++    let mut method_names = Vec::with_capacity(max_depth);
++    let mut arg_lists = Vec::with_capacity(max_depth);
++    let mut spans = Vec::with_capacity(max_depth);
++
++    let mut current = expr;
++    for _ in 0..max_depth {
++        if let ExprKind::MethodCall(path, span, args) = &current.kind {
++            if args.iter().any(|e| e.span.from_expansion()) {
++                break;
++            }
++            method_names.push(path.ident.name);
++            arg_lists.push(&**args);
++            spans.push(*span);
++            current = &args[0];
++        } else {
++            break;
++        }
++    }
++
++    (method_names, arg_lists, spans)
++}
++
++/// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
++///
++/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
++/// `matched_method_chain(expr, &["bar", "baz"])` will return a `Vec`
++/// containing the `Expr`s for
++/// `.bar()` and `.baz()`
++pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<&'a [Expr<'a>]>> {
++    let mut current = expr;
++    let mut matched = Vec::with_capacity(methods.len());
++    for method_name in methods.iter().rev() {
++        // method chains are stored last -> first
++        if let ExprKind::MethodCall(ref path, _, ref args) = current.kind {
++            if path.ident.name.as_str() == *method_name {
++                if args.iter().any(|e| e.span.from_expansion()) {
++                    return None;
++                }
++                matched.push(&**args); // build up `matched` backwards
++                current = &args[0] // go to parent expression
++            } else {
++                return None;
++            }
++        } else {
++            return None;
++        }
++    }
++    // Reverse `matched` so that it is in the same order as `methods`.
++    matched.reverse();
++    Some(matched)
++}
++
++/// Returns `true` if the provided `def_id` is an entrypoint to a program.
++pub fn is_entrypoint_fn(cx: &LateContext<'_, '_>, def_id: DefId) -> bool {
++    cx.tcx
++        .entry_fn(LOCAL_CRATE)
++        .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id.to_def_id())
++}
++
++/// Gets the name of the item the expression is in, if available.
++pub fn get_item_name(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option<Name> {
++    let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
++    match cx.tcx.hir().find(parent_id) {
++        Some(
++            Node::Item(Item { ident, .. })
++            | Node::TraitItem(TraitItem { ident, .. })
++            | Node::ImplItem(ImplItem { ident, .. }),
++        ) => Some(ident.name),
++        _ => None,
++    }
++}
++
++/// Gets the name of a `Pat`, if any.
++pub fn get_pat_name(pat: &Pat<'_>) -> Option<Name> {
++    match pat.kind {
++        PatKind::Binding(.., ref spname, _) => Some(spname.name),
++        PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
++        PatKind::Box(ref p) | PatKind::Ref(ref p, _) => get_pat_name(&*p),
++        _ => None,
++    }
++}
++
++struct ContainsName {
++    name: Name,
++    result: bool,
++}
++
++impl<'tcx> Visitor<'tcx> for ContainsName {
++    type Map = Map<'tcx>;
++
++    fn visit_name(&mut self, _: Span, name: Name) {
++        if self.name == name {
++            self.result = true;
++        }
++    }
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++/// Checks if an `Expr` contains a certain name.
++pub fn contains_name(name: Name, expr: &Expr<'_>) -> bool {
++    let mut cn = ContainsName { name, result: false };
++    cn.visit_expr(expr);
++    cn.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));
++    trim_multiline(snip, true, indent)
++}
++
++/// Same as `snippet_block`, but adapts the applicability level by the rules of
++/// `snippet_with_applicabiliy`.
++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));
++    trim_multiline(snip, true, indent)
++}
++
++/// 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 {
++    if let Some(first_char_pos) = first_char_in_first_line(cx, span) {
++        span.with_lo(first_char_pos)
++    } else {
++        span
++    }
++}
++
++fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
++    let line_span = line_span(cx, span);
++    if let Some(snip) = snippet_opt(cx, line_span) {
++        snip.find(|c: char| !c.is_whitespace())
++            .map(|pos| line_span.lo() + BytePos::from_usize(pos))
++    } else {
++        None
++    }
++}
++
++/// 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> {
++    if let Some(snip) = snippet_opt(cx, line_span(cx, span)) {
++        snip.find(|c: char| !c.is_whitespace())
++    } else {
++        None
++    }
++}
++
++/// Extends the span to the beginning of the spans line, incl. whitespaces.
++///
++/// ```rust,ignore
++///        let x = ();
++/// //             ^^
++/// // will be converted to
++///        let x = ();
++/// // ^^^^^^^^^^^^^^
++/// ```
++fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
++    let span = original_sp(span, DUMMY_SP);
++    let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap();
++    let line_no = source_map_and_line.line;
++    let line_start = source_map_and_line.sf.lines[line_no];
++    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))
++    }
++}
++
++/// Trim indentation from a multiline string with possibility of ignoring the
++/// first line.
++fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
++    let s_space = trim_multiline_inner(s, ignore_first, indent, ' ');
++    let s_tab = trim_multiline_inner(s_space, ignore_first, indent, '\t');
++    trim_multiline_inner(s_tab, ignore_first, indent, ' ')
++}
++
++fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>, ch: char) -> Cow<'_, str> {
++    let mut 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);
++    if let Some(indent) = indent {
++        x = x.saturating_sub(indent);
++    }
++    if x > 0 {
++        Cow::Owned(
++            s.lines()
++                .enumerate()
++                .map(|(i, l)| {
++                    if (ignore_first && i == 0) || l.is_empty() {
++                        l
++                    } else {
++                        l.split_at(x).1
++                    }
++                })
++                .collect::<Vec<_>>()
++                .join("\n"),
++        )
++    } else {
++        s
++    }
++}
++
++/// Gets the parent expression, if any –- this is useful to constrain a lint.
++pub fn get_parent_expr<'c>(cx: &'c LateContext<'_, '_>, e: &Expr<'_>) -> Option<&'c Expr<'c>> {
++    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;
++    }
++    map.find(parent_id).and_then(|node| {
++        if let Node::Expr(parent) = node {
++            Some(parent)
++        } else {
++            None
++        }
++    })
++}
++
++pub fn get_enclosing_block<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
++    let map = &cx.tcx.hir();
++    let enclosing_node = map
++        .get_enclosing_scope(hir_id)
++        .and_then(|enclosing_id| map.find(enclosing_id));
++    if let Some(node) = enclosing_node {
++        match node {
++            Node::Block(block) => Some(block),
++            Node::Item(&Item {
++                kind: ItemKind::Fn(_, _, eid),
++                ..
++            })
++            | Node::ImplItem(&ImplItem {
++                kind: ImplItemKind::Fn(_, eid),
++                ..
++            }) => match cx.tcx.hir().body(eid).value.kind {
++                ExprKind::Block(ref block, _) => Some(block),
++                _ => None,
++            },
++            _ => None,
++        }
++    } else {
++        None
++    }
++}
++
++/// 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.
++pub fn walk_ptrs_ty(ty: Ty<'_>) -> Ty<'_> {
++    match ty.kind {
++        ty::Ref(_, ty, _) => walk_ptrs_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)
++}
++
++/// Checks whether the given expression is a constant integer of the given value.
++/// unlike `is_integer_literal`, this version does const folding
++pub fn is_integer_const(cx: &LateContext<'_, '_>, e: &Expr<'_>, value: u128) -> bool {
++    if is_integer_literal(e, value) {
++        return true;
++    }
++    let map = cx.tcx.hir();
++    let parent_item = map.get_parent_item(e.hir_id);
++    if let Some((Constant::Int(v), _)) = map
++        .maybe_body_owned_by(parent_item)
++        .and_then(|body_id| constant(cx, cx.tcx.body_tables(body_id), e))
++    {
++        value == v
++    } else {
++        false
++    }
++}
++
++/// Checks whether the given expression is a constant literal of the given value.
++pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
++    // FIXME: use constant folding
++    if let ExprKind::Lit(ref spanned) = expr.kind {
++        if let LitKind::Int(v, _) = spanned.node {
++            return v == value;
++        }
++    }
++    false
++}
++
++/// Returns `true` if the given `Expr` has been coerced before.
++///
++/// Examples of coercions can be found in the Nomicon at
++/// <https://doc.rust-lang.org/nomicon/coercions.html>.
++///
++/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_typeck::check::coercion` for more
++/// information on adjustments and coercions.
++pub fn is_adjusted(cx: &LateContext<'_, '_>, e: &Expr<'_>) -> bool {
++    cx.tables.adjustments().get(e.hir_id).is_some()
++}
++
++/// Returns the pre-expansion span if is this comes from an expansion of the
++/// macro `name`.
++/// See also `is_direct_expn_of`.
++#[must_use]
++pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
++    loop {
++        if span.from_expansion() {
++            let data = span.ctxt().outer_expn_data();
++            let new_span = data.call_site;
++
++            if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
++                if mac_name.as_str() == name {
++                    return Some(new_span);
++                }
++            }
++
++            span = new_span;
++        } else {
++            return None;
++        }
++    }
++}
++
++/// Returns the pre-expansion span if the span directly comes from an expansion
++/// of the macro `name`.
++/// The difference with `is_expn_of` is that in
++/// ```rust,ignore
++/// foo!(bar!(42));
++/// ```
++/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
++/// `bar!` by
++/// `is_direct_expn_of`.
++#[must_use]
++pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
++    if span.from_expansion() {
++        let data = span.ctxt().outer_expn_data();
++        let new_span = data.call_site;
++
++        if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
++            if mac_name.as_str() == name {
++                return Some(new_span);
++            }
++        }
++    }
++
++    None
++}
++
++/// Convenience function to get the return type of a function.
++pub fn return_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
++    let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
++    let ret_ty = cx.tcx.fn_sig(fn_def_id).output();
++    cx.tcx.erase_late_bound_regions(&ret_ty)
++}
++
++/// Checks if two types are the same.
++///
++/// This discards any lifetime annotations, too.
++//
++// FIXME: this works correctly for lifetimes bounds (`for <'a> Foo<'a>` ==
++// `for <'b> Foo<'b>`, but not for type parameters).
++pub fn same_tys<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
++    let a = cx.tcx.erase_late_bound_regions(&Binder::bind(a));
++    let b = cx.tcx.erase_late_bound_regions(&Binder::bind(b));
++    cx.tcx
++        .infer_ctxt()
++        .enter(|infcx| infcx.can_eq(cx.param_env, a, b).is_ok())
++}
++
++/// Returns `true` if the given type is an `unsafe` function.
++pub fn type_is_unsafe_function<'a, 'tcx>(cx: &LateContext<'a, '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<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
++    ty.is_copy_modulo_regions(cx.tcx, cx.param_env, DUMMY_SP)
++}
++
++/// 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 {
++        if let ExprKind::Path(ref qp) = fun.kind {
++            let res = cx.tables.qpath_res(qp, fun.hir_id);
++            return match res {
++                def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
++                def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
++                _ => false,
++            };
++        }
++    }
++    false
++}
++
++/// Returns `true` if a pattern is refutable.
++pub fn is_refutable(cx: &LateContext<'_, '_>, pat: &Pat<'_>) -> bool {
++    fn is_enum_variant(cx: &LateContext<'_, '_>, qpath: &QPath<'_>, id: HirId) -> bool {
++        matches!(
++            cx.tables.qpath_res(qpath, id),
++            def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
++        )
++    }
++
++    fn are_refutable<'a, I: Iterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_, '_>, mut i: I) -> bool {
++        i.any(|pat| is_refutable(cx, pat))
++    }
++
++    match pat.kind {
++        PatKind::Binding(..) | PatKind::Wild => false,
++        PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => is_refutable(cx, pat),
++        PatKind::Lit(..) | PatKind::Range(..) => true,
++        PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
++        PatKind::Or(ref pats) | PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)),
++        PatKind::Struct(ref qpath, ref fields, _) => {
++            if is_enum_variant(cx, qpath, pat.hir_id) {
++                true
++            } else {
++                are_refutable(cx, fields.iter().map(|field| &*field.pat))
++            }
++        },
++        PatKind::TupleStruct(ref qpath, ref pats, _) => {
++            if is_enum_variant(cx, qpath, pat.hir_id) {
++                true
++            } else {
++                are_refutable(cx, pats.iter().map(|pat| &**pat))
++            }
++        },
++        PatKind::Slice(ref head, ref middle, ref tail) => {
++            are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat))
++        },
++    }
++}
++
++/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
++/// implementations have.
++pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
++    attr::contains_name(attrs, sym!(automatically_derived))
++}
++
++/// Remove blocks around an expression.
++///
++/// Ie. `x`, `{ x }` and `{{{{ x }}}}` all give `x`. `{ x; y }` and `{}` return
++/// themselves.
++pub fn remove_blocks<'tcx>(mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
++    while let ExprKind::Block(ref block, ..) = expr.kind {
++        match (block.stmts.is_empty(), block.expr.as_ref()) {
++            (true, Some(e)) => expr = e,
++            _ => break,
++        }
++    }
++    expr
++}
++
++pub fn is_self(slf: &Param<'_>) -> bool {
++    if let PatKind::Binding(.., name, _) = slf.pat.kind {
++        name.name == kw::SelfLower
++    } else {
++        false
++    }
++}
++
++pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
++    if_chain! {
++        if let TyKind::Path(ref qp) = slf.kind;
++        if let QPath::Resolved(None, ref path) = *qp;
++        if let Res::SelfTy(..) = path.res;
++        then {
++            return true
++        }
++    }
++    false
++}
++
++pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
++    (0..decl.inputs.len()).map(move |i| &body.params[i])
++}
++
++/// Checks if a given expression is a match expression expanded from the `?`
++/// operator or the `try` macro.
++pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
++    fn is_ok(arm: &Arm<'_>) -> bool {
++        if_chain! {
++            if let PatKind::TupleStruct(ref path, ref pat, None) = arm.pat.kind;
++            if match_qpath(path, &paths::RESULT_OK[1..]);
++            if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
++            if let ExprKind::Path(QPath::Resolved(None, ref path)) = arm.body.kind;
++            if let Res::Local(lid) = path.res;
++            if lid == hir_id;
++            then {
++                return true;
++            }
++        }
++        false
++    }
++
++    fn is_err(arm: &Arm<'_>) -> bool {
++        if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
++            match_qpath(path, &paths::RESULT_ERR[1..])
++        } else {
++            false
++        }
++    }
++
++    if let ExprKind::Match(_, ref arms, ref source) = expr.kind {
++        // desugared from a `?` operator
++        if let MatchSource::TryDesugar = *source {
++            return Some(expr);
++        }
++
++        if_chain! {
++            if arms.len() == 2;
++            if arms[0].guard.is_none();
++            if arms[1].guard.is_none();
++            if (is_ok(&arms[0]) && is_err(&arms[1])) ||
++                (is_ok(&arms[1]) && is_err(&arms[0]));
++            then {
++                return Some(expr);
++            }
++        }
++    }
++
++    None
++}
++
++/// Returns `true` if the lint is allowed in the current context
++///
++/// Useful for skipping long running code when it's unnecessary
++pub fn is_allowed(cx: &LateContext<'_, '_>, lint: &'static Lint, id: HirId) -> bool {
++    cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
++}
++
++pub fn get_arg_name(pat: &Pat<'_>) -> Option<ast::Name> {
++    match pat.kind {
++        PatKind::Binding(.., ident, None) => Some(ident.name),
++        PatKind::Ref(ref subpat, _) => get_arg_name(subpat),
++        _ => None,
++    }
++}
++
++pub fn int_bits(tcx: TyCtxt<'_>, ity: ast::IntTy) -> u64 {
++    Integer::from_attr(&tcx, attr::IntType::SignedInt(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: ast::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: ast::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: ast::UintTy) -> u128 {
++    let bits = Integer::from_attr(&tcx, attr::IntType::UnsignedInt(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;
++    let mut enclosing_node = node;
++    while Some(enclosing_node) != prev_enclosing_node {
++        if is_automatically_derived(map.attrs(enclosing_node)) {
++            return true;
++        }
++        prev_enclosing_node = Some(enclosing_node);
++        enclosing_node = map.get_parent_item(enclosing_node);
++    }
++    false
++}
++
++/// Returns true if ty has `iter` or `iter_mut` methods
++pub fn has_iter_method(cx: &LateContext<'_, '_>, probably_ref_ty: Ty<'_>) -> Option<&'static str> {
++    // 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: [&[&str]; 13] = [
++        &paths::VEC,
++        &paths::OPTION,
++        &paths::RESULT,
++        &paths::BTREESET,
++        &paths::BTREEMAP,
++        &paths::VEC_DEQUE,
++        &paths::LINKED_LIST,
++        &paths::BINARY_HEAP,
++        &paths::HASHSET,
++        &paths::HASHMAP,
++        &paths::PATH_BUF,
++        &paths::PATH,
++        &paths::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("array"),
++        ty::Slice(..) => return Some("slice"),
++        ty::Adt(adt, _) => adt.did,
++        _ => return None,
++    };
++
++    for path in &into_iter_collections {
++        if match_def_path(cx, def_id, path) {
++            return Some(*path.last().unwrap());
++        }
++    }
++    None
++}
++
++/// Matches a function call with the given path and returns the arguments.
++///
++/// Usage:
++///
++/// ```rust,ignore
++/// if let Some(args) = match_function_call(cx, begin_panic_call, &paths::BEGIN_PANIC);
++/// ```
++pub fn match_function_call<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    expr: &'tcx Expr<'_>,
++    path: &[&str],
++) -> Option<&'tcx [Expr<'tcx>]> {
++    if_chain! {
++        if let ExprKind::Call(ref fun, ref args) = expr.kind;
++        if let ExprKind::Path(ref qpath) = fun.kind;
++        if let Some(fun_def_id) = cx.tables.qpath_res(qpath, fun.hir_id).opt_def_id();
++        if match_def_path(cx, fun_def_id, path);
++        then {
++            return Some(&args)
++        }
++    };
++    None
++}
++
++/// Checks if `Ty` is normalizable. This function is useful
++/// to avoid crashes on `layout_of`.
++pub fn is_normalizable<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
++    cx.tcx.infer_ctxt().enter(|infcx| {
++        let cause = rustc_middle::traits::ObligationCause::dummy();
++        infcx.at(&cause, param_env).normalize(&ty).is_ok()
++    })
++}
++
++pub fn match_def_path<'a, 'tcx>(cx: &LateContext<'a, '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.
++    let syms = syms.iter().map(|p| Symbol::intern(p)).collect::<Vec<Symbol>>();
++    cx.match_def_path(did, &syms)
++}
++
++/// Returns the list of condition expressions and the list of blocks in a
++/// 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();
++
++    while let Some((ref cond, ref then_expr, ref else_expr)) = higher::if_block(&expr) {
++        conds.push(&**cond);
++        if let ExprKind::Block(ref block, _) = then_expr.kind {
++            blocks.push(block);
++        } else {
++            panic!("ExprKind::If node is not an ExprKind::Block");
++        }
++
++        if let Some(ref else_expr) = *else_expr {
++            expr = else_expr;
++        } else {
++            break;
++        }
++    }
++
++    // final `else {..}`
++    if !blocks.is_empty() {
++        if let ExprKind::Block(ref block, _) = expr.kind {
++            blocks.push(&**block);
++        }
++    }
++
++    (conds, blocks)
++}
++
++pub fn parent_node_is_if_expr<'a, 'b>(expr: &Expr<'_>, cx: &LateContext<'a, 'b>) -> bool {
++    let map = cx.tcx.hir();
++    let parent_id = map.get_parent_node(expr.hir_id);
++    let parent_node = map.get(parent_id);
++
++    match parent_node {
++        Node::Expr(e) => higher::if_block(&e).is_some(),
++        Node::Arm(e) => higher::if_block(&e.body).is_some(),
++        _ => false,
++    }
++}
++
++// Finds the attribute with the given name, if any
++pub fn attr_by_name<'a>(attrs: &'a [Attribute], name: &'_ str) -> Option<&'a Attribute> {
++    attrs
++        .iter()
++        .find(|attr| attr.ident().map_or(false, |ident| ident.as_str() == name))
++}
++
++// Finds the `#[must_use]` attribute, if any
++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<'a, 'tcx>(cx: &LateContext<'a, '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.predicates_of(*def_id).predicates {
++                if let ty::Predicate::Trait(ref poly_trait_predicate, _) = predicate {
++                    if must_use_attr(&cx.tcx.get_attrs(poly_trait_predicate.skip_binder().trait_ref.def_id)).is_some() {
++                        return true;
++                    }
++                }
++            }
++            false
++        },
++        ty::Dynamic(binder, _) => {
++            for predicate in binder.skip_binder().iter() {
++                if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate {
++                    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] attribyte
++pub fn is_must_use_func_call(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
++    let did = match expr.kind {
++        ExprKind::Call(ref path, _) => if_chain! {
++            if let ExprKind::Path(ref qpath) = path.kind;
++            if let def::Res::Def(_, did) = cx.tables.qpath_res(qpath, path.hir_id);
++            then {
++                Some(did)
++            } else {
++                None
++            }
++        },
++        ExprKind::MethodCall(_, _, _) => cx.tables.type_dependent_def_id(expr.hir_id),
++        _ => None,
++    };
++
++    if let Some(did) = did {
++        must_use_attr(&cx.tcx.get_attrs(did)).is_some()
++    } else {
++        false
++    }
++}
++
++pub fn is_no_std_crate(krate: &Crate<'_>) -> bool {
++    krate.item.attrs.iter().any(|attr| {
++        if let ast::AttrKind::Normal(ref attr) = attr.kind {
++            attr.path == symbol::sym::no_std
++        } else {
++            false
++        }
++    })
++}
++
++/// Check if parent of a hir node is a trait implementation block.
++/// For example, `f` in
++/// ```rust,ignore
++/// impl Trait for S {
++///     fn f() {}
++/// }
++/// ```
++pub fn is_trait_impl_item(cx: &LateContext<'_, '_>, hir_id: HirId) -> bool {
++    if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
++        matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. })
++    } else {
++        false
++    }
++}
++
++/// Check if it's even possible to satisfy the `where` clause for the item.
++///
++/// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example:
++///
++/// ```ignore
++/// fn foo() where i32: Iterator {
++///     for _ in 2i32 {}
++/// }
++/// ```
++pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_, '_>, did: DefId) -> bool {
++    use rustc_trait_selection::traits;
++    let predicates =
++        cx.tcx
++            .predicates_of(did)
++            .predicates
++            .iter()
++            .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
++    !traits::normalize_and_test_predicates(
++        cx.tcx,
++        traits::elaborate_predicates(cx.tcx, predicates)
++            .map(|o| o.predicate)
++            .collect::<Vec<_>>(),
++    )
++}
++
++pub fn run_lints(cx: &LateContext<'_, '_>, lints: &[&'static Lint], id: HirId) -> bool {
++    lints.iter().any(|lint| {
++        matches!(
++            cx.tcx.lint_level_at_node(lint, id),
++            (Level::Forbid | Level::Deny | Level::Warn, _)
++        )
++    })
++}
++
++#[cfg(test)]
++mod test {
++    use super::{trim_multiline, without_block_comments};
++
++    #[test]
++    fn test_trim_multiline_single_line() {
++        assert_eq!("", trim_multiline("".into(), false, None));
++        assert_eq!("...", trim_multiline("...".into(), false, None));
++        assert_eq!("...", trim_multiline("    ...".into(), false, None));
++        assert_eq!("...", trim_multiline("\t...".into(), false, None));
++        assert_eq!("...", trim_multiline("\t\t...".into(), false, None));
++    }
++
++    #[test]
++    #[rustfmt::skip]
++    fn test_trim_multiline_block() {
++        assert_eq!("\
++    if x {
++        y
++    } else {
++        z
++    }", trim_multiline("    if x {
++            y
++        } else {
++            z
++        }".into(), false, None));
++        assert_eq!("\
++    if x {
++    \ty
++    } else {
++    \tz
++    }", trim_multiline("    if x {
++        \ty
++        } else {
++        \tz
++        }".into(), false, None));
++    }
++
++    #[test]
++    #[rustfmt::skip]
++    fn test_trim_multiline_empty_line() {
++        assert_eq!("\
++    if x {
++        y
++
++    } else {
++        z
++    }", trim_multiline("    if x {
++            y
++
++        } else {
++            z
++        }".into(), false, None));
++    }
++
++    #[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 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..99413153d49bbc08f28dd38deaabef9e62239988
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,227 @@@
++use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind};
++
++#[derive(Debug, PartialEq)]
++pub enum Radix {
++    Binary,
++    Octal,
++    Decimal,
++    Hexadecimal,
++}
++
++impl Radix {
++    /// Returns a reasonable digit group size for this radix.
++    #[must_use]
++    fn suggest_grouping(&self) -> usize {
++        match *self {
++            Self::Binary | Self::Hexadecimal => 4,
++            Self::Octal | Self::Decimal => 3,
++        }
++    }
++}
++
++/// A helper method to format numeric literals with digit grouping.
++/// `lit` must be a valid numeric literal without suffix.
++pub fn format(lit: &str, type_suffix: Option<&str>, float: bool) -> String {
++    NumericLiteral::new(lit, type_suffix, float).format()
++}
++
++#[derive(Debug)]
++pub struct NumericLiteral<'a> {
++    /// Which radix the literal was represented in.
++    pub radix: Radix,
++    /// The radix prefix, if present.
++    pub prefix: Option<&'a str>,
++
++    /// The integer part of the number.
++    pub integer: &'a str,
++    /// The fraction part of the number.
++    pub fraction: Option<&'a str>,
++    /// The character used as exponent seperator (b'e' or b'E') and the exponent part.
++    pub exponent: Option<(char, &'a str)>,
++
++    /// The type suffix, including preceding underscore if present.
++    pub suffix: Option<&'a str>,
++}
++
++impl<'a> NumericLiteral<'a> {
++    pub fn from_lit(src: &'a str, lit: &Lit) -> Option<NumericLiteral<'a>> {
++        NumericLiteral::from_lit_kind(src, &lit.kind)
++    }
++
++    pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> {
++        if lit_kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) {
++            let (unsuffixed, suffix) = split_suffix(&src, lit_kind);
++            let float = if let LitKind::Float(..) = lit_kind { true } else { false };
++            Some(NumericLiteral::new(unsuffixed, suffix, float))
++        } else {
++            None
++        }
++    }
++
++    #[must_use]
++    pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self {
++        // Determine delimiter for radix prefix, if present, and radix.
++        let radix = if lit.starts_with("0x") {
++            Radix::Hexadecimal
++        } else if lit.starts_with("0b") {
++            Radix::Binary
++        } else if lit.starts_with("0o") {
++            Radix::Octal
++        } else {
++            Radix::Decimal
++        };
++
++        // Grab part of the literal after prefix, if present.
++        let (prefix, mut sans_prefix) = if let Radix::Decimal = radix {
++            (None, lit)
++        } else {
++            let (p, s) = lit.split_at(2);
++            (Some(p), s)
++        };
++
++        if suffix.is_some() && sans_prefix.ends_with('_') {
++            // The '_' before the suffix isn't part of the digits
++            sans_prefix = &sans_prefix[..sans_prefix.len() - 1];
++        }
++
++        let (integer, fraction, exponent) = Self::split_digit_parts(sans_prefix, float);
++
++        Self {
++            radix,
++            prefix,
++            integer,
++            fraction,
++            exponent,
++            suffix,
++        }
++    }
++
++    pub fn is_decimal(&self) -> bool {
++        self.radix == Radix::Decimal
++    }
++
++    pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(char, &str)>) {
++        let mut integer = digits;
++        let mut fraction = None;
++        let mut exponent = None;
++
++        if float {
++            for (i, c) in digits.char_indices() {
++                match c {
++                    '.' => {
++                        integer = &digits[..i];
++                        fraction = Some(&digits[i + 1..]);
++                    },
++                    'e' | 'E' => {
++                        if integer.len() > i {
++                            integer = &digits[..i];
++                        } else {
++                            fraction = Some(&digits[integer.len() + 1..i]);
++                        };
++                        exponent = Some((c, &digits[i + 1..]));
++                        break;
++                    },
++                    _ => {},
++                }
++            }
++        }
++
++        (integer, fraction, exponent)
++    }
++
++    /// Returns literal formatted in a sensible way.
++    pub fn format(&self) -> String {
++        let mut output = String::new();
++
++        if let Some(prefix) = self.prefix {
++            output.push_str(prefix);
++        }
++
++        let group_size = self.radix.suggest_grouping();
++
++        Self::group_digits(
++            &mut output,
++            self.integer,
++            group_size,
++            true,
++            self.radix == Radix::Hexadecimal,
++        );
++
++        if let Some(fraction) = self.fraction {
++            output.push('.');
++            Self::group_digits(&mut output, fraction, group_size, false, false);
++        }
++
++        if let Some((separator, exponent)) = self.exponent {
++            output.push(separator);
++            Self::group_digits(&mut output, exponent, group_size, true, false);
++        }
++
++        if let Some(suffix) = self.suffix {
++            output.push('_');
++            output.push_str(suffix);
++        }
++
++        output
++    }
++
++    pub fn group_digits(output: &mut String, input: &str, group_size: usize, partial_group_first: bool, pad: bool) {
++        debug_assert!(group_size > 0);
++
++        let mut digits = input.chars().filter(|&c| c != '_');
++
++        let first_group_size;
++
++        if partial_group_first {
++            first_group_size = (digits.clone().count() - 1) % group_size + 1;
++            if pad {
++                for _ in 0..group_size - first_group_size {
++                    output.push('0');
++                }
++            }
++        } else {
++            first_group_size = group_size;
++        }
++
++        for _ in 0..first_group_size {
++            if let Some(digit) = digits.next() {
++                output.push(digit);
++            }
++        }
++
++        for (c, i) in digits.zip((0..group_size).cycle()) {
++            if i == 0 {
++                output.push('_');
++            }
++            output.push(c);
++        }
++    }
++}
++
++fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) {
++    debug_assert!(lit_kind.is_numeric());
++    if let Some(suffix_length) = lit_suffix_length(lit_kind) {
++        let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length);
++        (unsuffixed, Some(suffix))
++    } else {
++        (src, None)
++    }
++}
++
++fn lit_suffix_length(lit_kind: &LitKind) -> Option<usize> {
++    debug_assert!(lit_kind.is_numeric());
++    let suffix = match lit_kind {
++        LitKind::Int(_, int_lit_kind) => match int_lit_kind {
++            LitIntType::Signed(int_ty) => Some(int_ty.name_str()),
++            LitIntType::Unsigned(uint_ty) => Some(uint_ty.name_str()),
++            LitIntType::Unsuffixed => None,
++        },
++        LitKind::Float(_, float_lit_kind) => match float_lit_kind {
++            LitFloatType::Suffixed(float_ty) => Some(float_ty.name_str()),
++            LitFloatType::Unsuffixed => None,
++        },
++        _ => None,
++    };
++
++    suffix.map(str::len)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ca2c4ade15565bc0d45c1e3c607fb0c883a4b788
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,139 @@@
++//! This module contains paths to types and functions Clippy needs to know
++//! about.
++//!
++//! Whenever possible, please consider diagnostic items over hardcoded paths.
++//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
++
++pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
++pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
++pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
++pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
++pub const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
++pub const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"];
++pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"];
++pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
++pub const BOX: [&str; 3] = ["alloc", "boxed", "Box"];
++pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"];
++pub const BTREEMAP_ENTRY: [&str; 5] = ["alloc", "collections", "btree", "map", "Entry"];
++pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"];
++pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"];
++pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
++pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
++pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
++pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
++pub const CSTRING: [&str; 4] = ["std", "ffi", "c_str", "CString"];
++pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"];
++pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
++pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
++pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
++pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
++pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
++pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
++pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
++pub const DROP: [&str; 3] = ["core", "mem", "drop"];
++pub const DROP_TRAIT: [&str; 4] = ["core", "ops", "drop", "Drop"];
++pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
++pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"];
++pub const EXIT: [&str; 3] = ["std", "process", "exit"];
++pub const FILE: [&str; 3] = ["std", "fs", "File"];
++pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
++pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
++pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
++pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
++pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
++pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
++pub const HASH: [&str; 2] = ["hash", "Hash"];
++pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
++pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
++pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
++pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
++pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
++pub const INTO: [&str; 3] = ["core", "convert", "Into"];
++pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
++pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
++pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
++pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"];
++pub const LATE_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "LateContext"];
++pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
++pub const LINT: [&str; 3] = ["rustc_session", "lint", "Lint"];
++pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
++pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
++pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"];
++pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"];
++pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"];
++pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"];
++pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
++pub const OPS_MODULE: [&str; 2] = ["core", "ops"];
++pub const OPTION: [&str; 3] = ["core", "option", "Option"];
++pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"];
++pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
++pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
++pub const OS_STRING: [&str; 4] = ["std", "ffi", "os_str", "OsString"];
++pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
++pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
++pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"];
++pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 2] = ["parking_lot", "RwLockReadGuard"];
++pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWriteGuard"];
++pub const PATH: [&str; 3] = ["std", "path", "Path"];
++pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"];
++pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
++pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
++pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
++pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
++pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"];
++pub const RANGE: [&str; 3] = ["core", "ops", "Range"];
++pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
++pub const RANGE_FROM: [&str; 3] = ["core", "ops", "RangeFrom"];
++pub const RANGE_FROM_STD: [&str; 3] = ["std", "ops", "RangeFrom"];
++pub const RANGE_FULL: [&str; 3] = ["core", "ops", "RangeFull"];
++pub const RANGE_FULL_STD: [&str; 3] = ["std", "ops", "RangeFull"];
++pub const RANGE_INCLUSIVE_NEW: [&str; 4] = ["core", "ops", "RangeInclusive", "new"];
++pub const RANGE_INCLUSIVE_STD_NEW: [&str; 4] = ["std", "ops", "RangeInclusive", "new"];
++pub const RANGE_STD: [&str; 3] = ["std", "ops", "Range"];
++pub const RANGE_TO: [&str; 3] = ["core", "ops", "RangeTo"];
++pub const RANGE_TO_INCLUSIVE: [&str; 3] = ["core", "ops", "RangeToInclusive"];
++pub const RANGE_TO_INCLUSIVE_STD: [&str; 3] = ["std", "ops", "RangeToInclusive"];
++pub const RANGE_TO_STD: [&str; 3] = ["std", "ops", "RangeTo"];
++pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
++pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
++pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"];
++pub const REGEX: [&str; 3] = ["regex", "re_unicode", "Regex"];
++pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
++pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
++pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
++pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
++pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
++pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
++pub const REPEAT: [&str; 3] = ["core", "iter", "repeat"];
++pub const RESULT: [&str; 3] = ["core", "result", "Result"];
++pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
++pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
++pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"];
++pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
++pub const SERDE_DESERIALIZE: [&str; 2] = ["_serde", "Deserialize"];
++pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
++pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
++pub const SLICE_ITER: [&str; 3] = ["core", "slice", "Iter"];
++pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
++pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
++pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"];
++pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"];
++pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"];
++pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
++pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
++pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
++pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
++pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
++pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"];
++pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
++pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
++pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"];
++pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"];
++pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
++pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
++pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
++pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"];
++pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
++pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
++pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
++pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..240bf2449cb5ebbbc928be17beb60bf83e870efb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,88 @@@
++use crate::utils::{get_pat_name, match_var, snippet};
++use rustc_ast::ast::Name;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_hir::{Body, BodyId, Expr, ExprKind, Param};
++use rustc_lint::LateContext;
++use rustc_middle::hir::map::Map;
++use rustc_span::source_map::Span;
++use std::borrow::Cow;
++
++pub fn get_spans(
++    cx: &LateContext<'_, '_>,
++    opt_body_id: Option<BodyId>,
++    idx: usize,
++    replacements: &[(&'static str, &'static str)],
++) -> Option<Vec<(Span, Cow<'static, str>)>> {
++    if let Some(body) = opt_body_id.map(|id| cx.tcx.hir().body(id)) {
++        get_binding_name(&body.params[idx]).map_or_else(
++            || Some(vec![]),
++            |name| extract_clone_suggestions(cx, name, replacements, body),
++        )
++    } else {
++        Some(vec![])
++    }
++}
++
++fn extract_clone_suggestions<'a, 'tcx>(
++    cx: &LateContext<'a, 'tcx>,
++    name: Name,
++    replace: &[(&'static str, &'static str)],
++    body: &'tcx Body<'_>,
++) -> Option<Vec<(Span, Cow<'static, str>)>> {
++    let mut visitor = PtrCloneVisitor {
++        cx,
++        name,
++        replace,
++        spans: vec![],
++        abort: false,
++    };
++    visitor.visit_body(body);
++    if visitor.abort {
++        None
++    } else {
++        Some(visitor.spans)
++    }
++}
++
++struct PtrCloneVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'a, 'tcx>,
++    name: Name,
++    replace: &'a [(&'static str, &'static str)],
++    spans: Vec<(Span, Cow<'static, str>)>,
++    abort: bool,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        if self.abort {
++            return;
++        }
++        if let ExprKind::MethodCall(ref seg, _, ref args) = expr.kind {
++            if args.len() == 1 && match_var(&args[0], self.name) {
++                if seg.ident.name.as_str() == "capacity" {
++                    self.abort = true;
++                    return;
++                }
++                for &(fn_name, suffix) in self.replace {
++                    if seg.ident.name.as_str() == fn_name {
++                        self.spans
++                            .push((expr.span, snippet(self.cx, args[0].span, "_") + suffix));
++                        return;
++                    }
++                }
++            }
++            return;
++        }
++        walk_expr(self, expr);
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++fn get_binding_name(arg: &Param<'_>) -> Option<Name> {
++    get_pat_name(&arg.pat)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a8fe637d3d978f0473e5a904f271a8dc48b970d6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,628 @@@
++//! Contains utility functions to generate suggestions.
++#![deny(clippy::missing_docs_in_private_items)]
++
++use crate::utils::{higher, 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;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::{EarlyContext, LateContext, LintContext};
++use rustc_span::source_map::{CharPos, Span};
++use rustc_span::{BytePos, Pos};
++use std::borrow::Cow;
++use std::convert::TryInto;
++use std::fmt::Display;
++
++/// A helper type to build suggestion correctly handling parenthesis.
++pub enum Sugg<'a> {
++    /// An expression that never needs parenthesis such as `1337` or `[0; 42]`.
++    NonParen(Cow<'a, str>),
++    /// An expression that does not fit in other variants.
++    MaybeParen(Cow<'a, str>),
++    /// A binary operator expression, including `as`-casts and explicit type
++    /// coercion.
++    BinOp(AssocOp, Cow<'a, str>),
++}
++
++/// Literal constant `1`, for convenience.
++pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
++
++impl Display for Sugg<'_> {
++    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
++        match *self {
++            Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) | Sugg::BinOp(_, ref s) => s.fmt(f),
++        }
++    }
++}
++
++#[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method
++impl<'a> Sugg<'a> {
++    /// Prepare a suggestion from an expression.
++    pub fn hir_opt(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) -> Option<Self> {
++        snippet_opt(cx, expr.span).map(|snippet| {
++            let snippet = Cow::Owned(snippet);
++            Self::hir_from_snippet(cx, expr, snippet)
++        })
++    }
++
++    /// Convenience function around `hir_opt` for suggestions with a default
++    /// text.
++    pub fn hir(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
++        Self::hir_opt(cx, expr).unwrap_or_else(|| Sugg::NonParen(Cow::Borrowed(default)))
++    }
++
++    /// Same as `hir`, 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 hir_with_applicability(
++        cx: &LateContext<'_, '_>,
++        expr: &hir::Expr<'_>,
++        default: &'a str,
++        applicability: &mut Applicability,
++    ) -> Self {
++        if *applicability != Applicability::Unspecified && expr.span.from_expansion() {
++            *applicability = Applicability::MaybeIncorrect;
++        }
++        Self::hir_opt(cx, expr).unwrap_or_else(|| {
++            if *applicability == Applicability::MachineApplicable {
++                *applicability = Applicability::HasPlaceholders;
++            }
++            Sugg::NonParen(Cow::Borrowed(default))
++        })
++    }
++
++    /// Same as `hir`, but will use the pre expansion span if the `expr` was in a macro.
++    pub fn hir_with_macro_callsite(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
++        let snippet = snippet_with_macro_callsite(cx, expr.span, default);
++
++        Self::hir_from_snippet(cx, expr, snippet)
++    }
++
++    /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
++    /// function variants of `Sugg`, since these use different snippet functions.
++    fn hir_from_snippet(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self {
++        if let Some(range) = higher::range(cx, expr) {
++            let op = match range.limits {
++                ast::RangeLimits::HalfOpen => AssocOp::DotDot,
++                ast::RangeLimits::Closed => AssocOp::DotDotEq,
++            };
++            return Sugg::BinOp(op, snippet);
++        }
++
++        match expr.kind {
++            hir::ExprKind::AddrOf(..)
++            | hir::ExprKind::Box(..)
++            | hir::ExprKind::Closure(..)
++            | hir::ExprKind::Unary(..)
++            | hir::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
++            hir::ExprKind::Continue(..)
++            | hir::ExprKind::Yield(..)
++            | hir::ExprKind::Array(..)
++            | hir::ExprKind::Block(..)
++            | hir::ExprKind::Break(..)
++            | hir::ExprKind::Call(..)
++            | hir::ExprKind::Field(..)
++            | hir::ExprKind::Index(..)
++            | hir::ExprKind::LlvmInlineAsm(..)
++            | hir::ExprKind::Lit(..)
++            | hir::ExprKind::Loop(..)
++            | hir::ExprKind::MethodCall(..)
++            | hir::ExprKind::Path(..)
++            | hir::ExprKind::Repeat(..)
++            | hir::ExprKind::Ret(..)
++            | hir::ExprKind::Struct(..)
++            | hir::ExprKind::Tup(..)
++            | hir::ExprKind::DropTemps(_)
++            | hir::ExprKind::Err => Sugg::NonParen(snippet),
++            hir::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
++            hir::ExprKind::AssignOp(op, ..) => Sugg::BinOp(hirbinop2assignop(op), snippet),
++            hir::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(higher::binop(op.node)), snippet),
++            hir::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
++            hir::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
++        }
++    }
++
++    /// Prepare a suggestion from an expression.
++    pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
++        use rustc_ast::ast::RangeLimits;
++
++        let snippet = snippet(cx, expr.span, default);
++
++        match expr.kind {
++            ast::ExprKind::AddrOf(..)
++            | ast::ExprKind::Box(..)
++            | ast::ExprKind::Closure(..)
++            | ast::ExprKind::If(..)
++            | ast::ExprKind::Let(..)
++            | ast::ExprKind::Unary(..)
++            | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
++            ast::ExprKind::Async(..)
++            | ast::ExprKind::Block(..)
++            | ast::ExprKind::Break(..)
++            | ast::ExprKind::Call(..)
++            | ast::ExprKind::Continue(..)
++            | ast::ExprKind::Yield(..)
++            | ast::ExprKind::Field(..)
++            | ast::ExprKind::ForLoop(..)
++            | ast::ExprKind::Index(..)
++            | ast::ExprKind::LlvmInlineAsm(..)
++            | ast::ExprKind::Lit(..)
++            | ast::ExprKind::Loop(..)
++            | ast::ExprKind::MacCall(..)
++            | ast::ExprKind::MethodCall(..)
++            | ast::ExprKind::Paren(..)
++            | ast::ExprKind::Path(..)
++            | ast::ExprKind::Repeat(..)
++            | ast::ExprKind::Ret(..)
++            | ast::ExprKind::Struct(..)
++            | ast::ExprKind::Try(..)
++            | ast::ExprKind::TryBlock(..)
++            | ast::ExprKind::Tup(..)
++            | ast::ExprKind::Array(..)
++            | ast::ExprKind::While(..)
++            | ast::ExprKind::Await(..)
++            | ast::ExprKind::Err => Sugg::NonParen(snippet),
++            ast::ExprKind::Range(.., RangeLimits::HalfOpen) => Sugg::BinOp(AssocOp::DotDot, snippet),
++            ast::ExprKind::Range(.., RangeLimits::Closed) => Sugg::BinOp(AssocOp::DotDotEq, snippet),
++            ast::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
++            ast::ExprKind::AssignOp(op, ..) => Sugg::BinOp(astbinop2assignop(op), snippet),
++            ast::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(op.node), snippet),
++            ast::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
++            ast::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
++        }
++    }
++
++    /// Convenience method to create the `<lhs> && <rhs>` suggestion.
++    pub fn and(self, rhs: &Self) -> Sugg<'static> {
++        make_binop(ast::BinOpKind::And, &self, rhs)
++    }
++
++    /// Convenience method to create the `<lhs> & <rhs>` suggestion.
++    pub fn bit_and(self, rhs: &Self) -> Sugg<'static> {
++        make_binop(ast::BinOpKind::BitAnd, &self, rhs)
++    }
++
++    /// Convenience method to create the `<lhs> as <rhs>` suggestion.
++    pub fn as_ty<R: Display>(self, rhs: R) -> Sugg<'static> {
++        make_assoc(AssocOp::As, &self, &Sugg::NonParen(rhs.to_string().into()))
++    }
++
++    /// Convenience method to create the `&<expr>` suggestion.
++    pub fn addr(self) -> Sugg<'static> {
++        make_unop("&", self)
++    }
++
++    /// Convenience method to create the `&mut <expr>` suggestion.
++    pub fn mut_addr(self) -> Sugg<'static> {
++        make_unop("&mut ", self)
++    }
++
++    /// Convenience method to create the `*<expr>` suggestion.
++    pub fn deref(self) -> Sugg<'static> {
++        make_unop("*", self)
++    }
++
++    /// Convenience method to create the `&*<expr>` suggestion. Currently this
++    /// is needed because `sugg.deref().addr()` produces an unnecessary set of
++    /// parentheses around the deref.
++    pub fn addr_deref(self) -> Sugg<'static> {
++        make_unop("&*", self)
++    }
++
++    /// Convenience method to create the `&mut *<expr>` suggestion. Currently
++    /// this is needed because `sugg.deref().mut_addr()` produces an unnecessary
++    /// set of parentheses around the deref.
++    pub fn mut_addr_deref(self) -> Sugg<'static> {
++        make_unop("&mut *", self)
++    }
++
++    /// Convenience method to transform suggestion into a return call
++    pub fn make_return(self) -> Sugg<'static> {
++        Sugg::NonParen(Cow::Owned(format!("return {}", self)))
++    }
++
++    /// Convenience method to transform suggestion into a block
++    /// where the suggestion is a trailing expression
++    pub fn blockify(self) -> Sugg<'static> {
++        Sugg::NonParen(Cow::Owned(format!("{{ {} }}", self)))
++    }
++
++    /// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>`
++    /// suggestion.
++    #[allow(dead_code)]
++    pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> {
++        match limit {
++            ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, end),
++            ast::RangeLimits::Closed => make_assoc(AssocOp::DotDotEq, &self, end),
++        }
++    }
++
++    /// Adds parenthesis to any expression that might need them. Suitable to the
++    /// `self` argument of a method call
++    /// (e.g., to build `bar.foo()` or `(1 + 2).foo()`).
++    pub fn maybe_par(self) -> Self {
++        match self {
++            Sugg::NonParen(..) => self,
++            // `(x)` and `(x).y()` both don't need additional parens.
++            Sugg::MaybeParen(sugg) => {
++                if sugg.starts_with('(') && sugg.ends_with(')') {
++                    Sugg::MaybeParen(sugg)
++                } else {
++                    Sugg::NonParen(format!("({})", sugg).into())
++                }
++            },
++            Sugg::BinOp(_, sugg) => Sugg::NonParen(format!("({})", sugg).into()),
++        }
++    }
++}
++
++impl<'a, 'b> std::ops::Add<Sugg<'b>> for Sugg<'a> {
++    type Output = Sugg<'static>;
++    fn add(self, rhs: Sugg<'b>) -> Sugg<'static> {
++        make_binop(ast::BinOpKind::Add, &self, &rhs)
++    }
++}
++
++impl<'a, 'b> std::ops::Sub<Sugg<'b>> for Sugg<'a> {
++    type Output = Sugg<'static>;
++    fn sub(self, rhs: Sugg<'b>) -> Sugg<'static> {
++        make_binop(ast::BinOpKind::Sub, &self, &rhs)
++    }
++}
++
++impl<'a> std::ops::Not for Sugg<'a> {
++    type Output = Sugg<'static>;
++    fn not(self) -> Sugg<'static> {
++        make_unop("!", self)
++    }
++}
++
++/// Helper type to display either `foo` or `(foo)`.
++struct ParenHelper<T> {
++    /// `true` if parentheses are needed.
++    paren: bool,
++    /// The main thing to display.
++    wrapped: T,
++}
++
++impl<T> ParenHelper<T> {
++    /// Builds a `ParenHelper`.
++    fn new(paren: bool, wrapped: T) -> Self {
++        Self { paren, wrapped }
++    }
++}
++
++impl<T: Display> Display for ParenHelper<T> {
++    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
++        if self.paren {
++            write!(f, "({})", self.wrapped)
++        } else {
++            self.wrapped.fmt(f)
++        }
++    }
++}
++
++/// Builds the string for `<op><expr>` adding parenthesis when necessary.
++///
++/// For convenience, the operator is taken as a string because all unary
++/// operators have the same
++/// precedence.
++pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> {
++    Sugg::MaybeParen(format!("{}{}", op, expr.maybe_par()).into())
++}
++
++/// Builds the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
++///
++/// Precedence of shift operator relative to other arithmetic operation is
++/// often confusing so
++/// parenthesis will always be added for a mix of these.
++pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
++    /// Returns `true` if the operator is a shift operator `<<` or `>>`.
++    fn is_shift(op: &AssocOp) -> bool {
++        matches!(*op, AssocOp::ShiftLeft | AssocOp::ShiftRight)
++    }
++
++    /// Returns `true` if the operator is a arithmetic operator
++    /// (i.e., `+`, `-`, `*`, `/`, `%`).
++    fn is_arith(op: &AssocOp) -> bool {
++        matches!(
++            *op,
++            AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus
++        )
++    }
++
++    /// Returns `true` if the operator `op` needs parenthesis with the operator
++    /// `other` in the direction `dir`.
++    fn needs_paren(op: &AssocOp, other: &AssocOp, dir: Associativity) -> bool {
++        other.precedence() < op.precedence()
++            || (other.precedence() == op.precedence()
++                && ((op != other && associativity(op) != dir)
++                    || (op == other && associativity(op) != Associativity::Both)))
++            || is_shift(op) && is_arith(other)
++            || is_shift(other) && is_arith(op)
++    }
++
++    let lhs_paren = if let Sugg::BinOp(ref lop, _) = *lhs {
++        needs_paren(&op, lop, Associativity::Left)
++    } else {
++        false
++    };
++
++    let rhs_paren = if let Sugg::BinOp(ref rop, _) = *rhs {
++        needs_paren(&op, rop, Associativity::Right)
++    } else {
++        false
++    };
++
++    let lhs = ParenHelper::new(lhs_paren, lhs);
++    let rhs = ParenHelper::new(rhs_paren, rhs);
++    let sugg = match op {
++        AssocOp::Add
++        | AssocOp::BitAnd
++        | AssocOp::BitOr
++        | AssocOp::BitXor
++        | AssocOp::Divide
++        | AssocOp::Equal
++        | AssocOp::Greater
++        | AssocOp::GreaterEqual
++        | AssocOp::LAnd
++        | AssocOp::LOr
++        | AssocOp::Less
++        | AssocOp::LessEqual
++        | AssocOp::Modulus
++        | AssocOp::Multiply
++        | AssocOp::NotEqual
++        | AssocOp::ShiftLeft
++        | AssocOp::ShiftRight
++        | AssocOp::Subtract => format!(
++            "{} {} {}",
++            lhs,
++            op.to_ast_binop().expect("Those are AST ops").to_string(),
++            rhs
++        ),
++        AssocOp::Assign => format!("{} = {}", lhs, rhs),
++        AssocOp::AssignOp(op) => format!("{} {}= {}", lhs, token_kind_to_string(&token::BinOp(op)), rhs),
++        AssocOp::As => format!("{} as {}", lhs, rhs),
++        AssocOp::DotDot => format!("{}..{}", lhs, rhs),
++        AssocOp::DotDotEq => format!("{}..={}", lhs, rhs),
++        AssocOp::Colon => format!("{}: {}", lhs, rhs),
++    };
++
++    Sugg::BinOp(op, sugg.into())
++}
++
++/// Convenience wrapper around `make_assoc` and `AssocOp::from_ast_binop`.
++pub fn make_binop(op: ast::BinOpKind, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
++    make_assoc(AssocOp::from_ast_binop(op), lhs, rhs)
++}
++
++#[derive(PartialEq, Eq, Clone, Copy)]
++/// Operator associativity.
++enum Associativity {
++    /// The operator is both left-associative and right-associative.
++    Both,
++    /// The operator is left-associative.
++    Left,
++    /// The operator is not associative.
++    None,
++    /// The operator is right-associative.
++    Right,
++}
++
++/// Returns the associativity/fixity of an operator. The difference with
++/// `AssocOp::fixity` is that an operator can be both left and right associative
++/// (such as `+`: `a + b + c == (a + b) + c == a + (b + c)`.
++///
++/// Chained `as` and explicit `:` type coercion never need inner parenthesis so
++/// they are considered
++/// associative.
++#[must_use]
++fn associativity(op: &AssocOp) -> Associativity {
++    use rustc_ast::util::parser::AssocOp::{
++        Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Colon, Divide, DotDot, DotDotEq, Equal, Greater,
++        GreaterEqual, LAnd, LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract,
++    };
++
++    match *op {
++        Assign | AssignOp(_) => Associativity::Right,
++        Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As | Colon => Associativity::Both,
++        Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight
++        | Subtract => Associativity::Left,
++        DotDot | DotDotEq => Associativity::None,
++    }
++}
++
++/// Converts a `hir::BinOp` to the corresponding assigning binary operator.
++fn hirbinop2assignop(op: hir::BinOp) -> AssocOp {
++    use rustc_ast::token::BinOpToken::{And, Caret, Minus, Or, Percent, Plus, Shl, Shr, Slash, Star};
++
++    AssocOp::AssignOp(match op.node {
++        hir::BinOpKind::Add => Plus,
++        hir::BinOpKind::BitAnd => And,
++        hir::BinOpKind::BitOr => Or,
++        hir::BinOpKind::BitXor => Caret,
++        hir::BinOpKind::Div => Slash,
++        hir::BinOpKind::Mul => Star,
++        hir::BinOpKind::Rem => Percent,
++        hir::BinOpKind::Shl => Shl,
++        hir::BinOpKind::Shr => Shr,
++        hir::BinOpKind::Sub => Minus,
++
++        hir::BinOpKind::And
++        | hir::BinOpKind::Eq
++        | hir::BinOpKind::Ge
++        | hir::BinOpKind::Gt
++        | hir::BinOpKind::Le
++        | hir::BinOpKind::Lt
++        | hir::BinOpKind::Ne
++        | hir::BinOpKind::Or => panic!("This operator does not exist"),
++    })
++}
++
++/// Converts an `ast::BinOp` to the corresponding assigning binary operator.
++fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
++    use rustc_ast::ast::BinOpKind::{
++        Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
++    };
++    use rustc_ast::token::BinOpToken;
++
++    AssocOp::AssignOp(match op.node {
++        Add => BinOpToken::Plus,
++        BitAnd => BinOpToken::And,
++        BitOr => BinOpToken::Or,
++        BitXor => BinOpToken::Caret,
++        Div => BinOpToken::Slash,
++        Mul => BinOpToken::Star,
++        Rem => BinOpToken::Percent,
++        Shl => BinOpToken::Shl,
++        Shr => BinOpToken::Shr,
++        Sub => BinOpToken::Minus,
++        And | Eq | Ge | Gt | Le | Lt | Ne | Or => panic!("This operator does not exist"),
++    })
++}
++
++/// Returns the indentation before `span` if there are nothing but `[ \t]`
++/// before it on its line.
++fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> {
++    let lo = cx.sess().source_map().lookup_char_pos(span.lo());
++    if let Some(line) = lo.file.get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */) {
++        if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
++            // We can mix char and byte positions here because we only consider `[ \t]`.
++            if lo.col == CharPos(pos) {
++                Some(line[..pos].into())
++            } else {
++                None
++            }
++        } else {
++            None
++        }
++    } else {
++        None
++    }
++}
++
++/// Convenience extension trait for `DiagnosticBuilder`.
++pub trait DiagnosticBuilderExt<'a, T: LintContext> {
++    /// Suggests to add an attribute to an item.
++    ///
++    /// Correctly handles indentation of the attribute and item.
++    ///
++    /// # Example
++    ///
++    /// ```rust,ignore
++    /// diag.suggest_item_with_attr(cx, item, "#[derive(Default)]");
++    /// ```
++    fn suggest_item_with_attr<D: Display + ?Sized>(
++        &mut self,
++        cx: &T,
++        item: Span,
++        msg: &str,
++        attr: &D,
++        applicability: Applicability,
++    );
++
++    /// Suggest to add an item before another.
++    ///
++    /// The item should not be indented (expect for inner indentation).
++    ///
++    /// # Example
++    ///
++    /// ```rust,ignore
++    /// diag.suggest_prepend_item(cx, item,
++    /// "fn foo() {
++    ///     bar();
++    /// }");
++    /// ```
++    fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability);
++
++    /// Suggest to completely remove an item.
++    ///
++    /// This will remove an item and all following whitespace until the next non-whitespace
++    /// character. This should work correctly if item is on the same indentation level as the
++    /// following item.
++    ///
++    /// # Example
++    ///
++    /// ```rust,ignore
++    /// diag.suggest_remove_item(cx, item, "remove this")
++    /// ```
++    fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability);
++}
++
++impl<'a, 'b, 'c, T: LintContext> DiagnosticBuilderExt<'c, T> for rustc_errors::DiagnosticBuilder<'b> {
++    fn suggest_item_with_attr<D: Display + ?Sized>(
++        &mut self,
++        cx: &T,
++        item: Span,
++        msg: &str,
++        attr: &D,
++        applicability: Applicability,
++    ) {
++        if let Some(indent) = indentation(cx, item) {
++            let span = item.with_hi(item.lo());
++
++            self.span_suggestion(span, msg, format!("{}\n{}", attr, indent), applicability);
++        }
++    }
++
++    fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability) {
++        if let Some(indent) = indentation(cx, item) {
++            let span = item.with_hi(item.lo());
++
++            let mut first = true;
++            let new_item = new_item
++                .lines()
++                .map(|l| {
++                    if first {
++                        first = false;
++                        format!("{}\n", l)
++                    } else {
++                        format!("{}{}\n", indent, l)
++                    }
++                })
++                .collect::<String>();
++
++            self.span_suggestion(span, msg, format!("{}\n{}", new_item, indent), applicability);
++        }
++    }
++
++    fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability) {
++        let mut remove_span = item;
++        let hi = cx.sess().source_map().next_point(remove_span).hi();
++        let fmpos = cx.sess().source_map().lookup_byte_offset(hi);
++
++        if let Some(ref src) = fmpos.sf.src {
++            let non_whitespace_offset = src[fmpos.pos.to_usize()..].find(|c| c != ' ' && c != '\t' && c != '\n');
++
++            if let Some(non_whitespace_offset) = non_whitespace_offset {
++                remove_span = remove_span
++                    .with_hi(remove_span.hi() + BytePos(non_whitespace_offset.try_into().expect("offset too large")))
++            }
++        }
++
++        self.span_suggestion(remove_span, msg, String::new(), applicability);
++    }
++}
++
++#[cfg(test)]
++mod test {
++    use super::Sugg;
++    use std::borrow::Cow;
++
++    const SUGGESTION: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("function_call()"));
++
++    #[test]
++    fn make_return_transform_sugg_into_a_return_call() {
++        assert_eq!("return function_call()", SUGGESTION.make_return().to_string());
++    }
++
++    #[test]
++    fn blockify_transforms_sugg_into_a_block() {
++        assert_eq!("{ function_call() }", SUGGESTION.blockify().to_string());
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..273288c3d52c589db88d2f507b32e75534f24580
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++#[macro_export]
++macro_rules! sym {
++    ($tt:tt) => {
++        rustc_span::symbol::Symbol::intern(stringify!($tt))
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c14da6aacea047de74149e8d09921aa20ca1e33f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,108 @@@
++use crate::utils::match_var;
++use rustc_ast::ast;
++use rustc_data_structures::fx::FxHashSet;
++use rustc_hir::def::Res;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_hir::{Expr, HirId, Path};
++use rustc_infer::infer::TyCtxtInferExt;
++use rustc_lint::LateContext;
++use rustc_middle::hir::map::Map;
++use rustc_middle::ty;
++use rustc_span::symbol::Ident;
++use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase};
++
++/// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined.
++pub fn mutated_variables<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &'a LateContext<'a, 'tcx>) -> Option<FxHashSet<HirId>> {
++    let mut delegate = MutVarsDelegate {
++        used_mutably: FxHashSet::default(),
++        skip: false,
++    };
++    let def_id = expr.hir_id.owner.to_def_id();
++    cx.tcx.infer_ctxt().enter(|infcx| {
++        ExprUseVisitor::new(&mut delegate, &infcx, def_id.expect_local(), cx.param_env, cx.tables).walk_expr(expr);
++    });
++
++    if delegate.skip {
++        return None;
++    }
++    Some(delegate.used_mutably)
++}
++
++pub fn is_potentially_mutated<'a, 'tcx>(
++    variable: &'tcx Path<'_>,
++    expr: &'tcx Expr<'_>,
++    cx: &'a LateContext<'a, 'tcx>,
++) -> bool {
++    if let Res::Local(id) = variable.res {
++        mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&id))
++    } else {
++        true
++    }
++}
++
++struct MutVarsDelegate {
++    used_mutably: FxHashSet<HirId>,
++    skip: bool,
++}
++
++impl<'tcx> MutVarsDelegate {
++    #[allow(clippy::similar_names)]
++    fn update(&mut self, cat: &Place<'tcx>) {
++        match cat.base {
++            PlaceBase::Local(id) => {
++                self.used_mutably.insert(id);
++            },
++            PlaceBase::Upvar(_) => {
++                //FIXME: This causes false negatives. We can't get the `NodeId` from
++                //`Categorization::Upvar(_)`. So we search for any `Upvar`s in the
++                //`while`-body, not just the ones in the condition.
++                self.skip = true
++            },
++            _ => {},
++        }
++    }
++}
++
++impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
++    fn consume(&mut self, _: &Place<'tcx>, _: ConsumeMode) {}
++
++    fn borrow(&mut self, cmt: &Place<'tcx>, bk: ty::BorrowKind) {
++        if let ty::BorrowKind::MutBorrow = bk {
++            self.update(&cmt)
++        }
++    }
++
++    fn mutate(&mut self, cmt: &Place<'tcx>) {
++        self.update(&cmt)
++    }
++}
++
++pub struct UsedVisitor {
++    pub var: ast::Name, // var to look for
++    pub used: bool,     // has the var been used otherwise?
++}
++
++impl<'tcx> Visitor<'tcx> for UsedVisitor {
++    type Map = Map<'tcx>;
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        if match_var(expr, self.var) {
++            self.used = true;
++        } else {
++            walk_expr(self, expr);
++        }
++    }
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++}
++
++pub fn is_unused<'tcx>(ident: &'tcx Ident, body: &'tcx Expr<'_>) -> bool {
++    let mut visitor = UsedVisitor {
++        var: ident.name,
++        used: false,
++    };
++    walk_expr(&mut visitor, body);
++    !visitor.used
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1174f421577493b20c80dd230057bda7d116c57a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,106 @@@
++use crate::consts::constant;
++use crate::utils::{higher, is_copy, snippet_with_applicability, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{BorrowKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::{self, Ty};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would
++    /// be possible.
++    ///
++    /// **Why is this bad?** This is less efficient.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust,ignore
++    /// foo(&vec![1, 2])
++    /// ```
++    pub USELESS_VEC,
++    perf,
++    "useless `vec!`"
++}
++
++declare_lint_pass!(UselessVec => [USELESS_VEC]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessVec {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        // search for `&vec![_]` expressions where the adjusted type is `&[_]`
++        if_chain! {
++            if let ty::Ref(_, ty, _) = cx.tables.expr_ty_adjusted(expr).kind;
++            if let ty::Slice(..) = ty.kind;
++            if let ExprKind::AddrOf(BorrowKind::Ref, _, ref addressee) = expr.kind;
++            if let Some(vec_args) = higher::vec_macro(cx, addressee);
++            then {
++                check_vec_macro(cx, &vec_args, expr.span);
++            }
++        }
++
++        // search for `for _ in vec![…]`
++        if_chain! {
++            if let Some((_, arg, _)) = higher::for_loop(expr);
++            if let Some(vec_args) = higher::vec_macro(cx, arg);
++            if is_copy(cx, vec_type(cx.tables.expr_ty_adjusted(arg)));
++            then {
++                // report the error around the `vec!` not inside `<std macros>:`
++                let span = arg.span
++                    .ctxt()
++                    .outer_expn_data()
++                    .call_site
++                    .ctxt()
++                    .outer_expn_data()
++                    .call_site;
++                check_vec_macro(cx, &vec_args, span);
++            }
++        }
++    }
++}
++
++fn check_vec_macro<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) {
++    let mut applicability = Applicability::MachineApplicable;
++    let snippet = match *vec_args {
++        higher::VecArgs::Repeat(elem, len) => {
++            if constant(cx, cx.tables, len).is_some() {
++                format!(
++                    "&[{}; {}]",
++                    snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
++                    snippet_with_applicability(cx, len.span, "len", &mut applicability)
++                )
++            } else {
++                return;
++            }
++        },
++        higher::VecArgs::Vec(args) => {
++            if let Some(last) = args.iter().last() {
++                let span = args[0].span.to(last.span);
++
++                format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability))
++            } else {
++                "&[]".into()
++            }
++        },
++    };
++
++    span_lint_and_sugg(
++        cx,
++        USELESS_VEC,
++        span,
++        "useless use of `vec!`",
++        "you can use a slice directly",
++        snippet,
++        applicability,
++    );
++}
++
++/// Returns the item type of the vector (i.e., the `T` in `Vec<T>`).
++fn vec_type(ty: Ty<'_>) -> Ty<'_> {
++    if let ty::Adt(_, substs) = ty.kind {
++        substs.type_at(0)
++    } else {
++        panic!("The type of `vec!` is a not a struct?");
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4d8d4438d881dade5084b16ccbb730e2019a4438
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,85 @@@
++use crate::utils::{match_type, paths, span_lint_and_help};
++use if_chain::if_chain;
++use rustc_hir::{Expr, ExprKind, QPath};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for use of File::read_to_end and File::read_to_string.
++    ///
++    /// **Why is this bad?** `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
++    /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust,no_run
++    /// # use std::io::Read;
++    /// # use std::fs::File;
++    /// let mut f = File::open("foo.txt").unwrap();
++    /// let mut bytes = Vec::new();
++    /// f.read_to_end(&mut bytes).unwrap();
++    /// ```
++    /// Can be written more concisely as
++    /// ```rust,no_run
++    /// # use std::fs;
++    /// let mut bytes = fs::read("foo.txt").unwrap();
++    /// ```
++    pub VERBOSE_FILE_READS,
++    restriction,
++    "use of `File::read_to_end` or `File::read_to_string`"
++}
++
++declare_lint_pass!(VerboseFileReads => [VERBOSE_FILE_READS]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VerboseFileReads {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) {
++        if is_file_read_to_end(cx, expr) {
++            span_lint_and_help(
++                cx,
++                VERBOSE_FILE_READS,
++                expr.span,
++                "use of `File::read_to_end`",
++                None,
++                "consider using `fs::read` instead",
++            );
++        } else if is_file_read_to_string(cx, expr) {
++            span_lint_and_help(
++                cx,
++                VERBOSE_FILE_READS,
++                expr.span,
++                "use of `File::read_to_string`",
++                None,
++                "consider using `fs::read_to_string` instead",
++            )
++        }
++    }
++}
++
++fn is_file_read_to_end<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
++    if_chain! {
++        if let ExprKind::MethodCall(method_name, _, exprs) = expr.kind;
++        if method_name.ident.as_str() == "read_to_end";
++        if let ExprKind::Path(QPath::Resolved(None, _)) = &exprs[0].kind;
++        let ty = cx.tables.expr_ty(&exprs[0]);
++        if match_type(cx, ty, &paths::FILE);
++        then {
++            return true
++        }
++    }
++    false
++}
++
++fn is_file_read_to_string<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
++    if_chain! {
++        if let ExprKind::MethodCall(method_name, _, exprs) = expr.kind;
++        if method_name.ident.as_str() == "read_to_string";
++        if let ExprKind::Path(QPath::Resolved(None, _)) = &exprs[0].kind;
++        let ty = cx.tables.expr_ty(&exprs[0]);
++        if match_type(cx, ty, &paths::FILE);
++        then {
++            return true
++        }
++    }
++    false
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d8d48eb15358d19d70cb82b07343e476714440ae
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++use crate::utils::{run_lints, span_lint};
++use rustc_hir::{hir_id::CRATE_HIR_ID, Crate};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::DUMMY_SP;
++
++use if_chain::if_chain;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for wildcard dependencies in the `Cargo.toml`.
++    ///
++    /// **Why is this bad?** [As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html),
++    /// it is highly unlikely that you work with any possible version of your dependency,
++    /// and wildcard dependencies would cause unnecessary breakage in the ecosystem.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```toml
++    /// [dependencies]
++    /// regex = "*"
++    /// ```
++    pub WILDCARD_DEPENDENCIES,
++    cargo,
++    "wildcard dependencies being used"
++}
++
++declare_lint_pass!(WildcardDependencies => [WILDCARD_DEPENDENCIES]);
++
++impl LateLintPass<'_, '_> for WildcardDependencies {
++    fn check_crate(&mut self, cx: &LateContext<'_, '_>, _: &Crate<'_>) {
++        if !run_lints(cx, &[WILDCARD_DEPENDENCIES], CRATE_HIR_ID) {
++            return;
++        }
++
++        let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().no_deps().exec() {
++            metadata
++        } else {
++            span_lint(cx, WILDCARD_DEPENDENCIES, DUMMY_SP, "could not read cargo metadata");
++            return;
++        };
++
++        for dep in &metadata.packages[0].dependencies {
++            // VersionReq::any() does not work
++            if_chain! {
++                if let Ok(wildcard_ver) = semver::VersionReq::parse("*");
++                if let Some(ref source) = dep.source;
++                if !source.starts_with("git");
++                if dep.req == wildcard_ver;
++                then {
++                    span_lint(
++                        cx,
++                        WILDCARD_DEPENDENCIES,
++                        DUMMY_SP,
++                        &format!("wildcard dependency for `{}`", dep.name),
++                    );
++                }
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f3038861cee2431c41795dd5db020f0fcedc16fd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,156 @@@
++use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_sugg};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{
++    def::{DefKind, Res},
++    Item, ItemKind, UseKind,
++};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::BytePos;
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `use Enum::*`.
++    ///
++    /// **Why is this bad?** It is usually better style to use the prefixed name of
++    /// an enumeration variant, rather than importing variants.
++    ///
++    /// **Known problems:** Old-style enumerations that prefix the variants are
++    /// still around.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// use std::cmp::Ordering::*;
++    /// ```
++    pub ENUM_GLOB_USE,
++    pedantic,
++    "use items that import all variants of an enum"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for wildcard imports `use _::*`.
++    ///
++    /// **Why is this bad?** wildcard imports can polute the namespace. This is especially bad if
++    /// you try to import something through a wildcard, that already has been imported by name from
++    /// a different source:
++    ///
++    /// ```rust,ignore
++    /// use crate1::foo; // Imports a function named foo
++    /// use crate2::*; // Has a function named foo
++    ///
++    /// foo(); // Calls crate1::foo
++    /// ```
++    ///
++    /// This can lead to confusing error messages at best and to unexpected behavior at worst.
++    ///
++    /// Note that this will not warn about wildcard imports from modules named `prelude`; many
++    /// crates (including the standard library) provide modules named "prelude" specifically
++    /// designed for wildcard import.
++    ///
++    /// **Known problems:** If macros are imported through the wildcard, this macro is not included
++    /// by the suggestion and has to be added by hand.
++    ///
++    /// Applying the suggestion when explicit imports of the things imported with a glob import
++    /// exist, may result in `unused_imports` warnings.
++    ///
++    /// **Example:**
++    ///
++    /// Bad:
++    /// ```rust,ignore
++    /// use crate1::*;
++    ///
++    /// foo();
++    /// ```
++    ///
++    /// Good:
++    /// ```rust,ignore
++    /// use crate1::foo;
++    ///
++    /// foo();
++    /// ```
++    pub WILDCARD_IMPORTS,
++    pedantic,
++    "lint `use _::*` statements"
++}
++
++declare_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]);
++
++impl LateLintPass<'_, '_> for WildcardImports {
++    fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) {
++        if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() {
++            return;
++        }
++        if_chain! {
++            if !in_macro(item.span);
++            if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind;
++            // don't lint prelude glob imports
++            if !use_path.segments.iter().last().map_or(false, |ps| ps.ident.as_str() == "prelude");
++            let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner);
++            if !used_imports.is_empty(); // Already handled by `unused_imports`
++            then {
++                let mut applicability = Applicability::MachineApplicable;
++                let import_source_snippet = snippet_with_applicability(cx, use_path.span, "..", &mut applicability);
++                let (span, braced_glob) = if import_source_snippet.is_empty() {
++                    // This is a `_::{_, *}` import
++                    // In this case `use_path.span` is empty and ends directly in front of the `*`,
++                    // so we need to extend it by one byte.
++                    (
++                        use_path.span.with_hi(use_path.span.hi() + BytePos(1)),
++                        true,
++                    )
++                } else {
++                    // In this case, the `use_path.span` ends right before the `::*`, so we need to
++                    // extend it up to the `*`. Since it is hard to find the `*` in weird
++                    // formattings like `use _ ::  *;`, we extend it up to, but not including the
++                    // `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we
++                    // can just use the end of the item span
++                    let mut span = use_path.span.with_hi(item.span.hi());
++                    if snippet(cx, span, "").ends_with(';') {
++                        span = use_path.span.with_hi(item.span.hi() - BytePos(1));
++                    }
++                    (
++                        span,
++                        false,
++                    )
++                };
++
++                let imports_string = if used_imports.len() == 1 {
++                    used_imports.iter().next().unwrap().to_string()
++                } else {
++                    let mut imports = used_imports
++                        .iter()
++                        .map(ToString::to_string)
++                        .collect::<Vec<_>>();
++                    imports.sort();
++                    if braced_glob {
++                        imports.join(", ")
++                    } else {
++                        format!("{{{}}}", imports.join(", "))
++                    }
++                };
++
++                let sugg = if braced_glob {
++                    imports_string
++                } else {
++                    format!("{}::{}", import_source_snippet, imports_string)
++                };
++
++                let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res {
++                    (ENUM_GLOB_USE, "usage of wildcard import for enum variants")
++                } else {
++                    (WILDCARD_IMPORTS, "usage of wildcard import")
++                };
++
++                span_lint_and_sugg(
++                    cx,
++                    lint,
++                    span,
++                    message,
++                    "try",
++                    sugg,
++                    applicability,
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5ad43ad55a36ab7ae3cb7012ee765dba490764f9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,491 @@@
++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 rustc_ast::ast::{Expr, ExprKind, Item, ItemKind, MacCall, StrLit, StrStyle};
++use rustc_ast::token;
++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::Symbol;
++use rustc_span::{BytePos, Span};
++
++declare_clippy_lint! {
++    /// **What it does:** This lint warns when you use `println!("")` to
++    /// print a newline.
++    ///
++    /// **Why is this bad?** You should use `println!()`, which is simpler.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// println!("");
++    /// ```
++    pub PRINTLN_EMPTY_STRING,
++    style,
++    "using `println!(\"\")` with an empty string"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** This lint warns when you use `print!()` with a format
++    /// string that
++    /// ends in a newline.
++    ///
++    /// **Why is this bad?** You should use `println!()` instead, which appends the
++    /// newline.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let name = "World";
++    /// print!("Hello {}!\n", name);
++    /// ```
++    /// use println!() instead
++    /// ```rust
++    /// # let name = "World";
++    /// println!("Hello {}!", name);
++    /// ```
++    pub PRINT_WITH_NEWLINE,
++    style,
++    "using `print!()` with a format string that ends in a single newline"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for printing on *stdout*. The purpose of this lint
++    /// is to catch debugging remnants.
++    ///
++    /// **Why is this bad?** People often print on *stdout* while debugging an
++    /// application and might forget to remove those prints afterward.
++    ///
++    /// **Known problems:** Only catches `print!` and `println!` calls.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// println!("Hello world!");
++    /// ```
++    pub PRINT_STDOUT,
++    restriction,
++    "printing on stdout"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for use of `Debug` formatting. The purpose of this
++    /// lint is to catch debugging remnants.
++    ///
++    /// **Why is this bad?** The purpose of the `Debug` trait is to facilitate
++    /// debugging Rust code. It should not be used in user-facing output.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # let foo = "bar";
++    /// println!("{:?}", foo);
++    /// ```
++    pub USE_DEBUG,
++    restriction,
++    "use of `Debug`-based formatting"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** This lint warns about the use of literals as `print!`/`println!` args.
++    ///
++    /// **Why is this bad?** Using literals as `println!` args is inefficient
++    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
++    /// (i.e., just put the literal in the format string)
++    ///
++    /// **Known problems:** Will also warn with macro calls as arguments that expand to literals
++    /// -- e.g., `println!("{}", env!("FOO"))`.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// println!("{}", "foo");
++    /// ```
++    /// use the literal without formatting:
++    /// ```rust
++    /// println!("foo");
++    /// ```
++    pub PRINT_LITERAL,
++    style,
++    "printing a literal with a format string"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** This lint warns when you use `writeln!(buf, "")` to
++    /// print a newline.
++    ///
++    /// **Why is this bad?** You should use `writeln!(buf)`, which is simpler.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # use std::fmt::Write;
++    /// # let mut buf = String::new();
++    /// writeln!(buf, "");
++    /// ```
++    pub WRITELN_EMPTY_STRING,
++    style,
++    "using `writeln!(buf, \"\")` with an empty string"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** This lint warns when you use `write!()` with a format
++    /// string that
++    /// ends in a newline.
++    ///
++    /// **Why is this bad?** You should use `writeln!()` instead, which appends the
++    /// newline.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # use std::fmt::Write;
++    /// # let mut buf = String::new();
++    /// # let name = "World";
++    /// write!(buf, "Hello {}!\n", name);
++    /// ```
++    pub WRITE_WITH_NEWLINE,
++    style,
++    "using `write!()` with a format string that ends in a single newline"
++}
++
++declare_clippy_lint! {
++    /// **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.
++    ///
++    /// **Why is this bad?** Using literals as `writeln!` args is inefficient
++    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
++    /// (i.e., just put the literal in the format string)
++    ///
++    /// **Known problems:** Will also warn with macro calls as arguments that expand to literals
++    /// -- e.g., `writeln!(buf, "{}", env!("FOO"))`.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// # use std::fmt::Write;
++    /// # let mut buf = String::new();
++    /// writeln!(buf, "{}", "foo");
++    /// ```
++    pub WRITE_LITERAL,
++    style,
++    "writing a literal with a format string"
++}
++
++#[derive(Default)]
++pub struct Write {
++    in_debug_impl: bool,
++}
++
++impl_lint_pass!(Write => [
++    PRINT_WITH_NEWLINE,
++    PRINTLN_EMPTY_STRING,
++    PRINT_STDOUT,
++    USE_DEBUG,
++    PRINT_LITERAL,
++    WRITE_WITH_NEWLINE,
++    WRITELN_EMPTY_STRING,
++    WRITE_LITERAL
++]);
++
++impl EarlyLintPass for Write {
++    fn check_item(&mut self, _: &EarlyContext<'_>, item: &Item) {
++        if let ItemKind::Impl {
++            of_trait: Some(trait_ref),
++            ..
++        } = &item.kind
++        {
++            let trait_name = trait_ref
++                .path
++                .segments
++                .iter()
++                .last()
++                .expect("path has at least one segment")
++                .ident
++                .name;
++            if trait_name == sym!(Debug) {
++                self.in_debug_impl = true;
++            }
++        }
++    }
++
++    fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &Item) {
++        self.in_debug_impl = false;
++    }
++
++    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
++        if mac.path == sym!(println) {
++            span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
++            if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) {
++                if fmt_str.symbol == Symbol::intern("") {
++                    span_lint_and_sugg(
++                        cx,
++                        PRINTLN_EMPTY_STRING,
++                        mac.span(),
++                        "using `println!(\"\")`",
++                        "replace it with",
++                        "println!()".to_string(),
++                        Applicability::MachineApplicable,
++                    );
++                }
++            }
++        } else if mac.path == sym!(print) {
++            span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
++            if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) {
++                if check_newlines(&fmt_str) {
++                    span_lint_and_then(
++                        cx,
++                        PRINT_WITH_NEWLINE,
++                        mac.span(),
++                        "using `print!()` with a format string that ends in a single newline",
++                        |err| {
++                            err.multipart_suggestion(
++                                "use `println!` instead",
++                                vec![
++                                    (mac.path.span, String::from("println")),
++                                    (newline_span(&fmt_str), String::new()),
++                                ],
++                                Applicability::MachineApplicable,
++                            );
++                        },
++                    );
++                }
++            }
++        } else if mac.path == sym!(write) {
++            if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), true) {
++                if check_newlines(&fmt_str) {
++                    span_lint_and_then(
++                        cx,
++                        WRITE_WITH_NEWLINE,
++                        mac.span(),
++                        "using `write!()` with a format string that ends in a single newline",
++                        |err| {
++                            err.multipart_suggestion(
++                                "use `writeln!()` instead",
++                                vec![
++                                    (mac.path.span, String::from("writeln")),
++                                    (newline_span(&fmt_str), String::new()),
++                                ],
++                                Applicability::MachineApplicable,
++                            );
++                        },
++                    )
++                }
++            }
++        } else if mac.path == sym!(writeln) {
++            if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) {
++                if fmt_str.symbol == Symbol::intern("") {
++                    let mut applicability = Applicability::MachineApplicable;
++                    let suggestion = expr.map_or_else(
++                        move || {
++                            applicability = Applicability::HasPlaceholders;
++                            Cow::Borrowed("v")
++                        },
++                        move |expr| snippet_with_applicability(cx, expr.span, "v", &mut applicability),
++                    );
++
++                    span_lint_and_sugg(
++                        cx,
++                        WRITELN_EMPTY_STRING,
++                        mac.span(),
++                        format!("using `writeln!({}, \"\")`", suggestion).as_str(),
++                        "replace it with",
++                        format!("writeln!({})", suggestion),
++                        applicability,
++                    );
++                }
++            }
++        }
++    }
++}
++
++/// Given a format string that ends in a newline and its span, calculates the span of the
++/// newline.
++fn newline_span(fmtstr: &StrLit) -> Span {
++    let sp = fmtstr.span;
++    let contents = &fmtstr.symbol.as_str();
++
++    let newline_sp_hi = sp.hi()
++        - match fmtstr.style {
++            StrStyle::Cooked => BytePos(1),
++            StrStyle::Raw(hashes) => BytePos((1 + hashes).into()),
++        };
++
++    let newline_sp_len = if contents.ends_with('\n') {
++        BytePos(1)
++    } else if contents.ends_with(r"\n") {
++        BytePos(2)
++    } else {
++        panic!("expected format string to contain a newline");
++    };
++
++    sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi)
++}
++
++impl Write {
++    /// 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
++    /// source. The second `Option` in the tuple is, in the `write[ln]!` case, the expression the
++    /// `format_str` should be written to.
++    ///
++    /// Example:
++    ///
++    /// Calling this function on
++    /// ```rust
++    /// # use std::fmt::Write;
++    /// # let mut buf = String::new();
++    /// # let something = "something";
++    /// writeln!(buf, "string to write: {}", something);
++    /// ```
++    /// will return
++    /// ```rust,ignore
++    /// (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 fmt_macros::{
++            AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec, Parser, Piece,
++        };
++        let tts = tts.clone();
++
++        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 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, Vec::new(), false);
++        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 lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
++        let mut idx = 0;
++        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 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(_) => {
++                    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 let ExprKind::Lit(_) = rhs.kind {
++                        if let ExprKind::Path(_, p) = &lhs.kind {
++                            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");
++                            }
++                        }
++                    }
++                },
++                _ => idx += 1,
++            }
++        }
++    }
++}
++
++/// Checks if the format string contains a single newline that terminates it.
++///
++/// Literal and escaped newlines are both checked (only literal for raw strings).
++fn check_newlines(fmtstr: &StrLit) -> bool {
++    let mut has_internal_newline = false;
++    let mut last_was_cr = false;
++    let mut should_lint = false;
++
++    let contents = &fmtstr.symbol.as_str();
++
++    let mut cb = |r: Range<usize>, c: Result<char, EscapeError>| {
++        let c = c.unwrap();
++
++        if r.end == contents.len() && c == '\n' && !last_was_cr && !has_internal_newline {
++            should_lint = true;
++        } else {
++            last_was_cr = c == '\r';
++            if c == '\n' {
++                has_internal_newline = true;
++            }
++        }
++    };
++
++    match fmtstr.style {
++        StrStyle::Cooked => unescape::unescape_str(contents, &mut cb),
++        StrStyle::Raw(_) => unescape::unescape_raw_str(contents, &mut cb),
++    }
++
++    should_lint
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fb4700d8743fdab5465c66a4668a97d66702de57
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++use crate::consts::{constant_simple, Constant};
++use crate::utils::span_lint_and_help;
++use if_chain::if_chain;
++use rustc_hir::{BinOpKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// **What it does:** Checks for `0.0 / 0.0`.
++    ///
++    /// **Why is this bad?** It's less readable than `f32::NAN` or `f64::NAN`.
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    /// ```rust
++    /// 0.0f32 / 0.0;
++    /// ```
++    pub ZERO_DIVIDED_BY_ZERO,
++    complexity,
++    "usage of `0.0 / 0.0` to obtain NaN instead of `f32::NAN` or `f64::NAN`"
++}
++
++declare_lint_pass!(ZeroDiv => [ZERO_DIVIDED_BY_ZERO]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ZeroDiv {
++    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
++        // check for instances of 0.0/0.0
++        if_chain! {
++            if let ExprKind::Binary(ref op, ref left, ref right) = expr.kind;
++            if let BinOpKind::Div = op.node;
++            // TODO - constant_simple does not fold many operations involving floats.
++            // That's probably fine for this lint - it's pretty unlikely that someone would
++            // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too.
++            if let Some(lhs_value) = constant_simple(cx, cx.tables, left);
++            if let Some(rhs_value) = constant_simple(cx, cx.tables, right);
++            if Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value;
++            if Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value;
++            then {
++                // since we're about to suggest a use of f32::NAN or f64::NAN,
++                // match the precision of the literals that are given.
++                let float_type = match (lhs_value, rhs_value) {
++                    (Constant::F64(_), _)
++                    | (_, Constant::F64(_)) => "f64",
++                    _ => "f32"
++                };
++                span_lint_and_help(
++                    cx,
++                    ZERO_DIVIDED_BY_ZERO,
++                    expr.span,
++                    "constant division of `0.0` with `0.0` will always result in NaN",
++                    None,
++                    &format!(
++                        "Consider using `{}::NAN` if you would like a constant representing NaN",
++                        float_type,
++                    ),
++                );
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7a235b215d38d418b8ac0835f404706e72efc5f8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++[package]
++name = "clippy_workspace_tests"
++version = "0.1.0"
++edition = "2018"
++
++[workspace]
++members = ["subcrate"]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b322eca1db51d2cf71da534d254f47be4213c6d1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++#![deny(rust_2018_idioms)]
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..83ea5868160bbaeae2cd25b3a37a4e0b4ba38b2c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++[package]
++name = "subcrate"
++version = "0.1.0"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9ad1315c17521c146f2d396ae38f5a9f7b1d8017
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,477 @@@
++# Adding a new lint
++
++You are probably here because you want to add a new lint to Clippy. If this is
++the first time you're contributing to Clippy, this document guides you through
++creating an example lint from scratch.
++
++To get started, we will create a lint that detects functions called `foo`,
++because that's clearly a non-descriptive name.
++
++- [Adding a new lint](#adding-a-new-lint)
++  - [Setup](#setup)
++  - [Getting Started](#getting-started)
++  - [Testing](#testing)
++  - [Rustfix tests](#rustfix-tests)
++  - [Edition 2018 tests](#edition-2018-tests)
++  - [Testing manually](#testing-manually)
++  - [Lint declaration](#lint-declaration)
++  - [Lint passes](#lint-passes)
++  - [Emitting a lint](#emitting-a-lint)
++  - [Adding the lint logic](#adding-the-lint-logic)
++  - [Author lint](#author-lint)
++  - [Documentation](#documentation)
++  - [Running rustfmt](#running-rustfmt)
++  - [Debugging](#debugging)
++  - [PR Checklist](#pr-checklist)
++  - [Cheatsheet](#cheatsheet)
++
++## Setup
++
++When working on Clippy, you will need the current git master version of rustc,
++which can change rapidly. Make sure you're working near rust-clippy's master,
++and use the `setup-toolchain.sh` script to configure the appropriate toolchain
++for the Clippy directory.
++
++## Getting Started
++
++There is a bit of boilerplate code that needs to be set up when creating a new
++lint. Fortunately, you can use the clippy dev tools to handle this for you. We
++are naming our new lint `foo_functions` (lints are generally written in snake
++case), and we don't need type information so it will have an early pass type
++(more on this later on). To get started on this lint you can run
++`cargo dev new_lint --name=foo_functions --pass=early --category=pedantic`
++(category will default to nursery if not provided). This command will create
++two files: `tests/ui/foo_functions.rs` and `clippy_lints/src/foo_functions.rs`,
++as well as run `cargo dev update_lints` to register the new lint. Next, we'll
++open up these files and add our lint!
++
++## Testing
++
++Let's write some tests first that we can execute while we iterate on our lint.
++
++Clippy uses UI tests for testing. UI tests check that the output of Clippy is
++exactly as expected. Each test is just a plain Rust file that contains the code
++we want to check. The output of Clippy is compared against a `.stderr` file.
++Note that you don't have to create this file yourself, we'll get to
++generating the `.stderr` files further down.
++
++We start by opening the test file created at `tests/ui/foo_functions.rs`.
++
++Update the file with some examples to get started:
++
++```rust
++#![warn(clippy::foo_functions)]
++
++// Impl methods
++struct A;
++impl A {
++    pub fn fo(&self) {}
++    pub fn foo(&self) {}
++    pub fn food(&self) {}
++}
++
++// Default trait methods
++trait B {
++    fn fo(&self) {}
++    fn foo(&self) {}
++    fn food(&self) {}
++}
++
++// Plain functions
++fn fo() {}
++fn foo() {}
++fn food() {}
++
++fn main() {
++    // We also don't want to lint method calls
++    foo();
++    let a = A;
++    a.foo();
++}
++```
++
++Now we can run the test with `TESTNAME=foo_functions cargo uitest`,
++currently this test is meaningless though.
++
++While we are working on implementing our lint, we can keep running the UI
++test. That allows us to check if the output is turning into what we want.
++
++Once we are satisfied with the output, we need to run
++`tests/ui/update-all-references.sh` to update the `.stderr` file for our lint.
++Please note that, we should run `TESTNAME=foo_functions cargo uitest`
++every time before running `tests/ui/update-all-references.sh`.
++Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit
++our lint, we need to commit the generated `.stderr` files, too. In general, you
++should only commit files changed by `tests/ui/update-all-references.sh` for the
++specific lint you are creating/editing.
++
++## Rustfix tests
++
++If the lint you are working on is making use of structured suggestions, the
++test file should include a `// run-rustfix` comment at the top. This will
++additionally run [rustfix] for that test. Rustfix will apply the suggestions
++from the lint to the code of the test file and compare that to the contents of
++a `.fixed` file.
++
++Use `tests/ui/update-all-references.sh` to automatically generate the
++`.fixed` file after running the tests.
++
++[rustfix]: https://github.com/rust-lang/rustfix
++
++## Edition 2018 tests
++
++Some features require the 2018 edition to work (e.g. `async_await`), but
++compile-test tests run on the 2015 edition by default. To change this behavior
++add `// edition:2018` at the top of the test file (note that it's space-sensitive).
++
++## Testing manually
++
++Manually testing against an example file can be useful if you have added some
++`println!`s and the test suite output becomes unreadable. To try Clippy with
++your local modifications, run `env CLIPPY_TESTS=true cargo run --bin
++clippy-driver -- -L ./target/debug input.rs` from the working copy root.
++
++With tests in place, let's have a look at implementing our lint now.
++
++## Lint declaration
++
++Let's start by opening the new file created in the `clippy_lints` crate
++at `clippy_lints/src/foo_functions.rs`. That's the crate where all the
++lint code is. This file has already imported some initial things we will need:
++
++```rust
++use rustc_lint::{EarlyLintPass, EarlyContext};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_ast::ast::*;
++```
++
++The next step is to update the lint declaration. Lints are declared using the
++[`declare_clippy_lint!`][declare_clippy_lint] macro, and we just need to update
++the auto-generated lint declaration to have a real description, something like this:
++
++```rust
++declare_clippy_lint! {
++    /// **What it does:**
++    ///
++    /// **Why is this bad?**
++    ///
++    /// **Known problems:** None.
++    ///
++    /// **Example:**
++    ///
++    /// ```rust
++    /// // example code
++    /// ```
++    pub FOO_FUNCTIONS,
++    pedantic,
++    "function named `foo`, which is not a descriptive name"
++}
++```
++
++* The section of lines prefixed with `///` constitutes the lint documentation
++  section. This is the default documentation style and will be displayed
++  [like this][example_lint_page].
++* `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the
++  [lint naming guidelines][lint_naming] here when naming your lint.
++  In short, the name should state the thing that is being checked for and
++  read well when used with `allow`/`warn`/`deny`.
++* `pedantic` sets the lint level to `Allow`.
++  The exact mapping can be found [here][category_level_mapping]
++* The last part should be a text that explains what exactly is wrong with the
++  code
++
++The rest of this file contains an empty implementation for our lint pass,
++which in this case is `EarlyLintPass` and should look like this:
++
++```rust
++// clippy_lints/src/foo_functions.rs
++
++// .. imports and lint declaration ..
++
++declare_lint_pass!(FooFunctions => [FOO_FUNCTIONS]);
++
++impl EarlyLintPass for FooFunctions {}
++```
++
++Normally after declaring the lint, we have to run `cargo dev update_lints`,
++which updates some files, so Clippy knows about the new lint. Since we used
++`cargo dev new_lint ...` to generate the lint declaration, this was done
++automatically. While `update_lints` automates most of the things, it doesn't
++automate everything. We will have to register our lint pass manually in the
++`register_plugins` function in `clippy_lints/src/lib.rs`:
++
++```rust
++store.register_early_pass(|| box foo_functions::FooFunctions);
++```
++
++[declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60
++[example_lint_page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
++[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
++[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110
++
++## Lint passes
++
++Writing a lint that only checks for the name of a function means that we only
++have to deal with the AST and don't have to deal with the type system at all.
++This is good, because it makes writing this particular lint less complicated.
++
++We have to make this decision with every new Clippy lint. It boils down to using
++either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass].
++
++In short, the `LateLintPass` has access to type information while the
++`EarlyLintPass` doesn't. If you don't need access to type information, use the
++`EarlyLintPass`. The `EarlyLintPass` is also faster. However linting speed
++hasn't really been a concern with Clippy so far.
++
++Since we don't need type information for checking the function name, we used
++`--pass=early` when running the new lint automation and all the imports were
++added accordingly.
++
++[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
++[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
++
++## Emitting a lint
++
++With UI tests and the lint declaration in place, we can start working on the
++implementation of the lint logic.
++
++Let's start by implementing the `EarlyLintPass` for our `FooFunctions`:
++
++```rust
++impl EarlyLintPass for FooFunctions {
++    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
++        // TODO: Emit lint here
++    }
++}
++```
++
++We implement the [`check_fn`][check_fn] method from the
++[`EarlyLintPass`][early_lint_pass] trait. This gives us access to various
++information about the function that is currently being checked. More on that in
++the next section. Let's worry about the details later and emit our lint for
++*every* function definition first.
++
++Depending on how complex we want our lint message to be, we can choose from a
++variety of lint emission functions. They can all be found in
++[`clippy_lints/src/utils/diagnostics.rs`][diagnostics].
++
++`span_lint_and_help` seems most appropriate in this case. It allows us to
++provide an extra help message and we can't really suggest a better name
++automatically. This is how it looks:
++
++```rust
++impl EarlyLintPass for FooFunctions {
++    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
++        span_lint_and_help(
++            cx,
++            FOO_FUNCTIONS,
++            span,
++            "function named `foo`",
++            None,
++            "consider using a more meaningful name"
++        );
++    }
++}
++```
++
++Running our UI test should now produce output that contains the lint message.
++
++[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
++[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs
++
++## Adding the lint logic
++
++Writing the logic for your lint will most likely be different from our example,
++so this section is kept rather short.
++
++Using the [`check_fn`][check_fn] method gives us access to [`FnKind`][fn_kind]
++that has the [`FnKind::Fn`] variant. It provides access to the name of the
++function/method via an [`Ident`][ident].
++
++With that we can expand our `check_fn` method to:
++
++```rust
++impl EarlyLintPass for FooFunctions {
++    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
++        if is_foo_fn(fn_kind) {
++            span_lint_and_help(
++                cx,
++                FOO_FUNCTIONS,
++                span,
++                "function named `foo`",
++                None,
++                "consider using a more meaningful name"
++            );
++        }
++    }
++}
++```
++
++We separate the lint conditional from the lint emissions because it makes the
++code a bit easier to read. In some cases this separation would also allow to
++write some unit tests (as opposed to only UI tests) for the separate function.
++
++In our example, `is_foo_fn` looks like:
++
++```rust
++// use statements, impl EarlyLintPass, check_fn, ..
++
++fn is_foo_fn(fn_kind: FnKind<'_>) -> bool {
++    match fn_kind {
++        FnKind::Fn(_, ident, ..) => {
++            // check if `fn` name is `foo`
++            ident.name.as_str() == "foo"
++        }
++        // ignore closures
++        FnKind::Closure(..) => false
++    }
++}
++```
++
++Now we should also run the full test suite with `cargo test`. At this point
++running `cargo test` should produce the expected output. Remember to run
++`tests/ui/update-all-references.sh` to update the `.stderr` file.
++
++`cargo test` (as opposed to `cargo uitest`) will also ensure that our lint
++implementation is not violating any Clippy lints itself.
++
++That should be it for the lint implementation. Running `cargo test` should now
++pass.
++
++[fn_kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html
++[`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn
++[ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html
++
++## Author lint
++
++If you have trouble implementing your lint, there is also the internal `author`
++lint to generate Clippy code that detects the offending pattern. It does not
++work for all of the Rust syntax, but can give a good starting point.
++
++The quickest way to use it, is the
++[Rust playground: play.rust-lang.org][author_example].
++Put the code you want to lint into the editor and add the `#[clippy::author]`
++attribute above the item. Then run Clippy via `Tools -> Clippy` and you should
++see the generated code in the output below.
++
++[Here][author_example] is an example on the playground.
++
++If the command was executed successfully, you can copy the code over to where
++you are implementing your lint.
++
++[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
++
++## Documentation
++
++The final thing before submitting our PR is to add some documentation to our
++lint declaration.
++
++Please document your lint with a doc comment akin to the following:
++
++```rust
++declare_clippy_lint! {
++    /// **What it does:** Checks for ... (describe what the lint matches).
++    ///
++    /// **Why is this bad?** Supply the reason for linting the code.
++    ///
++    /// **Known problems:** None. (Or describe where it could go wrong.)
++    ///
++    /// **Example:**
++    ///
++    /// ```rust,ignore
++    /// // Bad
++    /// Insert a short example of code that triggers the lint
++    ///
++    /// // Good
++    /// Insert a short example of improved code that doesn't trigger the lint
++    /// ```
++    pub FOO_FUNCTIONS,
++    pedantic,
++    "function named `foo`, which is not a descriptive name"
++}
++```
++
++Once your lint is merged, this documentation will show up in the [lint
++list][lint_list].
++
++[lint_list]: https://rust-lang.github.io/rust-clippy/master/index.html
++
++## Running rustfmt
++
++[Rustfmt] is a tool for formatting Rust code according to style guidelines.
++Your code has to be formatted by `rustfmt` before a PR can be merged.
++Clippy uses nightly `rustfmt` in the CI.
++
++It can be installed via `rustup`:
++
++```bash
++rustup component add rustfmt --toolchain=nightly
++```
++
++Use `cargo dev fmt` to format the whole codebase. Make sure that `rustfmt` is
++installed for the nightly toolchain.
++
++[Rustfmt]: https://github.com/rust-lang/rustfmt
++
++## Debugging
++
++If you want to debug parts of your lint implementation, you can use the [`dbg!`]
++macro anywhere in your code. Running the tests should then include the debug
++output in the `stdout` part.
++
++[`dbg!`]: https://doc.rust-lang.org/std/macro.dbg.html
++
++## PR Checklist
++
++Before submitting your PR make sure you followed all of the basic requirements:
++
++<!-- Sync this with `.github/PULL_REQUEST_TEMPLATE` -->
++
++- [ ] Followed [lint naming conventions][lint_naming]
++- [ ] Added passing UI tests (including committed `.stderr` file)
++- [ ] `cargo test` passes locally
++- [ ] Executed `cargo dev update_lints`
++- [ ] Added lint documentation
++- [ ] Run `cargo dev fmt`
++
++## Cheatsheet
++
++Here are some pointers to things you are likely going to need for every lint:
++
++* [Clippy utils][utils] - Various helper functions. Maybe the function you need
++  is already in here (`implements_trait`, `match_path`, `snippet`, etc)
++* [Clippy diagnostics][diagnostics]
++* [The `if_chain` macro][if_chain]
++* [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro]
++* [`Span`][span]
++* [`Applicability`][applicability]
++* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler concepts
++* [The nightly rustc docs][nightly_docs] which has been linked to throughout
++  this guide
++
++For `EarlyLintPass` lints:
++
++* [`EarlyLintPass`][early_lint_pass]
++* [`rustc_ast::ast`][ast]
++
++For `LateLintPass` lints:
++
++* [`LateLintPass`][late_lint_pass]
++* [`Ty::TyKind`][ty]
++
++While most of Clippy's lint utils are documented, most of rustc's internals lack
++documentation currently. This is unfortunate, but in most cases you can probably
++get away with copying things from existing similar lints. If you are stuck,
++don't hesitate to ask on [Discord] or in the issue/PR.
++
++[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/mod.rs
++[if_chain]: https://docs.rs/if_chain/*/if_chain/
++[from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
++[in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html
++[span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html
++[applicability]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html
++[rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/
++[nightly_docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/
++[ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html
++[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/index.html
++[Discord]: https://discord.gg/rust-lang
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..259696658eababfe7496dfe0907f2d15f72399a6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++# Backport Changes
++
++Sometimes it is necessary to backport changes to the beta release of Clippy.
++Backports in Clippy are rare and should be approved by the Clippy team. For
++example, a backport is done, if a crucial ICE was fixed or a lint is broken to a
++point, that it has to be disabled, before landing on stable.
++
++Backports are done to the `beta` release of Clippy. Backports to stable Clippy
++releases basically don't exist, since this would require a Rust point release,
++which is almost never justifiable for a Clippy fix.
++
++
++## Backport the changes
++
++Backports are done on the beta branch of the Clippy repository.
++
++```bash
++# Assuming the current directory corresponds to the Clippy repository
++$ git checkout beta
++$ git checkout -b backport
++$ git cherry-pick <SHA>  # `<SHA>` is the commit hash of the commit, that should be backported
++$ git push origin backport
++```
++
++After this, you can open a PR to the `beta` branch of the Clippy repository.
++
++
++## Update Clippy in the Rust Repository
++
++This step must be done, **after** the PR of the previous step was merged.
++
++After the backport landed in the Clippy repository, also the Clippy version on
++the Rust `beta` branch has to be updated.
++
++```bash
++# Assuming the current directory corresponds to the Rust repository
++$ git checkout beta
++$ git checkout -b clippy_backport
++$ pushd src/tools/clippy
++$ git fetch
++$ git checkout beta
++$ popd
++$ git add src/tools/clippy
++§ git commit -m "Update Clippy"
++$ git push origin clippy_backport
++```
++
++After this you can open a PR to the `beta` branch of the Rust repository. In
++this PR you should tag the Clippy team member, that agreed to the backport or
++the `@rust-lang/clippy` team. Make sure to add `[beta]` to the title of the PR.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0b80cce6d23eaa15df2644b130267e78c30d1d0c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,78 @@@
++# Changelog Update
++
++If you want to help with updating the [changelog][changelog], you're in the right place.
++
++## When to update
++
++Typos and other small fixes/additions are _always_ welcome.
++
++Special care needs to be taken when it comes to updating the changelog for a new
++Rust release. For that purpose, the changelog is ideally updated during the week
++before an upcoming stable release. You can find the release dates on the [Rust
++Forge][forge].
++
++Most of the time we only need to update the changelog for minor Rust releases. It's
++been very rare that Clippy changes were included in a patch release.
++
++## Changelog update walkthrough
++
++### 1. Finding the relevant Clippy commits
++
++Each Rust release ships with its own version of Clippy. The Clippy submodule can
++be found in the `tools` directory of the Rust repository.
++
++Depending on the current time and what exactly you want to update, the following
++bullet points might be helpful:
++
++* When writing the release notes for the **upcoming stable release** you need to check
++  out the Clippy commit of the current Rust `beta` branch. [Link][rust_beta_tools]
++* When writing the release notes for the **upcoming beta release**, you need to check
++  out the Clippy commit of the current Rust `master`. [Link][rust_master_tools]
++* When writing the (forgotten) release notes for a **past stable release**, you
++  need to select the Rust release tag from the dropdown and then check the
++  commit of the Clippy directory:
++
++  ![Explanation of how to find the commit hash](https://user-images.githubusercontent.com/2042399/62846160-1f8b0480-bcce-11e9-9da8-7964ca034e7a.png)
++
++
++### 2. Fetching the PRs between those commits
++
++Once you've got the correct commit range, run
++
++    util/fetch_prs_between.sh commit1 commit2 > changes.txt
++
++and open that file in your editor of choice.
++
++When updating the changelog it's also a good idea to make sure that `commit1` is
++already correct in the current changelog.
++
++### 3. Authoring the final changelog
++
++The above script should have dumped all the relevant PRs to the file you
++specified. It should have filtered out most of the irrelevant PRs
++already, but it's a good idea to do a manual cleanup pass where you look for
++more irrelevant PRs. If you're not sure about some PRs, just leave them in for
++the review and ask for feedback.
++
++With the PRs filtered, you can start to take each PR and move the
++`changelog: ` content to `CHANGELOG.md`. Adapt the wording as you see fit but
++try to keep it somewhat coherent.
++
++The order should roughly be:
++
++1. New lints
++2. Moves or deprecations of lints
++3. Changes that expand what code existing lints cover
++4. False positive fixes
++5. Suggestion fixes/improvements
++6. ICE fixes
++7. Documentation improvements
++8. Others
++
++Please also be sure to update the Beta/Unreleased sections at the top with the
++relevant commit ranges.
++
++[changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md
++[forge]: https://forge.rust-lang.org/
++[rust_master_tools]: https://github.com/rust-lang/rust/tree/master/src/tools
++[rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9d69fa8a7f69a3f17de0df63721b723fca6b4f8b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,111 @@@
++# Release a new Clippy Version
++
++_NOTE: This document is probably only relevant to you, if you're a member of the
++Clippy team._
++
++Clippy is released together with stable Rust releases. The dates for these
++releases can be found at the [Rust Forge]. This document explains the necessary
++steps to create a Clippy release.
++
++1. [Find the Clippy commit](#find-the-clippy-commit)
++2. [Tag the stable commit](#tag-the-stable-commit)
++3. [Update `CHANGELOG.md`](#update-changelogmd)
++4. [Remerge the `beta` branch](#remerge-the-beta-branch)
++5. [Update the `beta` branch](#update-the-beta-branch)
++
++_NOTE: This document is for stable Rust releases, not for point releases. For
++point releases, step 1. and 2. should be enough._
++
++[Rust Forge]: https://forge.rust-lang.org/
++
++
++## Find the Clippy commit
++
++The first step is to tag the Clippy commit, that is included in the stable Rust
++release. This commit can be found in the Rust repository.
++
++```bash
++# Assuming the current directory corresponds to the Rust repository
++$ git fetch upstream    # `upstream` is the `rust-lang/rust` remote
++$ git checkout 1.XX.0   # XX should be exchanged with the corresponding version
++$ git submodule update
++$ SHA=$(git submodule status src/tools/clippy | awk '{print $1}')
++```
++
++
++## Tag the stable commit
++
++After finding the Clippy commit, it can be tagged with the release number.
++
++```bash
++# Assuming the current directory corresponds to the Clippy repository
++$ git checkout $SHA
++$ git tag rust-1.XX.0               # XX should be exchanged with the corresponding version
++$ git push upstream master --tags   # `upstream` is the `rust-lang/rust-clippy` remote
++```
++
++After this, the release should be available on the Clippy [release page].
++
++[release page]: https://github.com/rust-lang/rust-clippy/releases
++
++
++## Update `CHANGELOG.md`
++
++For this see the document on [how to update the changelog].
++
++[how to update the changelog]: https://github.com/rust-lang/rust-clippy/blob/master/doc/changelog_update.md
++
++
++## Remerge the `beta` branch
++
++This step is only necessary, if since the last release something was backported
++to the beta Rust release. The remerge is then necessary, to make sure that the
++Clippy commit, that was used by the now stable Rust release, persists in the
++tree of the Clippy repository.
++
++To find out if this step is necessary run
++
++```bash
++# Assumes that the local master branch is up-to-date
++$ git fetch upstream
++$ git branch master --contains upstream/beta
++```
++
++If this command outputs `master`, this step is **not** necessary.
++
++```bash
++# Assuming `HEAD` is the current `master` branch of rust-lang/rust-clippy
++$ git checkout -b backport_remerge
++$ git merge beta
++$ git diff  # This diff has to be empty, otherwise something with the remerge failed
++$ git push origin backport_remerge  # This can be pushed to your fork
++```
++
++After this, open a PR to the master branch. In this PR, the commit hash of the
++`HEAD` of the `beta` branch must exists. In addition to that, no files should
++be changed by this PR.
++
++
++## Update the `beta` branch
++
++This step must be done **after** the PR of the previous step was merged.
++
++First, the Clippy commit of the `beta` branch of the Rust repository has to be
++determined.
++
++```bash
++# Assuming the current directory corresponds to the Rust repository
++$ git checkout beta
++$ git submodule update
++$ BETA_SHA=$(git submodule status src/tools/clippy | awk '{print $1}')
++```
++
++After finding the Clippy commit, the `beta` branch in the Clippy repository can
++be updated.
++
++```bash
++# Assuming the current directory corresponds to the Clippy repository
++$ git checkout beta
++$ git rebase $BETA_SHA
++$ git push upstream beta
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fcd7abbf3f1693cc100c0e1f4a4e4326d26a40f3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,69 @@@
++This repository was previously licensed under MPL-2.0, however in #3093
++([archive](http://web.archive.org/web/20181005185227/https://github.com/rust-lang-nursery/rust-clippy/issues/3093),
++[screenshot](https://user-images.githubusercontent.com/1617736/46573505-5b856880-c94b-11e8-9a14-981c889b4981.png)) we
++relicensed it to the Rust license (dual licensed as Apache v2 / MIT)
++
++At the time, the contributors were those listed in contributors.txt.
++
++We opened a bunch of issues asking for an explicit relicensing approval. Screenshots of all these issues at the time of
++relicensing are archived on GitHub. We also have saved Wayback Machine copies of these:
++
++- #3094
++  ([archive](http://web.archive.org/web/20181005191247/https://github.com/rust-lang-nursery/rust-clippy/issues/3094),
++  [screenshot](https://user-images.githubusercontent.com/1617736/46573506-5b856880-c94b-11e8-8a44-51cb40bc16ee.png))
++- #3095
++  ([archive](http://web.archive.org/web/20181005184416/https://github.com/rust-lang-nursery/rust-clippy/issues/3095),
++  [screenshot](https://user-images.githubusercontent.com/1617736/46573507-5c1dff00-c94b-11e8-912a-4bd6b5f838f5.png))
++- #3096
++  ([archive](http://web.archive.org/web/20181005184802/https://github.com/rust-lang-nursery/rust-clippy/issues/3096),
++  [screenshot](https://user-images.githubusercontent.com/1617736/46573508-5c1dff00-c94b-11e8-9425-2464f7260ff0.png))
++- #3097
++  ([archive](http://web.archive.org/web/20181005184821/https://github.com/rust-lang-nursery/rust-clippy/issues/3097),
++  [screenshot](https://user-images.githubusercontent.com/1617736/46573509-5c1dff00-c94b-11e8-8ba2-53f687984fe7.png))
++- #3098
++  ([archive](http://web.archive.org/web/20181005184900/https://github.com/rust-lang-nursery/rust-clippy/issues/3098),
++  [screenshot](https://user-images.githubusercontent.com/1617736/46573510-5c1dff00-c94b-11e8-8f64-371698401c60.png))
++- #3099
++  ([archive](http://web.archive.org/web/20181005184901/https://github.com/rust-lang-nursery/rust-clippy/issues/3099),
++  [screenshot](https://user-images.githubusercontent.com/1617736/46573511-5c1dff00-c94b-11e8-8e20-7d0eeb392b95.png))
++- #3100
++  ([archive](http://web.archive.org/web/20181005184901/https://github.com/rust-lang-nursery/rust-clippy/issues/3100),
++  [screenshot](https://user-images.githubusercontent.com/1617736/46573512-5c1dff00-c94b-11e8-8a13-7d758ed3563d.png))
++- #3230
++  ([archive](http://web.archive.org/web/20181005184903/https://github.com/rust-lang-nursery/rust-clippy/issues/3230),
++  [screenshot](https://user-images.githubusercontent.com/1617736/46573513-5cb69580-c94b-11e8-86b1-14ce82741e5c.png))
++
++The usernames of commenters on these issues can be found in relicense_comments.txt
++
++There are a couple people in relicense_comments.txt who are not found in contributors.txt:
++
++- @EpocSquadron has [made minor text contributions to the
++  README](https://github.com/rust-lang/rust-clippy/commits?author=EpocSquadron) which have since been overwritten, and
++  doesn't count
++- @JayKickliter [agreed to the relicense on their pull
++  request](https://github.com/rust-lang/rust-clippy/pull/3195#issuecomment-423781016)
++  ([archive](https://web.archive.org/web/20181005190730/https://github.com/rust-lang/rust-clippy/pull/3195),
++  [screenshot](https://user-images.githubusercontent.com/1617736/46573514-5cb69580-c94b-11e8-8ffb-05a5bd02e2cc.png)
++
++- @sanmai-NL's [contribution](https://github.com/rust-lang/rust-clippy/commits?author=sanmai-NL) is a minor one-word
++  addition which doesn't count for copyright assignment
++- @zmt00's [contributions](https://github.com/rust-lang/rust-clippy/commits?author=zmt00) are minor typo fixes and don't
++  count
++- @VKlayd has [nonminor contributions](https://github.com/rust-lang/rust-clippy/commits?author=VKlayd) which we rewrote
++  (see below)
++- @wartman4404 has [nonminor contributions](https://github.com/rust-lang/rust-clippy/commits?author=wartman4404) which
++  we rewrote (see below)
++
++
++Two of these contributors had nonminor contributions (#2184, #427) requiring a rewrite, carried out in #3251
++([archive](http://web.archive.org/web/20181005192411/https://github.com/rust-lang-nursery/rust-clippy/pull/3251),
++[screenshot](https://user-images.githubusercontent.com/1617736/46573515-5cb69580-c94b-11e8-86e5-b456452121b2.png))
++
++First, I (Manishearth) removed the lints they had added. I then documented at a high level what the lints did in #3251,
++asking for co-maintainers who had not seen the code for the lints to rewrite them. #2814 was rewritten by @phansch, and
++#427 was rewritten by @oli-obk, who did not recall having previously seen the code they were rewriting.
++
++------
++
++Since this document was written, @JayKickliter and @sanmai-ML added their consent in #3230
++([archive](http://web.archive.org/web/20181006171926/https://github.com/rust-lang-nursery/rust-clippy/issues/3230))
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e81ebf21484982358f88c848220735a794311840
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,232 @@@
++0ndorio
++0xbsec
++17cupsofcoffee
++Aaron1011
++Aaronepower
++aaudiber
++afck
++alexcrichton
++AlexEne
++alexeyzab
++alexheretic
++alexreg
++alusch
++andersk
++aochagavia
++apasel422
++Arnavion
++AtheMathmo
++auscompgeek
++AVerm
++badboy
++Baelyk
++BenoitZugmeyer
++bestouff
++birkenfeld
++bjgill
++bkchr
++Bobo1239
++bood
++bootandy
++b-r-u
++budziq
++CAD97
++Caemor
++camsteffen
++carols10cents
++CBenoit
++cesarb
++cgm616
++chrisduerr
++chrisvittal
++chyvonomys
++clarcharr
++clippered
++commandline
++cramertj
++csmoe
++ctjhoa
++cuviper
++CYBAI
++darArch
++DarkEld3r
++dashed
++daubaris
++d-dorazio
++debris
++dereckson
++detrumi
++devonhollowood
++dtolnay
++durka
++dwijnand
++eddyb
++elliottneilclark
++elpiel
++ensch
++EpicatSupercell
++EpocSquadron
++erickt
++estk
++etaoins
++F001
++fanzier
++FauxFaux
++fhartwig
++flip1995
++Fraser999
++Frederick888
++frewsxcv
++gbip
++gendx
++gibfahn
++gnieto
++gnzlbg
++goodmanjonathan
++guido4000
++GuillaumeGomez
++Hanaasagi
++hdhoang
++HMPerson1
++hobofan
++iKevinY
++illicitonion
++imp
++inrustwetrust
++ishitatsuyuki
++Jascha-N
++jayhardee9
++JayKickliter
++JDemler
++jedisct1
++jmquigs
++joelgallant
++joeratt
++josephDunne
++JoshMcguigan
++joshtriplett
++jugglerchris
++karyon
++Keats
++kennytm
++Kha
++killercup
++kimsnj
++KitFreddura
++koivunej
++kraai
++kvikas
++LaurentMazare
++letheed
++llogiq
++lo48576
++lpesk
++lucab
++luisbg
++lukasstevens
++Machtan
++MaloJaffre
++Manishearth
++marcusklaas
++mark-i-m
++martiansideofthemoon
++martinlindhe
++mathstuf
++mati865
++matthiaskrgr
++mattyhall
++mbrubeck
++mcarton
++memoryleak47
++messense
++michaelrutherford
++mikerite
++mipli
++mockersf
++montrivo
++mrecachinas
++Mrmaxmeier
++mrmonday
++ms2300
++Ms2ger
++musoke
++nathan
++Nemo157
++NiekGr
++niklasf
++nrc
++nweston
++o01eg
++ogham
++oli-obk
++ordovicia
++pengowen123
++pgerber
++phansch
++philipturnbull
++pickfire
++pietro
++PixelPirate
++pizzaiter
++PSeitz
++Pyriphlegethon
++pythonesque
++quininer
++Rantanen
++rcoh
++reiner-dolp
++reujab
++Robzz
++samueltardieu
++sanmai-NL
++sanxiyn
++scott-linder
++scottmcm
++scurest
++senden9
++shahn
++shepmaster
++shnewto
++shssoichiro
++siiptuo
++sinkuu
++skade
++sourcefrog
++sourcejedi
++steveklabnik
++sunfishcode
++sunjay
++swgillespie
++Techcable
++terry90
++theemathas
++thekidxp
++theotherphil
++TimNN
++TomasKralCZ
++tomprince
++topecongiro
++tspiteri
++Twisol
++U007D
++uHOOCCOOHu
++untitaker
++upsuper
++utaal
++utam0k
++vi
++VKlayd
++Vlad-Shcherbina
++vorner
++wafflespeanut
++wartman4404
++waywardmonkeys
++yaahallo
++yangby-cryptape
++yati-sagade
++ykrivopalov
++ysimonson
++zayenz
++zmanian
++zmbush
++zmt00
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..52c25eb201fb30d8b4aafe731d4691e169328cbd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,227 @@@
++0ndorio
++0xbsec
++17cupsofcoffee
++Aaron1011
++Aaronepower
++aaudiber
++afck
++alexcrichton
++AlexEne
++alexeyzab
++alexheretic
++alexreg
++alusch
++andersk
++aochagavia
++apasel422
++Arnavion
++AtheMathmo
++auscompgeek
++AVerm
++badboy
++Baelyk
++BenoitZugmeyer
++bestouff
++birkenfeld
++bjgill
++bkchr
++Bobo1239
++bood
++bootandy
++b-r-u
++budziq
++CAD97
++Caemor
++camsteffen
++carols10cents
++CBenoit
++cesarb
++cgm616
++chrisduerr
++chrisvittal
++chyvonomys
++clarcharr
++clippered
++commandline
++cramertj
++csmoe
++ctjhoa
++cuviper
++CYBAI
++darArch
++DarkEld3r
++dashed
++daubaris
++d-dorazio
++debris
++dereckson
++detrumi
++devonhollowood
++dtolnay
++durka
++dwijnand
++eddyb
++elliottneilclark
++elpiel
++ensch
++EpicatSupercell
++erickt
++estk
++etaoins
++F001
++fanzier
++FauxFaux
++fhartwig
++flip1995
++Fraser999
++Frederick888
++frewsxcv
++gbip
++gendx
++gibfahn
++gnieto
++gnzlbg
++goodmanjonathan
++guido4000
++GuillaumeGomez
++Hanaasagi
++hdhoang
++HMPerson1
++hobofan
++iKevinY
++illicitonion
++imp
++inrustwetrust
++ishitatsuyuki
++Jascha-N
++jayhardee9
++JDemler
++jedisct1
++jmquigs
++joelgallant
++joeratt
++josephDunne
++JoshMcguigan
++joshtriplett
++jugglerchris
++karyon
++Keats
++kennytm
++Kha
++killercup
++kimsnj
++KitFreddura
++koivunej
++kraai
++kvikas
++LaurentMazare
++letheed
++llogiq
++lo48576
++lpesk
++lucab
++luisbg
++lukasstevens
++Machtan
++MaloJaffre
++Manishearth
++marcusklaas
++mark-i-m
++martiansideofthemoon
++martinlindhe
++mathstuf
++mati865
++matthiaskrgr
++mattyhall
++mbrubeck
++mcarton
++memoryleak47
++messense
++michaelrutherford
++mikerite
++mipli
++mockersf
++montrivo
++mrecachinas
++Mrmaxmeier
++mrmonday
++ms2300
++Ms2ger
++musoke
++nathan
++Nemo157
++NiekGr
++niklasf
++nrc
++nweston
++o01eg
++ogham
++oli-obk
++ordovicia
++pengowen123
++pgerber
++phansch
++philipturnbull
++pickfire
++pietro
++PixelPirate
++pizzaiter
++PSeitz
++Pyriphlegethon
++pythonesque
++quininer
++Rantanen
++rcoh
++reiner-dolp
++reujab
++Robzz
++samueltardieu
++sanxiyn
++scott-linder
++scottmcm
++scurest
++senden9
++shahn
++shepmaster
++shnewto
++shssoichiro
++siiptuo
++sinkuu
++skade
++sourcefrog
++sourcejedi
++steveklabnik
++sunfishcode
++sunjay
++swgillespie
++Techcable
++terry90
++theemathas
++thekidxp
++theotherphil
++TimNN
++TomasKralCZ
++tommilligan
++tomprince
++topecongiro
++tspiteri
++Twisol
++U007D
++uHOOCCOOHu
++untitaker
++upsuper
++utaal
++utam0k
++vi
++Vlad-Shcherbina
++vorner
++wafflespeanut
++waywardmonkeys
++yaahallo
++yangby-cryptape
++yati-sagade
++ykrivopalov
++ysimonson
++zayenz
++zmanian
++zmbush
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..75ab17588a7f4415fdce9be4a565d3745a7b9491
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++[package]
++name = "clippy-mini-macro-test"
++version = "0.2.0"
++authors = [
++      "Manish Goregaokar <manishsmail@gmail.com>",
++      "Andre Bogus <bogusandre@gmail.com>",
++      "Georg Brandl <georg@python.org>",
++      "Martin Carton <cartonmartin@gmail.com>",
++      "Oliver Schneider <clippy-iethah7aipeen8neex1a@oli-obk.de>"
++]
++license = "MIT OR Apache-2.0"
++description = "A macro to test clippy's procedural macro checks"
++repository = "https://github.com/rust-lang/rust-clippy"
++edition = "2018"
++
++[lib]
++name = "clippy_mini_macro_test"
++proc-macro = true
++
++[dependencies]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..92b6f7015557701aaee4c7fb07aa4fb2b26c5d02
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++#![feature(proc_macro_quote, proc_macro_hygiene)]
++#![deny(rust_2018_idioms)]
++// FIXME: Remove this attribute once the weird failure is gone.
++#![allow(unused_extern_crates)]
++extern crate proc_macro;
++
++use proc_macro::{quote, TokenStream};
++
++#[proc_macro_derive(ClippyMiniMacroTest)]
++pub fn mini_macro(_: TokenStream) -> TokenStream {
++    quote!(
++        #[allow(unused)]
++        fn needless_take_by_value(s: String) {
++            println!("{}", s.len());
++        }
++        #[allow(unused)]
++        fn needless_loop(items: &[u8]) {
++            for i in 0..items.len() {
++                println!("{}", items[i]);
++            }
++        }
++        fn line_wrapper() {
++            println!("{}", line!());
++        }
++    )
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bf867e0ae5b6c08df1118a2ece970677bc479f1b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++nightly
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6f0fc5bee8f091cae4557d9512e9dbd2bea1e932
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++[package]
++name = "rustc_tools_util"
++version = "0.2.0"
++authors = ["Matthias Krüger <matthias.krueger@famsik.de>"]
++description = "small helper to generate version information for git packages"
++repository = "https://github.com/rust-lang/rust-clippy"
++readme = "README.md"
++license = "MIT OR Apache-2.0"
++keywords = ["rustc", "tool", "git", "version", "hash"]
++categories = ["development-tools"]
++edition = "2018"
++
++[dependencies]
++
++[features]
++deny-warnings = []
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6027538dc4ab22a74ee3a0299a2849667b450dcf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++# rustc_tools_util
++
++A small tool to help you generate version information
++for packages installed from a git repo
++
++## Usage
++
++Add a `build.rs` file to your repo and list it in `Cargo.toml`
++````
++build = "build.rs"
++````
++
++List rustc_tools_util as regular AND build dependency.
++````
++[dependencies]
++rustc_tools_util = "0.1"
++
++[build-dependencies]
++rustc_tools_util = "0.1"
++````
++
++In `build.rs`, generate the data in your `main()`
++````rust
++fn main() {
++    println!(
++        "cargo:rustc-env=GIT_HASH={}",
++        rustc_tools_util::get_commit_hash().unwrap_or_default()
++    );
++    println!(
++        "cargo:rustc-env=COMMIT_DATE={}",
++        rustc_tools_util::get_commit_date().unwrap_or_default()
++    );
++    println!(
++        "cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}",
++        rustc_tools_util::get_channel().unwrap_or_default()
++    );
++}
++
++````
++
++Use the version information in your main.rs
++````rust
++use rustc_tools_util::*;
++
++fn show_version() {
++    let version_info = rustc_tools_util::get_version_info!();
++    println!("{}", version_info);
++}
++````
++This gives the following output in clippy:
++`clippy 0.0.212 (a416c5e 2018-12-14)`
++
++
++## License
++
++Copyright 2014-2020 The Rust Project Developers
++
++Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
++http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
++<LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
++option. All files in the project carrying such notice may not be
++copied, modified, or distributed except according to those terms.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ff2a7de572571d850872fb2dec4e081bc9224404
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,162 @@@
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++
++use std::env;
++
++#[macro_export]
++macro_rules! get_version_info {
++    () => {{
++        let major = env!("CARGO_PKG_VERSION_MAJOR").parse::<u8>().unwrap();
++        let minor = env!("CARGO_PKG_VERSION_MINOR").parse::<u8>().unwrap();
++        let patch = env!("CARGO_PKG_VERSION_PATCH").parse::<u16>().unwrap();
++        let crate_name = String::from(env!("CARGO_PKG_NAME"));
++
++        let host_compiler = option_env!("RUSTC_RELEASE_CHANNEL").map(str::to_string);
++        let commit_hash = option_env!("GIT_HASH").map(str::to_string);
++        let commit_date = option_env!("COMMIT_DATE").map(str::to_string);
++
++        VersionInfo {
++            major,
++            minor,
++            patch,
++            host_compiler,
++            commit_hash,
++            commit_date,
++            crate_name,
++        }
++    }};
++}
++
++// some code taken and adapted from RLS and cargo
++pub struct VersionInfo {
++    pub major: u8,
++    pub minor: u8,
++    pub patch: u16,
++    pub host_compiler: Option<String>,
++    pub commit_hash: Option<String>,
++    pub commit_date: Option<String>,
++    pub crate_name: String,
++}
++
++impl std::fmt::Display for VersionInfo {
++    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
++        let hash = self.commit_hash.clone().unwrap_or_default();
++        let hash_trimmed = hash.trim();
++
++        let date = self.commit_date.clone().unwrap_or_default();
++        let date_trimmed = date.trim();
++
++        if (hash_trimmed.len() + date_trimmed.len()) > 0 {
++            write!(
++                f,
++                "{} {}.{}.{} ({} {})",
++                self.crate_name, self.major, self.minor, self.patch, hash_trimmed, date_trimmed,
++            )?;
++        } else {
++            write!(f, "{} {}.{}.{}", self.crate_name, self.major, self.minor, self.patch)?;
++        }
++
++        Ok(())
++    }
++}
++
++impl std::fmt::Debug for VersionInfo {
++    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
++        write!(
++            f,
++            "VersionInfo {{ crate_name: \"{}\", major: {}, minor: {}, patch: {}",
++            self.crate_name, self.major, self.minor, self.patch,
++        )?;
++        if self.commit_hash.is_some() {
++            write!(
++                f,
++                ", commit_hash: \"{}\", commit_date: \"{}\" }}",
++                self.commit_hash.clone().unwrap_or_default().trim(),
++                self.commit_date.clone().unwrap_or_default().trim()
++            )?;
++        } else {
++            write!(f, " }}")?;
++        }
++
++        Ok(())
++    }
++}
++
++#[must_use]
++pub fn get_commit_hash() -> Option<String> {
++    std::process::Command::new("git")
++        .args(&["rev-parse", "--short", "HEAD"])
++        .output()
++        .ok()
++        .and_then(|r| String::from_utf8(r.stdout).ok())
++}
++
++#[must_use]
++pub fn get_commit_date() -> Option<String> {
++    std::process::Command::new("git")
++        .args(&["log", "-1", "--date=short", "--pretty=format:%cd"])
++        .output()
++        .ok()
++        .and_then(|r| String::from_utf8(r.stdout).ok())
++}
++
++#[must_use]
++pub fn get_channel() -> Option<String> {
++    match env::var("CFG_RELEASE_CHANNEL") {
++        Ok(channel) => Some(channel),
++        Err(_) => {
++            // if that failed, try to ask rustc -V, do some parsing and find out
++            match std::process::Command::new("rustc")
++                .arg("-V")
++                .output()
++                .ok()
++                .and_then(|r| String::from_utf8(r.stdout).ok())
++            {
++                Some(rustc_output) => {
++                    if rustc_output.contains("beta") {
++                        Some(String::from("beta"))
++                    } else if rustc_output.contains("stable") {
++                        Some(String::from("stable"))
++                    } else {
++                        // default to nightly if we fail to parse
++                        Some(String::from("nightly"))
++                    }
++                },
++                // default to nightly
++                None => Some(String::from("nightly")),
++            }
++        },
++    }
++}
++
++#[cfg(test)]
++mod test {
++    use super::*;
++
++    #[test]
++    fn test_struct_local() {
++        let vi = get_version_info!();
++        assert_eq!(vi.major, 0);
++        assert_eq!(vi.minor, 2);
++        assert_eq!(vi.patch, 0);
++        assert_eq!(vi.crate_name, "rustc_tools_util");
++        // hard to make positive tests for these since they will always change
++        assert!(vi.commit_hash.is_none());
++        assert!(vi.commit_date.is_none());
++    }
++
++    #[test]
++    fn test_display_local() {
++        let vi = get_version_info!();
++        assert_eq!(vi.to_string(), "rustc_tools_util 0.2.0");
++    }
++
++    #[test]
++    fn test_debug_local() {
++        let vi = get_version_info!();
++        let s = format!("{:?}", vi);
++        assert_eq!(
++            s,
++            "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 2, patch: 0 }"
++        );
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f1241e74b0a3d40e4bfb64912b27fec9711efb2f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++max_width = 120
++comment_width = 100
++match_block_trailing_comma = true
++wrap_comments = true
++edition = "2018"
++error_on_line_overflow = true
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6038ed697f91ea7f2ccbec3672c6ac63bb2a695f
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++#!/usr/bin/env bash
++# Set up the appropriate rustc toolchain
++
++set -e
++
++cd "$(dirname "$0")"
++
++RTIM_PATH=$(command -v rustup-toolchain-install-master) || INSTALLED=false
++CARGO_HOME=${CARGO_HOME:-$HOME/.cargo}
++
++# Check if RTIM is not installed or installed in other locations not in ~/.cargo/bin
++if [[ "$INSTALLED" == false || "$RTIM_PATH" == $CARGO_HOME/bin/rustup-toolchain-install-master ]]; then
++    cargo +nightly install rustup-toolchain-install-master
++else
++    VERSION=$(rustup-toolchain-install-master -V | grep -o "[0-9.]*")
++    REMOTE=$(cargo +nightly search rustup-toolchain-install-master | grep -o "[0-9.]*")
++    echo "info: skipping updating rustup-toolchain-install-master at $RTIM_PATH"
++    echo "      current version : $VERSION"
++    echo "      remote version  : $REMOTE"
++fi
++
++RUST_COMMIT=$(git ls-remote https://github.com/rust-lang/rust master | awk '{print $1}')
++
++if rustc +master -Vv 2>/dev/null | grep -q "$RUST_COMMIT"; then
++    echo "info: master toolchain is up-to-date"
++    exit 0
++fi
++
++if [[ -n "$HOST_TOOLCHAIN" ]]; then
++    TOOLCHAIN=('--host' "$HOST_TOOLCHAIN")
++else
++    TOOLCHAIN=()
++fi
++
++rustup-toolchain-install-master -f -n master "${TOOLCHAIN[@]}" -c rustc-dev -- "$RUST_COMMIT"
++rustup override set master
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2c699998ea90e237cdb708de9eb62507f3d0c60c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,417 @@@
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++#![feature(rustc_private)]
++#![feature(str_strip)]
++
++// FIXME: switch to something more ergonomic here, once available.
++// (Currently there is no way to opt into sysroot crates without `extern crate`.)
++#[allow(unused_extern_crates)]
++extern crate rustc_driver;
++#[allow(unused_extern_crates)]
++extern crate rustc_errors;
++#[allow(unused_extern_crates)]
++extern crate rustc_interface;
++#[allow(unused_extern_crates)]
++extern crate rustc_middle;
++
++use rustc_interface::interface;
++use rustc_middle::ty::TyCtxt;
++use rustc_tools_util::VersionInfo;
++
++use lazy_static::lazy_static;
++use std::borrow::Cow;
++use std::env;
++use std::ops::Deref;
++use std::panic;
++use std::path::{Path, PathBuf};
++use std::process::{exit, Command};
++
++mod lintlist;
++
++/// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If
++/// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`.
++fn arg_value<'a, T: Deref<Target = str>>(
++    args: &'a [T],
++    find_arg: &str,
++    pred: impl Fn(&str) -> bool,
++) -> Option<&'a str> {
++    let mut args = args.iter().map(Deref::deref);
++    while let Some(arg) = args.next() {
++        let mut arg = arg.splitn(2, '=');
++        if arg.next() != Some(find_arg) {
++            continue;
++        }
++
++        match arg.next().or_else(|| args.next()) {
++            Some(v) if pred(v) => return Some(v),
++            _ => {},
++        }
++    }
++    None
++}
++
++#[test]
++fn test_arg_value() {
++    let args = &["--bar=bar", "--foobar", "123", "--foo"];
++
++    assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None);
++    assert_eq!(arg_value(args, "--bar", |_| false), None);
++    assert_eq!(arg_value(args, "--bar", |_| true), Some("bar"));
++    assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar"));
++    assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
++    assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
++    assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
++    assert_eq!(arg_value(args, "--foo", |_| true), None);
++}
++
++struct DefaultCallbacks;
++impl rustc_driver::Callbacks for DefaultCallbacks {}
++
++struct ClippyCallbacks;
++impl rustc_driver::Callbacks for ClippyCallbacks {
++    fn config(&mut self, config: &mut interface::Config) {
++        let previous = config.register_lints.take();
++        config.register_lints = Some(Box::new(move |sess, mut lint_store| {
++            // technically we're ~guaranteed that this is none but might as well call anything that
++            // is there already. Certainly it can't hurt.
++            if let Some(previous) = &previous {
++                (previous)(sess, lint_store);
++            }
++
++            let conf = clippy_lints::read_conf(&[], &sess);
++            clippy_lints::register_plugins(&mut lint_store, &sess, &conf);
++            clippy_lints::register_pre_expansion_lints(&mut lint_store, &conf);
++            clippy_lints::register_renamed(&mut lint_store);
++        }));
++
++        // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be
++        // run on the unoptimized MIR. On the other hand this results in some false negatives. If
++        // MIR passes can be enabled / disabled separately, we should figure out, what passes to
++        // use for Clippy.
++        config.opts.debugging_opts.mir_opt_level = 0;
++    }
++}
++
++#[allow(clippy::find_map, clippy::filter_map)]
++fn describe_lints() {
++    use lintlist::{Level, Lint, ALL_LINTS, LINT_LEVELS};
++    use std::collections::HashSet;
++
++    println!(
++        "
++Available lint options:
++    -W <foo>           Warn about <foo>
++    -A <foo>           Allow <foo>
++    -D <foo>           Deny <foo>
++    -F <foo>           Forbid <foo> (deny <foo> and all attempts to override)
++
++"
++    );
++
++    let lint_level = |lint: &Lint| {
++        LINT_LEVELS
++            .iter()
++            .find(|level_mapping| level_mapping.0 == lint.group)
++            .map(|(_, level)| match level {
++                Level::Allow => "allow",
++                Level::Warn => "warn",
++                Level::Deny => "deny",
++            })
++            .unwrap()
++    };
++
++    let mut lints: Vec<_> = ALL_LINTS.iter().collect();
++    // The sort doesn't case-fold but it's doubtful we care.
++    lints.sort_by_cached_key(|x: &&Lint| (lint_level(x), x.name));
++
++    let max_lint_name_len = lints
++        .iter()
++        .map(|lint| lint.name.len())
++        .map(|len| len + "clippy::".len())
++        .max()
++        .unwrap_or(0);
++
++    let padded = |x: &str| {
++        let mut s = " ".repeat(max_lint_name_len - x.chars().count());
++        s.push_str(x);
++        s
++    };
++
++    let scoped = |x: &str| format!("clippy::{}", x);
++
++    let lint_groups: HashSet<_> = lints.iter().map(|lint| lint.group).collect();
++
++    println!("Lint checks provided by clippy:\n");
++    println!("    {}  {:7.7}  meaning", padded("name"), "default");
++    println!("    {}  {:7.7}  -------", padded("----"), "-------");
++
++    let print_lints = |lints: &[&Lint]| {
++        for lint in lints {
++            let name = lint.name.replace("_", "-");
++            println!(
++                "    {}  {:7.7}  {}",
++                padded(&scoped(&name)),
++                lint_level(lint),
++                lint.desc
++            );
++        }
++        println!("\n");
++    };
++
++    print_lints(&lints);
++
++    let max_group_name_len = std::cmp::max(
++        "clippy::all".len(),
++        lint_groups
++            .iter()
++            .map(|group| group.len())
++            .map(|len| len + "clippy::".len())
++            .max()
++            .unwrap_or(0),
++    );
++
++    let padded_group = |x: &str| {
++        let mut s = " ".repeat(max_group_name_len - x.chars().count());
++        s.push_str(x);
++        s
++    };
++
++    println!("Lint groups provided by clippy:\n");
++    println!("    {}  sub-lints", padded_group("name"));
++    println!("    {}  ---------", padded_group("----"));
++    println!("    {}  the set of all clippy lints", padded_group("clippy::all"));
++
++    let print_lint_groups = || {
++        for group in lint_groups {
++            let name = group.to_lowercase().replace("_", "-");
++            let desc = lints
++                .iter()
++                .filter(|&lint| lint.group == group)
++                .map(|lint| lint.name)
++                .map(|name| name.replace("_", "-"))
++                .collect::<Vec<String>>()
++                .join(", ");
++            println!("    {}  {}", padded_group(&scoped(&name)), desc);
++        }
++        println!("\n");
++    };
++
++    print_lint_groups();
++}
++
++fn display_help() {
++    println!(
++        "\
++Checks a package to catch common mistakes and improve your Rust code.
++
++Usage:
++    cargo clippy [options] [--] [<opts>...]
++
++Common options:
++    -h, --help               Print this message
++    -V, --version            Print version info and exit
++
++Other options are the same as `cargo check`.
++
++To allow or deny a lint from the command line you can use `cargo clippy --`
++with:
++
++    -W --warn OPT       Set lint warnings
++    -A --allow OPT      Set lint allowed
++    -D --deny OPT       Set lint denied
++    -F --forbid OPT     Set lint forbidden
++
++You can use tool lints to allow or deny lints from your code, eg.:
++
++    #[allow(clippy::needless_lifetimes)]
++"
++    );
++}
++
++const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new";
++
++lazy_static! {
++    static ref ICE_HOOK: Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static> = {
++        let hook = panic::take_hook();
++        panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL)));
++        hook
++    };
++}
++
++fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
++    // Invoke our ICE handler, which prints the actual panic message and optionally a backtrace
++    (*ICE_HOOK)(info);
++
++    // Separate the output with an empty line
++    eprintln!();
++
++    let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
++        rustc_errors::ColorConfig::Auto,
++        None,
++        false,
++        false,
++        None,
++        false,
++    ));
++    let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
++
++    // a .span_bug or .bug call has already printed what
++    // it wants to print.
++    if !info.payload().is::<rustc_errors::ExplicitBug>() {
++        let d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
++        handler.emit_diagnostic(&d);
++    }
++
++    let version_info = rustc_tools_util::get_version_info!();
++
++    let xs: Vec<Cow<'static, str>> = vec![
++        "the compiler unexpectedly panicked. this is a bug.".into(),
++        format!("we would appreciate a bug report: {}", bug_report_url).into(),
++        format!("Clippy version: {}", version_info).into(),
++    ];
++
++    for note in &xs {
++        handler.note_without_error(&note);
++    }
++
++    // If backtraces are enabled, also print the query stack
++    let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");
++
++    if backtrace {
++        TyCtxt::try_print_query_stack(&handler);
++    }
++}
++
++fn toolchain_path(home: Option<String>, toolchain: Option<String>) -> Option<PathBuf> {
++    home.and_then(|home| {
++        toolchain.map(|toolchain| {
++            let mut path = PathBuf::from(home);
++            path.push("toolchains");
++            path.push(toolchain);
++            path
++        })
++    })
++}
++
++pub fn main() {
++    rustc_driver::init_rustc_env_logger();
++    lazy_static::initialize(&ICE_HOOK);
++    exit(
++        rustc_driver::catch_fatal_errors(move || {
++            let mut orig_args: Vec<String> = env::args().collect();
++
++            if orig_args.iter().any(|a| a == "--version" || a == "-V") {
++                let version_info = rustc_tools_util::get_version_info!();
++                println!("{}", version_info);
++                exit(0);
++            }
++
++            // Get the sysroot, looking from most specific to this invocation to the least:
++            // - command line
++            // - runtime environment
++            //    - SYSROOT
++            //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
++            // - sysroot from rustc in the path
++            // - compile-time environment
++            //    - SYSROOT
++            //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
++            let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true);
++            let have_sys_root_arg = sys_root_arg.is_some();
++            let sys_root = sys_root_arg
++                .map(PathBuf::from)
++                .or_else(|| std::env::var("SYSROOT").ok().map(PathBuf::from))
++                .or_else(|| {
++                    let home = std::env::var("RUSTUP_HOME")
++                        .or_else(|_| std::env::var("MULTIRUST_HOME"))
++                        .ok();
++                    let toolchain = std::env::var("RUSTUP_TOOLCHAIN")
++                        .or_else(|_| std::env::var("MULTIRUST_TOOLCHAIN"))
++                        .ok();
++                    toolchain_path(home, toolchain)
++                })
++                .or_else(|| {
++                    Command::new("rustc")
++                        .arg("--print")
++                        .arg("sysroot")
++                        .output()
++                        .ok()
++                        .and_then(|out| String::from_utf8(out.stdout).ok())
++                        .map(|s| PathBuf::from(s.trim()))
++                })
++                .or_else(|| option_env!("SYSROOT").map(PathBuf::from))
++                .or_else(|| {
++                    let home = option_env!("RUSTUP_HOME")
++                        .or(option_env!("MULTIRUST_HOME"))
++                        .map(ToString::to_string);
++                    let toolchain = option_env!("RUSTUP_TOOLCHAIN")
++                        .or(option_env!("MULTIRUST_TOOLCHAIN"))
++                        .map(ToString::to_string);
++                    toolchain_path(home, toolchain)
++                })
++                .map(|pb| pb.to_string_lossy().to_string())
++                .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");
++
++            // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
++            // We're invoking the compiler programmatically, so we ignore this/
++            let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref());
++
++            if wrapper_mode {
++                // we still want to be able to invoke it normally though
++                orig_args.remove(1);
++            }
++
++            if !wrapper_mode && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1) {
++                display_help();
++                exit(0);
++            }
++
++            let should_describe_lints = || {
++                let args: Vec<_> = env::args().collect();
++                args.windows(2).any(|args| {
++                    args[1] == "help"
++                        && match args[0].as_str() {
++                            "-W" | "-A" | "-D" | "-F" => true,
++                            _ => false,
++                        }
++                })
++            };
++
++            if !wrapper_mode && should_describe_lints() {
++                describe_lints();
++                exit(0);
++            }
++
++            // this conditional check for the --sysroot flag is there so users can call
++            // `clippy_driver` directly
++            // without having to pass --sysroot or anything
++            let mut args: Vec<String> = orig_args.clone();
++            if !have_sys_root_arg {
++                args.extend(vec!["--sysroot".into(), sys_root]);
++            };
++
++            // this check ensures that dependencies are built but not linted and the final
++            // crate is linted but not built
++            let clippy_enabled = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true")
++                || arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_none();
++
++            if clippy_enabled {
++                args.extend(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]);
++                if let Ok(extra_args) = env::var("CLIPPY_ARGS") {
++                    args.extend(extra_args.split("__CLIPPY_HACKERY__").filter_map(|s| {
++                        if s.is_empty() {
++                            None
++                        } else {
++                            Some(s.to_string())
++                        }
++                    }));
++                }
++            }
++            let mut clippy = ClippyCallbacks;
++            let mut default = DefaultCallbacks;
++            let callbacks: &mut (dyn rustc_driver::Callbacks + Send) =
++                if clippy_enabled { &mut clippy } else { &mut default };
++            rustc_driver::run_compiler(&args, callbacks, None, None)
++        })
++        .and_then(|result| result)
++        .is_err() as i32,
++    )
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c817d83b33aeb12df5817b1c5a2e891c3423df17
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++/// Lint data parsed from the Clippy source code.
++#[derive(Clone, PartialEq, Debug)]
++pub struct Lint {
++    pub name: &'static str,
++    pub group: &'static str,
++    pub desc: &'static str,
++    pub deprecation: Option<&'static str>,
++    pub module: &'static str,
++}
++
++#[derive(PartialOrd, PartialEq, Ord, Eq)]
++pub enum Level {
++    Allow,
++    Warn,
++    Deny,
++}
++
++pub const LINT_LEVELS: [(&str, Level); 8] = [
++    ("correctness", Level::Deny),
++    ("style", Level::Warn),
++    ("complexity", Level::Warn),
++    ("perf", Level::Warn),
++    ("restriction", Level::Allow),
++    ("pedantic", Level::Allow),
++    ("nursery", Level::Allow),
++    ("cargo", Level::Allow),
++];
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..72675c25175c98fdb421d25a79ed457ce795344e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2633 @@@
++//! This file is managed by `cargo dev update_lints`. Do not edit.
++
++use lazy_static::lazy_static;
++
++pub mod lint;
++pub use lint::Level;
++pub use lint::Lint;
++pub use lint::LINT_LEVELS;
++
++lazy_static! {
++// begin lint list, do not remove this comment, it’s used in `update_lints`
++pub static ref ALL_LINTS: Vec<Lint> = vec![
++    Lint {
++        name: "absurd_extreme_comparisons",
++        group: "correctness",
++        desc: "a comparison with a maximum or minimum value that is always true or false",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "almost_swapped",
++        group: "correctness",
++        desc: "`foo = bar; bar = foo` sequence",
++        deprecation: None,
++        module: "swap",
++    },
++    Lint {
++        name: "approx_constant",
++        group: "correctness",
++        desc: "the approximate of a known float constant (in `std::fXX::consts`)",
++        deprecation: None,
++        module: "approx_const",
++    },
++    Lint {
++        name: "as_conversions",
++        group: "restriction",
++        desc: "using a potentially dangerous silent `as` conversion",
++        deprecation: None,
++        module: "as_conversions",
++    },
++    Lint {
++        name: "assertions_on_constants",
++        group: "style",
++        desc: "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`",
++        deprecation: None,
++        module: "assertions_on_constants",
++    },
++    Lint {
++        name: "assign_op_pattern",
++        group: "style",
++        desc: "assigning the result of an operation on a variable to that same variable",
++        deprecation: None,
++        module: "assign_ops",
++    },
++    Lint {
++        name: "await_holding_lock",
++        group: "pedantic",
++        desc: "Inside an async function, holding a MutexGuard while calling await",
++        deprecation: None,
++        module: "await_holding_lock",
++    },
++    Lint {
++        name: "bad_bit_mask",
++        group: "correctness",
++        desc: "expressions of the form `_ & mask == select` that will only ever return `true` or `false`",
++        deprecation: None,
++        module: "bit_mask",
++    },
++    Lint {
++        name: "blacklisted_name",
++        group: "style",
++        desc: "usage of a blacklisted/placeholder name",
++        deprecation: None,
++        module: "blacklisted_name",
++    },
++    Lint {
++        name: "block_in_if_condition_expr",
++        group: "style",
++        desc: "braces that can be eliminated in conditions, e.g., `if { true } ...`",
++        deprecation: None,
++        module: "block_in_if_condition",
++    },
++    Lint {
++        name: "block_in_if_condition_stmt",
++        group: "style",
++        desc: "complex blocks in conditions, e.g., `if { let x = true; x } ...`",
++        deprecation: None,
++        module: "block_in_if_condition",
++    },
++    Lint {
++        name: "bool_comparison",
++        group: "complexity",
++        desc: "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`",
++        deprecation: None,
++        module: "needless_bool",
++    },
++    Lint {
++        name: "borrow_interior_mutable_const",
++        group: "correctness",
++        desc: "referencing `const` with interior mutability",
++        deprecation: None,
++        module: "non_copy_const",
++    },
++    Lint {
++        name: "borrowed_box",
++        group: "complexity",
++        desc: "a borrow of a boxed type",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "box_vec",
++        group: "perf",
++        desc: "usage of `Box<Vec<T>>`, vector elements are already on the heap",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "boxed_local",
++        group: "perf",
++        desc: "using `Box<T>` where unnecessary",
++        deprecation: None,
++        module: "escape",
++    },
++    Lint {
++        name: "builtin_type_shadow",
++        group: "style",
++        desc: "shadowing a builtin type",
++        deprecation: None,
++        module: "misc_early",
++    },
++    Lint {
++        name: "cargo_common_metadata",
++        group: "cargo",
++        desc: "common metadata is defined in `Cargo.toml`",
++        deprecation: None,
++        module: "cargo_common_metadata",
++    },
++    Lint {
++        name: "cast_lossless",
++        group: "pedantic",
++        desc: "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "cast_possible_truncation",
++        group: "pedantic",
++        desc: "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "cast_possible_wrap",
++        group: "pedantic",
++        desc: "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "cast_precision_loss",
++        group: "pedantic",
++        desc: "casts that cause loss of precision, e.g., `x as f32` where `x: u64`",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "cast_ptr_alignment",
++        group: "correctness",
++        desc: "cast from a pointer to a more-strictly-aligned pointer",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "cast_ref_to_mut",
++        group: "correctness",
++        desc: "a cast of reference to a mutable pointer",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "cast_sign_loss",
++        group: "pedantic",
++        desc: "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "char_lit_as_u8",
++        group: "complexity",
++        desc: "casting a character literal to `u8` truncates",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "chars_last_cmp",
++        group: "style",
++        desc: "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "chars_next_cmp",
++        group: "style",
++        desc: "using `.chars().next()` to check if a string starts with a char",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "checked_conversions",
++        group: "pedantic",
++        desc: "`try_from` could replace manual bounds checking when casting",
++        deprecation: None,
++        module: "checked_conversions",
++    },
++    Lint {
++        name: "clone_double_ref",
++        group: "correctness",
++        desc: "using `clone` on `&&T`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "clone_on_copy",
++        group: "complexity",
++        desc: "using `clone` on a `Copy` type",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "clone_on_ref_ptr",
++        group: "restriction",
++        desc: "using \'clone\' on a ref-counted pointer",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "cmp_nan",
++        group: "correctness",
++        desc: "comparisons to `NAN`, which will always return false, probably not intended",
++        deprecation: None,
++        module: "misc",
++    },
++    Lint {
++        name: "cmp_null",
++        group: "style",
++        desc: "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead.",
++        deprecation: None,
++        module: "ptr",
++    },
++    Lint {
++        name: "cmp_owned",
++        group: "perf",
++        desc: "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`",
++        deprecation: None,
++        module: "misc",
++    },
++    Lint {
++        name: "cognitive_complexity",
++        group: "nursery",
++        desc: "functions that should be split up into multiple functions",
++        deprecation: None,
++        module: "cognitive_complexity",
++    },
++    Lint {
++        name: "collapsible_if",
++        group: "style",
++        desc: "`if`s that can be collapsed (e.g., `if x { if y { ... } }` and `else { if x { ... } }`)",
++        deprecation: None,
++        module: "collapsible_if",
++    },
++    Lint {
++        name: "comparison_chain",
++        group: "style",
++        desc: "`if`s that can be rewritten with `match` and `cmp`",
++        deprecation: None,
++        module: "comparison_chain",
++    },
++    Lint {
++        name: "copy_iterator",
++        group: "pedantic",
++        desc: "implementing `Iterator` on a `Copy` type",
++        deprecation: None,
++        module: "copy_iterator",
++    },
++    Lint {
++        name: "crosspointer_transmute",
++        group: "complexity",
++        desc: "transmutes that have to or from types that are a pointer to the other",
++        deprecation: None,
++        module: "transmute",
++    },
++    Lint {
++        name: "dbg_macro",
++        group: "restriction",
++        desc: "`dbg!` macro is intended as a debugging tool",
++        deprecation: None,
++        module: "dbg_macro",
++    },
++    Lint {
++        name: "debug_assert_with_mut_call",
++        group: "nursery",
++        desc: "mutable arguments in `debug_assert{,_ne,_eq}!`",
++        deprecation: None,
++        module: "mutable_debug_assertion",
++    },
++    Lint {
++        name: "decimal_literal_representation",
++        group: "restriction",
++        desc: "using decimal representation when hexadecimal would be better",
++        deprecation: None,
++        module: "literal_representation",
++    },
++    Lint {
++        name: "declare_interior_mutable_const",
++        group: "correctness",
++        desc: "declaring `const` with interior mutability",
++        deprecation: None,
++        module: "non_copy_const",
++    },
++    Lint {
++        name: "default_trait_access",
++        group: "pedantic",
++        desc: "checks for literal calls to `Default::default()`",
++        deprecation: None,
++        module: "default_trait_access",
++    },
++    Lint {
++        name: "deprecated_cfg_attr",
++        group: "complexity",
++        desc: "usage of `cfg_attr(rustfmt)` instead of tool attributes",
++        deprecation: None,
++        module: "attrs",
++    },
++    Lint {
++        name: "deprecated_semver",
++        group: "correctness",
++        desc: "use of `#[deprecated(since = \"x\")]` where x is not semver",
++        deprecation: None,
++        module: "attrs",
++    },
++    Lint {
++        name: "deref_addrof",
++        group: "complexity",
++        desc: "use of `*&` or `*&mut` in an expression",
++        deprecation: None,
++        module: "reference",
++    },
++    Lint {
++        name: "derive_hash_xor_eq",
++        group: "correctness",
++        desc: "deriving `Hash` but implementing `PartialEq` explicitly",
++        deprecation: None,
++        module: "derive",
++    },
++    Lint {
++        name: "diverging_sub_expression",
++        group: "complexity",
++        desc: "whether an expression contains a diverging sub expression",
++        deprecation: None,
++        module: "eval_order_dependence",
++    },
++    Lint {
++        name: "doc_markdown",
++        group: "pedantic",
++        desc: "presence of `_`, `::` or camel-case outside backticks in documentation",
++        deprecation: None,
++        module: "doc",
++    },
++    Lint {
++        name: "double_comparisons",
++        group: "complexity",
++        desc: "unnecessary double comparisons that can be simplified",
++        deprecation: None,
++        module: "double_comparison",
++    },
++    Lint {
++        name: "double_must_use",
++        group: "style",
++        desc: "`#[must_use]` attribute on a `#[must_use]`-returning function / method",
++        deprecation: None,
++        module: "functions",
++    },
++    Lint {
++        name: "double_neg",
++        group: "style",
++        desc: "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++",
++        deprecation: None,
++        module: "misc_early",
++    },
++    Lint {
++        name: "double_parens",
++        group: "complexity",
++        desc: "Warn on unnecessary double parentheses",
++        deprecation: None,
++        module: "double_parens",
++    },
++    Lint {
++        name: "drop_bounds",
++        group: "correctness",
++        desc: "Bounds of the form `T: Drop` are useless",
++        deprecation: None,
++        module: "drop_bounds",
++    },
++    Lint {
++        name: "drop_copy",
++        group: "correctness",
++        desc: "calls to `std::mem::drop` with a value that implements Copy",
++        deprecation: None,
++        module: "drop_forget_ref",
++    },
++    Lint {
++        name: "drop_ref",
++        group: "correctness",
++        desc: "calls to `std::mem::drop` with a reference instead of an owned value",
++        deprecation: None,
++        module: "drop_forget_ref",
++    },
++    Lint {
++        name: "duplicate_underscore_argument",
++        group: "style",
++        desc: "function arguments having names which only differ by an underscore",
++        deprecation: None,
++        module: "misc_early",
++    },
++    Lint {
++        name: "duration_subsec",
++        group: "complexity",
++        desc: "checks for calculation of subsecond microseconds or milliseconds",
++        deprecation: None,
++        module: "duration_subsec",
++    },
++    Lint {
++        name: "else_if_without_else",
++        group: "restriction",
++        desc: "`if` expression with an `else if`, but without a final `else` branch",
++        deprecation: None,
++        module: "else_if_without_else",
++    },
++    Lint {
++        name: "empty_enum",
++        group: "pedantic",
++        desc: "enum with no variants",
++        deprecation: None,
++        module: "empty_enum",
++    },
++    Lint {
++        name: "empty_line_after_outer_attr",
++        group: "nursery",
++        desc: "empty line after outer attribute",
++        deprecation: None,
++        module: "attrs",
++    },
++    Lint {
++        name: "empty_loop",
++        group: "style",
++        desc: "empty `loop {}`, which should block or sleep",
++        deprecation: None,
++        module: "loops",
++    },
++    Lint {
++        name: "enum_clike_unportable_variant",
++        group: "correctness",
++        desc: "C-like enums that are `repr(isize/usize)` and have values that don\'t fit into an `i32`",
++        deprecation: None,
++        module: "enum_clike",
++    },
++    Lint {
++        name: "enum_glob_use",
++        group: "pedantic",
++        desc: "use items that import all variants of an enum",
++        deprecation: None,
++        module: "wildcard_imports",
++    },
++    Lint {
++        name: "enum_variant_names",
++        group: "style",
++        desc: "enums where all variants share a prefix/postfix",
++        deprecation: None,
++        module: "enum_variants",
++    },
++    Lint {
++        name: "eq_op",
++        group: "correctness",
++        desc: "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)",
++        deprecation: None,
++        module: "eq_op",
++    },
++    Lint {
++        name: "erasing_op",
++        group: "correctness",
++        desc: "using erasing operations, e.g., `x * 0` or `y & 0`",
++        deprecation: None,
++        module: "erasing_op",
++    },
++    Lint {
++        name: "eval_order_dependence",
++        group: "complexity",
++        desc: "whether a variable read occurs before a write depends on sub-expression evaluation order",
++        deprecation: None,
++        module: "eval_order_dependence",
++    },
++    Lint {
++        name: "excessive_precision",
++        group: "style",
++        desc: "excessive precision for float literal",
++        deprecation: None,
++        module: "float_literal",
++    },
++    Lint {
++        name: "exit",
++        group: "restriction",
++        desc: "`std::process::exit` is called, terminating the program",
++        deprecation: None,
++        module: "exit",
++    },
++    Lint {
++        name: "expect_fun_call",
++        group: "perf",
++        desc: "using any `expect` method with a function call",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "expl_impl_clone_on_copy",
++        group: "pedantic",
++        desc: "implementing `Clone` explicitly on `Copy` types",
++        deprecation: None,
++        module: "derive",
++    },
++    Lint {
++        name: "explicit_counter_loop",
++        group: "complexity",
++        desc: "for-looping with an explicit counter when `_.enumerate()` would do",
++        deprecation: None,
++        module: "loops",
++    },
++    Lint {
++        name: "explicit_deref_methods",
++        group: "pedantic",
++        desc: "Explicit use of deref or deref_mut method while not in a method chain.",
++        deprecation: None,
++        module: "dereference",
++    },
++    Lint {
++        name: "explicit_into_iter_loop",
++        group: "pedantic",
++        desc: "for-looping over `_.into_iter()` when `_` would do",
++        deprecation: None,
++        module: "loops",
++    },
++    Lint {
++        name: "explicit_iter_loop",
++        group: "pedantic",
++        desc: "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do",
++        deprecation: None,
++        module: "loops",
++    },
++    Lint {
++        name: "explicit_write",
++        group: "complexity",
++        desc: "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work",
++        deprecation: None,
++        module: "explicit_write",
++    },
++    Lint {
++        name: "extra_unused_lifetimes",
++        group: "complexity",
++        desc: "unused lifetimes in function definitions",
++        deprecation: None,
++        module: "lifetimes",
++    },
++    Lint {
++        name: "fallible_impl_from",
++        group: "nursery",
++        desc: "Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`",
++        deprecation: None,
++        module: "fallible_impl_from",
++    },
++    Lint {
++        name: "filetype_is_file",
++        group: "restriction",
++        desc: "`FileType::is_file` is not recommended to test for readable file type",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "filter_map",
++        group: "pedantic",
++        desc: "using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "filter_map_next",
++        group: "pedantic",
++        desc: "using combination of `filter_map` and `next` which can usually be written as a single method call",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "filter_next",
++        group: "complexity",
++        desc: "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "find_map",
++        group: "pedantic",
++        desc: "using a combination of `find` and `map` can usually be written as a single method call",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "flat_map_identity",
++        group: "complexity",
++        desc: "call to `flat_map` where `flatten` is sufficient",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "float_arithmetic",
++        group: "restriction",
++        desc: "any floating-point arithmetic statement",
++        deprecation: None,
++        module: "arithmetic",
++    },
++    Lint {
++        name: "float_cmp",
++        group: "correctness",
++        desc: "using `==` or `!=` on float values instead of comparing difference with an epsilon",
++        deprecation: None,
++        module: "misc",
++    },
++    Lint {
++        name: "float_cmp_const",
++        group: "restriction",
++        desc: "using `==` or `!=` on float constants instead of comparing difference with an epsilon",
++        deprecation: None,
++        module: "misc",
++    },
++    Lint {
++        name: "fn_address_comparisons",
++        group: "correctness",
++        desc: "comparison with an address of a function item",
++        deprecation: None,
++        module: "unnamed_address",
++    },
++    Lint {
++        name: "fn_params_excessive_bools",
++        group: "pedantic",
++        desc: "using too many bools in function parameters",
++        deprecation: None,
++        module: "excessive_bools",
++    },
++    Lint {
++        name: "fn_to_numeric_cast",
++        group: "style",
++        desc: "casting a function pointer to a numeric type other than usize",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "fn_to_numeric_cast_with_truncation",
++        group: "style",
++        desc: "casting a function pointer to a numeric type not wide enough to store the address",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "for_kv_map",
++        group: "style",
++        desc: "looping on a map using `iter` when `keys` or `values` would do",
++        deprecation: None,
++        module: "loops",
++    },
++    Lint {
++        name: "for_loop_over_option",
++        group: "correctness",
++        desc: "for-looping over an `Option`, which is more clearly expressed as an `if let`",
++        deprecation: None,
++        module: "loops",
++    },
++    Lint {
++        name: "for_loop_over_result",
++        group: "correctness",
++        desc: "for-looping over a `Result`, which is more clearly expressed as an `if let`",
++        deprecation: None,
++        module: "loops",
++    },
++    Lint {
++        name: "forget_copy",
++        group: "correctness",
++        desc: "calls to `std::mem::forget` with a value that implements Copy",
++        deprecation: None,
++        module: "drop_forget_ref",
++    },
++    Lint {
++        name: "forget_ref",
++        group: "correctness",
++        desc: "calls to `std::mem::forget` with a reference instead of an owned value",
++        deprecation: None,
++        module: "drop_forget_ref",
++    },
++    Lint {
++        name: "future_not_send",
++        group: "nursery",
++        desc: "public Futures must be Send",
++        deprecation: None,
++        module: "future_not_send",
++    },
++    Lint {
++        name: "get_last_with_len",
++        group: "complexity",
++        desc: "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler",
++        deprecation: None,
++        module: "get_last_with_len",
++    },
++    Lint {
++        name: "get_unwrap",
++        group: "restriction",
++        desc: "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "identity_conversion",
++        group: "complexity",
++        desc: "using always-identical `Into`/`From`/`IntoIter` conversions",
++        deprecation: None,
++        module: "identity_conversion",
++    },
++    Lint {
++        name: "identity_op",
++        group: "complexity",
++        desc: "using identity operations, e.g., `x + 0` or `y / 1`",
++        deprecation: None,
++        module: "identity_op",
++    },
++    Lint {
++        name: "if_let_mutex",
++        group: "correctness",
++        desc: "locking a `Mutex` in an `if let` block can cause deadlocks",
++        deprecation: None,
++        module: "if_let_mutex",
++    },
++    Lint {
++        name: "if_let_some_result",
++        group: "style",
++        desc: "usage of `ok()` in `if let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead",
++        deprecation: None,
++        module: "if_let_some_result",
++    },
++    Lint {
++        name: "if_not_else",
++        group: "pedantic",
++        desc: "`if` branches that could be swapped so no negation operation is necessary on the condition",
++        deprecation: None,
++        module: "if_not_else",
++    },
++    Lint {
++        name: "if_same_then_else",
++        group: "correctness",
++        desc: "`if` with the same `then` and `else` blocks",
++        deprecation: None,
++        module: "copies",
++    },
++    Lint {
++        name: "ifs_same_cond",
++        group: "correctness",
++        desc: "consecutive `if`s with the same condition",
++        deprecation: None,
++        module: "copies",
++    },
++    Lint {
++        name: "implicit_hasher",
++        group: "pedantic",
++        desc: "missing generalization over different hashers",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "implicit_return",
++        group: "restriction",
++        desc: "use a return statement like `return expr` instead of an expression",
++        deprecation: None,
++        module: "implicit_return",
++    },
++    Lint {
++        name: "implicit_saturating_sub",
++        group: "pedantic",
++        desc: "Perform saturating subtraction instead of implicitly checking lower bound of data type",
++        deprecation: None,
++        module: "implicit_saturating_sub",
++    },
++    Lint {
++        name: "imprecise_flops",
++        group: "nursery",
++        desc: "usage of imprecise floating point operations",
++        deprecation: None,
++        module: "floating_point_arithmetic",
++    },
++    Lint {
++        name: "inconsistent_digit_grouping",
++        group: "style",
++        desc: "integer literals with digits grouped inconsistently",
++        deprecation: None,
++        module: "literal_representation",
++    },
++    Lint {
++        name: "indexing_slicing",
++        group: "restriction",
++        desc: "indexing/slicing usage",
++        deprecation: None,
++        module: "indexing_slicing",
++    },
++    Lint {
++        name: "ineffective_bit_mask",
++        group: "correctness",
++        desc: "expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`",
++        deprecation: None,
++        module: "bit_mask",
++    },
++    Lint {
++        name: "inefficient_to_string",
++        group: "pedantic",
++        desc: "using `to_string` on `&&T` where `T: ToString`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "infallible_destructuring_match",
++        group: "style",
++        desc: "a `match` statement with a single infallible arm instead of a `let`",
++        deprecation: None,
++        module: "matches",
++    },
++    Lint {
++        name: "infinite_iter",
++        group: "correctness",
++        desc: "infinite iteration",
++        deprecation: None,
++        module: "infinite_iter",
++    },
++    Lint {
++        name: "inherent_to_string",
++        group: "style",
++        desc: "type implements inherent method `to_string()`, but should instead implement the `Display` trait",
++        deprecation: None,
++        module: "inherent_to_string",
++    },
++    Lint {
++        name: "inherent_to_string_shadow_display",
++        group: "correctness",
++        desc: "type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait",
++        deprecation: None,
++        module: "inherent_to_string",
++    },
++    Lint {
++        name: "inline_always",
++        group: "pedantic",
++        desc: "use of `#[inline(always)]`",
++        deprecation: None,
++        module: "attrs",
++    },
++    Lint {
++        name: "inline_fn_without_body",
++        group: "correctness",
++        desc: "use of `#[inline]` on trait methods without bodies",
++        deprecation: None,
++        module: "inline_fn_without_body",
++    },
++    Lint {
++        name: "int_plus_one",
++        group: "complexity",
++        desc: "instead of using `x >= y + 1`, use `x > y`",
++        deprecation: None,
++        module: "int_plus_one",
++    },
++    Lint {
++        name: "integer_arithmetic",
++        group: "restriction",
++        desc: "any integer arithmetic expression which could overflow or panic",
++        deprecation: None,
++        module: "arithmetic",
++    },
++    Lint {
++        name: "integer_division",
++        group: "restriction",
++        desc: "integer division may cause loss of precision",
++        deprecation: None,
++        module: "integer_division",
++    },
++    Lint {
++        name: "into_iter_on_ref",
++        group: "style",
++        desc: "using `.into_iter()` on a reference",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "invalid_atomic_ordering",
++        group: "correctness",
++        desc: "usage of invalid atomic ordering in atomic loads/stores and memory fences",
++        deprecation: None,
++        module: "atomic_ordering",
++    },
++    Lint {
++        name: "invalid_regex",
++        group: "correctness",
++        desc: "invalid regular expressions",
++        deprecation: None,
++        module: "regex",
++    },
++    Lint {
++        name: "invalid_upcast_comparisons",
++        group: "pedantic",
++        desc: "a comparison involving an upcast which is always true or false",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "items_after_statements",
++        group: "pedantic",
++        desc: "blocks where an item comes after a statement",
++        deprecation: None,
++        module: "items_after_statements",
++    },
++    Lint {
++        name: "iter_cloned_collect",
++        group: "style",
++        desc: "using `.cloned().collect()` on slice to create a `Vec`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "iter_next_loop",
++        group: "correctness",
++        desc: "for-looping over `_.next()` which is probably not intended",
++        deprecation: None,
++        module: "loops",
++    },
++    Lint {
++        name: "iter_nth",
++        group: "perf",
++        desc: "using `.iter().nth()` on a standard library type with O(1) element access",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "iter_nth_zero",
++        group: "style",
++        desc: "replace `iter.nth(0)` with `iter.next()`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "iter_skip_next",
++        group: "style",
++        desc: "using `.skip(x).next()` on an iterator",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "iterator_step_by_zero",
++        group: "correctness",
++        desc: "using `Iterator::step_by(0)`, which will panic at runtime",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "just_underscores_and_digits",
++        group: "style",
++        desc: "unclear name",
++        deprecation: None,
++        module: "non_expressive_names",
++    },
++    Lint {
++        name: "large_const_arrays",
++        group: "perf",
++        desc: "large non-scalar const array may cause performance overhead",
++        deprecation: None,
++        module: "large_const_arrays",
++    },
++    Lint {
++        name: "large_digit_groups",
++        group: "pedantic",
++        desc: "grouping digits into groups that are too large",
++        deprecation: None,
++        module: "literal_representation",
++    },
++    Lint {
++        name: "large_enum_variant",
++        group: "perf",
++        desc: "large size difference between variants on an enum",
++        deprecation: None,
++        module: "large_enum_variant",
++    },
++    Lint {
++        name: "large_stack_arrays",
++        group: "pedantic",
++        desc: "allocating large arrays on stack may cause stack overflow",
++        deprecation: None,
++        module: "large_stack_arrays",
++    },
++    Lint {
++        name: "len_without_is_empty",
++        group: "style",
++        desc: "traits or impls with a public `len` method but no corresponding `is_empty` method",
++        deprecation: None,
++        module: "len_zero",
++    },
++    Lint {
++        name: "len_zero",
++        group: "style",
++        desc: "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead",
++        deprecation: None,
++        module: "len_zero",
++    },
++    Lint {
++        name: "let_and_return",
++        group: "style",
++        desc: "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block",
++        deprecation: None,
++        module: "returns",
++    },
++    Lint {
++        name: "let_underscore_lock",
++        group: "correctness",
++        desc: "non-binding let on a synchronization lock",
++        deprecation: None,
++        module: "let_underscore",
++    },
++    Lint {
++        name: "let_underscore_must_use",
++        group: "restriction",
++        desc: "non-binding let on a `#[must_use]` expression",
++        deprecation: None,
++        module: "let_underscore",
++    },
++    Lint {
++        name: "let_unit_value",
++        group: "pedantic",
++        desc: "creating a `let` binding to a value of unit type, which usually can\'t be used afterwards",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "linkedlist",
++        group: "pedantic",
++        desc: "usage of LinkedList, usually a vector is faster, or a more specialized data structure like a `VecDeque`",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "logic_bug",
++        group: "correctness",
++        desc: "boolean expressions that contain terminals which can be eliminated",
++        deprecation: None,
++        module: "booleans",
++    },
++    Lint {
++        name: "lossy_float_literal",
++        group: "restriction",
++        desc: "lossy whole number float literals",
++        deprecation: None,
++        module: "float_literal",
++    },
++    Lint {
++        name: "macro_use_imports",
++        group: "pedantic",
++        desc: "#[macro_use] is no longer needed",
++        deprecation: None,
++        module: "macro_use",
++    },
++    Lint {
++        name: "main_recursion",
++        group: "style",
++        desc: "recursion using the entrypoint",
++        deprecation: None,
++        module: "main_recursion",
++    },
++    Lint {
++        name: "manual_memcpy",
++        group: "perf",
++        desc: "manually copying items between slices",
++        deprecation: None,
++        module: "loops",
++    },
++    Lint {
++        name: "manual_saturating_arithmetic",
++        group: "style",
++        desc: "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "manual_swap",
++        group: "complexity",
++        desc: "manual swap of two variables",
++        deprecation: None,
++        module: "swap",
++    },
++    Lint {
++        name: "many_single_char_names",
++        group: "style",
++        desc: "too many single character bindings",
++        deprecation: None,
++        module: "non_expressive_names",
++    },
++    Lint {
++        name: "map_clone",
++        group: "style",
++        desc: "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types",
++        deprecation: None,
++        module: "map_clone",
++    },
++    Lint {
++        name: "map_entry",
++        group: "perf",
++        desc: "use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`",
++        deprecation: None,
++        module: "entry",
++    },
++    Lint {
++        name: "map_flatten",
++        group: "pedantic",
++        desc: "using combinations of `flatten` and `map` which can usually be written as a single method call",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "match_as_ref",
++        group: "complexity",
++        desc: "a `match` on an Option value instead of using `as_ref()` or `as_mut`",
++        deprecation: None,
++        module: "matches",
++    },
++    Lint {
++        name: "match_bool",
++        group: "pedantic",
++        desc: "a `match` on a boolean expression instead of an `if..else` block",
++        deprecation: None,
++        module: "matches",
++    },
++    Lint {
++        name: "match_on_vec_items",
++        group: "correctness",
++        desc: "matching on vector elements can panic",
++        deprecation: None,
++        module: "match_on_vec_items",
++    },
++    Lint {
++        name: "match_overlapping_arm",
++        group: "style",
++        desc: "a `match` with overlapping arms",
++        deprecation: None,
++        module: "matches",
++    },
++    Lint {
++        name: "match_ref_pats",
++        group: "style",
++        desc: "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression",
++        deprecation: None,
++        module: "matches",
++    },
++    Lint {
++        name: "match_same_arms",
++        group: "pedantic",
++        desc: "`match` with identical arm bodies",
++        deprecation: None,
++        module: "copies",
++    },
++    Lint {
++        name: "match_single_binding",
++        group: "complexity",
++        desc: "a match with a single binding instead of using `let` statement",
++        deprecation: None,
++        module: "matches",
++    },
++    Lint {
++        name: "match_wild_err_arm",
++        group: "style",
++        desc: "a `match` with `Err(_)` arm and take drastic actions",
++        deprecation: None,
++        module: "matches",
++    },
++    Lint {
++        name: "maybe_infinite_iter",
++        group: "pedantic",
++        desc: "possible infinite iteration",
++        deprecation: None,
++        module: "infinite_iter",
++    },
++    Lint {
++        name: "mem_discriminant_non_enum",
++        group: "correctness",
++        desc: "calling `mem::descriminant` on non-enum type",
++        deprecation: None,
++        module: "mem_discriminant",
++    },
++    Lint {
++        name: "mem_forget",
++        group: "restriction",
++        desc: "`mem::forget` usage on `Drop` types, likely to cause memory leaks",
++        deprecation: None,
++        module: "mem_forget",
++    },
++    Lint {
++        name: "mem_replace_option_with_none",
++        group: "style",
++        desc: "replacing an `Option` with `None` instead of `take()`",
++        deprecation: None,
++        module: "mem_replace",
++    },
++    Lint {
++        name: "mem_replace_with_default",
++        group: "style",
++        desc: "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`",
++        deprecation: None,
++        module: "mem_replace",
++    },
++    Lint {
++        name: "mem_replace_with_uninit",
++        group: "correctness",
++        desc: "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`",
++        deprecation: None,
++        module: "mem_replace",
++    },
++    Lint {
++        name: "min_max",
++        group: "correctness",
++        desc: "`min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant",
++        deprecation: None,
++        module: "minmax",
++    },
++    Lint {
++        name: "mismatched_target_os",
++        group: "correctness",
++        desc: "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`",
++        deprecation: None,
++        module: "attrs",
++    },
++    Lint {
++        name: "misrefactored_assign_op",
++        group: "complexity",
++        desc: "having a variable on both sides of an assign op",
++        deprecation: None,
++        module: "assign_ops",
++    },
++    Lint {
++        name: "missing_const_for_fn",
++        group: "nursery",
++        desc: "Lint functions definitions that could be made `const fn`",
++        deprecation: None,
++        module: "missing_const_for_fn",
++    },
++    Lint {
++        name: "missing_docs_in_private_items",
++        group: "restriction",
++        desc: "detects missing documentation for public and private members",
++        deprecation: None,
++        module: "missing_doc",
++    },
++    Lint {
++        name: "missing_errors_doc",
++        group: "pedantic",
++        desc: "`pub fn` returns `Result` without `# Errors` in doc comment",
++        deprecation: None,
++        module: "doc",
++    },
++    Lint {
++        name: "missing_inline_in_public_items",
++        group: "restriction",
++        desc: "detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)",
++        deprecation: None,
++        module: "missing_inline",
++    },
++    Lint {
++        name: "missing_safety_doc",
++        group: "style",
++        desc: "`pub unsafe fn` without `# Safety` docs",
++        deprecation: None,
++        module: "doc",
++    },
++    Lint {
++        name: "mistyped_literal_suffixes",
++        group: "correctness",
++        desc: "mistyped literal suffix",
++        deprecation: None,
++        module: "literal_representation",
++    },
++    Lint {
++        name: "mixed_case_hex_literals",
++        group: "style",
++        desc: "hex literals whose letter digits are not consistently upper- or lowercased",
++        deprecation: None,
++        module: "misc_early",
++    },
++    Lint {
++        name: "module_inception",
++        group: "style",
++        desc: "modules that have the same name as their parent module",
++        deprecation: None,
++        module: "enum_variants",
++    },
++    Lint {
++        name: "module_name_repetitions",
++        group: "pedantic",
++        desc: "type names prefixed/postfixed with their containing module\'s name",
++        deprecation: None,
++        module: "enum_variants",
++    },
++    Lint {
++        name: "modulo_arithmetic",
++        group: "restriction",
++        desc: "any modulo arithmetic statement",
++        deprecation: None,
++        module: "modulo_arithmetic",
++    },
++    Lint {
++        name: "modulo_one",
++        group: "correctness",
++        desc: "taking a number modulo 1, which always returns 0",
++        deprecation: None,
++        module: "misc",
++    },
++    Lint {
++        name: "multiple_crate_versions",
++        group: "cargo",
++        desc: "multiple versions of the same crate being used",
++        deprecation: None,
++        module: "multiple_crate_versions",
++    },
++    Lint {
++        name: "multiple_inherent_impl",
++        group: "restriction",
++        desc: "Multiple inherent impl that could be grouped",
++        deprecation: None,
++        module: "inherent_impl",
++    },
++    Lint {
++        name: "must_use_candidate",
++        group: "pedantic",
++        desc: "function or method that could take a `#[must_use]` attribute",
++        deprecation: None,
++        module: "functions",
++    },
++    Lint {
++        name: "must_use_unit",
++        group: "style",
++        desc: "`#[must_use]` attribute on a unit-returning function / method",
++        deprecation: None,
++        module: "functions",
++    },
++    Lint {
++        name: "mut_from_ref",
++        group: "correctness",
++        desc: "fns that create mutable refs from immutable ref args",
++        deprecation: None,
++        module: "ptr",
++    },
++    Lint {
++        name: "mut_mut",
++        group: "pedantic",
++        desc: "usage of double-mut refs, e.g., `&mut &mut ...`",
++        deprecation: None,
++        module: "mut_mut",
++    },
++    Lint {
++        name: "mut_range_bound",
++        group: "complexity",
++        desc: "for loop over a range where one of the bounds is a mutable variable",
++        deprecation: None,
++        module: "loops",
++    },
++    Lint {
++        name: "mutable_key_type",
++        group: "correctness",
++        desc: "Check for mutable `Map`/`Set` key type",
++        deprecation: None,
++        module: "mut_key",
++    },
++    Lint {
++        name: "mutex_atomic",
++        group: "perf",
++        desc: "using a mutex where an atomic value could be used instead",
++        deprecation: None,
++        module: "mutex_atomic",
++    },
++    Lint {
++        name: "mutex_integer",
++        group: "nursery",
++        desc: "using a mutex for an integer type",
++        deprecation: None,
++        module: "mutex_atomic",
++    },
++    Lint {
++        name: "naive_bytecount",
++        group: "perf",
++        desc: "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values",
++        deprecation: None,
++        module: "bytecount",
++    },
++    Lint {
++        name: "needless_bool",
++        group: "complexity",
++        desc: "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`",
++        deprecation: None,
++        module: "needless_bool",
++    },
++    Lint {
++        name: "needless_borrow",
++        group: "nursery",
++        desc: "taking a reference that is going to be automatically dereferenced",
++        deprecation: None,
++        module: "needless_borrow",
++    },
++    Lint {
++        name: "needless_borrowed_reference",
++        group: "complexity",
++        desc: "taking a needless borrowed reference",
++        deprecation: None,
++        module: "needless_borrowed_ref",
++    },
++    Lint {
++        name: "needless_collect",
++        group: "perf",
++        desc: "collecting an iterator when collect is not needed",
++        deprecation: None,
++        module: "loops",
++    },
++    Lint {
++        name: "needless_continue",
++        group: "pedantic",
++        desc: "`continue` statements that can be replaced by a rearrangement of code",
++        deprecation: None,
++        module: "needless_continue",
++    },
++    Lint {
++        name: "needless_doctest_main",
++        group: "style",
++        desc: "presence of `fn main() {` in code examples",
++        deprecation: None,
++        module: "doc",
++    },
++    Lint {
++        name: "needless_lifetimes",
++        group: "complexity",
++        desc: "using explicit lifetimes for references in function arguments when elision rules would allow omitting them",
++        deprecation: None,
++        module: "lifetimes",
++    },
++    Lint {
++        name: "needless_pass_by_value",
++        group: "pedantic",
++        desc: "functions taking arguments by value, but not consuming them in its body",
++        deprecation: None,
++        module: "needless_pass_by_value",
++    },
++    Lint {
++        name: "needless_range_loop",
++        group: "style",
++        desc: "for-looping over a range of indices where an iterator over items would do",
++        deprecation: None,
++        module: "loops",
++    },
++    Lint {
++        name: "needless_return",
++        group: "style",
++        desc: "using a return statement like `return expr;` where an expression would suffice",
++        deprecation: None,
++        module: "returns",
++    },
++    Lint {
++        name: "needless_update",
++        group: "complexity",
++        desc: "using `Foo { ..base }` when there are no missing fields",
++        deprecation: None,
++        module: "needless_update",
++    },
++    Lint {
++        name: "neg_cmp_op_on_partial_ord",
++        group: "complexity",
++        desc: "The use of negated comparison operators on partially ordered types may produce confusing code.",
++        deprecation: None,
++        module: "neg_cmp_op_on_partial_ord",
++    },
++    Lint {
++        name: "neg_multiply",
++        group: "style",
++        desc: "multiplying integers with `-1`",
++        deprecation: None,
++        module: "neg_multiply",
++    },
++    Lint {
++        name: "never_loop",
++        group: "correctness",
++        desc: "any loop that will always `break` or `return`",
++        deprecation: None,
++        module: "loops",
++    },
++    Lint {
++        name: "new_ret_no_self",
++        group: "style",
++        desc: "not returning type containing `Self` in a `new` method",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "new_without_default",
++        group: "style",
++        desc: "`fn new() -> Self` method without `Default` implementation",
++        deprecation: None,
++        module: "new_without_default",
++    },
++    Lint {
++        name: "no_effect",
++        group: "complexity",
++        desc: "statements with no effect",
++        deprecation: None,
++        module: "no_effect",
++    },
++    Lint {
++        name: "non_ascii_literal",
++        group: "pedantic",
++        desc: "using any literal non-ASCII chars in a string literal instead of using the `\\\\u` escape",
++        deprecation: None,
++        module: "unicode",
++    },
++    Lint {
++        name: "nonminimal_bool",
++        group: "complexity",
++        desc: "boolean expressions that can be written more concisely",
++        deprecation: None,
++        module: "booleans",
++    },
++    Lint {
++        name: "nonsensical_open_options",
++        group: "correctness",
++        desc: "nonsensical combination of options for opening a file",
++        deprecation: None,
++        module: "open_options",
++    },
++    Lint {
++        name: "not_unsafe_ptr_arg_deref",
++        group: "correctness",
++        desc: "public functions dereferencing raw pointer arguments but not marked `unsafe`",
++        deprecation: None,
++        module: "functions",
++    },
++    Lint {
++        name: "ok_expect",
++        group: "style",
++        desc: "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "op_ref",
++        group: "style",
++        desc: "taking a reference to satisfy the type constraints on `==`",
++        deprecation: None,
++        module: "eq_op",
++    },
++    Lint {
++        name: "option_and_then_some",
++        group: "complexity",
++        desc: "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "option_as_ref_deref",
++        group: "complexity",
++        desc: "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "option_env_unwrap",
++        group: "correctness",
++        desc: "using `option_env!(...).unwrap()` to get environment variable",
++        deprecation: None,
++        module: "option_env_unwrap",
++    },
++    Lint {
++        name: "option_expect_used",
++        group: "restriction",
++        desc: "using `Option.expect()`, which might be better handled",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "option_map_or_none",
++        group: "style",
++        desc: "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "option_map_unit_fn",
++        group: "complexity",
++        desc: "using `option.map(f)`, where `f` is a function or closure that returns `()`",
++        deprecation: None,
++        module: "map_unit_fn",
++    },
++    Lint {
++        name: "option_map_unwrap_or",
++        group: "pedantic",
++        desc: "using `Option.map(f).unwrap_or(a)`, which is more succinctly expressed as `map_or(a, f)`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "option_map_unwrap_or_else",
++        group: "pedantic",
++        desc: "using `Option.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `map_or_else(g, f)`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "option_option",
++        group: "pedantic",
++        desc: "usage of `Option<Option<T>>`",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "option_unwrap_used",
++        group: "restriction",
++        desc: "using `Option.unwrap()`, which should at least get a better message using `expect()`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "or_fun_call",
++        group: "perf",
++        desc: "using any `*or` method with a function call, which suggests `*or_else`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "out_of_bounds_indexing",
++        group: "correctness",
++        desc: "out of bounds constant indexing",
++        deprecation: None,
++        module: "indexing_slicing",
++    },
++    Lint {
++        name: "overflow_check_conditional",
++        group: "complexity",
++        desc: "overflow checks inspired by C which are likely to panic",
++        deprecation: None,
++        module: "overflow_check_conditional",
++    },
++    Lint {
++        name: "panic",
++        group: "restriction",
++        desc: "usage of the `panic!` macro",
++        deprecation: None,
++        module: "panic_unimplemented",
++    },
++    Lint {
++        name: "panic_params",
++        group: "style",
++        desc: "missing parameters in `panic!` calls",
++        deprecation: None,
++        module: "panic_unimplemented",
++    },
++    Lint {
++        name: "panicking_unwrap",
++        group: "correctness",
++        desc: "checks for calls of `unwrap[_err]()` that will always fail",
++        deprecation: None,
++        module: "unwrap",
++    },
++    Lint {
++        name: "partialeq_ne_impl",
++        group: "complexity",
++        desc: "re-implementing `PartialEq::ne`",
++        deprecation: None,
++        module: "partialeq_ne_impl",
++    },
++    Lint {
++        name: "path_buf_push_overwrite",
++        group: "nursery",
++        desc: "calling `push` with file system root on `PathBuf` can overwrite it",
++        deprecation: None,
++        module: "path_buf_push_overwrite",
++    },
++    Lint {
++        name: "possible_missing_comma",
++        group: "correctness",
++        desc: "possible missing comma in array",
++        deprecation: None,
++        module: "formatting",
++    },
++    Lint {
++        name: "precedence",
++        group: "complexity",
++        desc: "operations where precedence may be unclear",
++        deprecation: None,
++        module: "precedence",
++    },
++    Lint {
++        name: "print_literal",
++        group: "style",
++        desc: "printing a literal with a format string",
++        deprecation: None,
++        module: "write",
++    },
++    Lint {
++        name: "print_stdout",
++        group: "restriction",
++        desc: "printing on stdout",
++        deprecation: None,
++        module: "write",
++    },
++    Lint {
++        name: "print_with_newline",
++        group: "style",
++        desc: "using `print!()` with a format string that ends in a single newline",
++        deprecation: None,
++        module: "write",
++    },
++    Lint {
++        name: "println_empty_string",
++        group: "style",
++        desc: "using `println!(\"\")` with an empty string",
++        deprecation: None,
++        module: "write",
++    },
++    Lint {
++        name: "ptr_arg",
++        group: "style",
++        desc: "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively",
++        deprecation: None,
++        module: "ptr",
++    },
++    Lint {
++        name: "ptr_offset_with_cast",
++        group: "complexity",
++        desc: "unneeded pointer offset cast",
++        deprecation: None,
++        module: "ptr_offset_with_cast",
++    },
++    Lint {
++        name: "pub_enum_variant_names",
++        group: "pedantic",
++        desc: "enums where all variants share a prefix/postfix",
++        deprecation: None,
++        module: "enum_variants",
++    },
++    Lint {
++        name: "question_mark",
++        group: "style",
++        desc: "checks for expressions that could be replaced by the question mark operator",
++        deprecation: None,
++        module: "question_mark",
++    },
++    Lint {
++        name: "range_minus_one",
++        group: "complexity",
++        desc: "`x..=(y-1)` reads better as `x..y`",
++        deprecation: None,
++        module: "ranges",
++    },
++    Lint {
++        name: "range_plus_one",
++        group: "pedantic",
++        desc: "`x..(y+1)` reads better as `x..=y`",
++        deprecation: None,
++        module: "ranges",
++    },
++    Lint {
++        name: "range_zip_with_len",
++        group: "complexity",
++        desc: "zipping iterator with a range when `enumerate()` would do",
++        deprecation: None,
++        module: "ranges",
++    },
++    Lint {
++        name: "redundant_allocation",
++        group: "perf",
++        desc: "redundant allocation",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "redundant_clone",
++        group: "perf",
++        desc: "`clone()` of an owned value that is going to be dropped immediately",
++        deprecation: None,
++        module: "redundant_clone",
++    },
++    Lint {
++        name: "redundant_closure",
++        group: "style",
++        desc: "redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)",
++        deprecation: None,
++        module: "eta_reduction",
++    },
++    Lint {
++        name: "redundant_closure_call",
++        group: "complexity",
++        desc: "throwaway closures called in the expression they are defined",
++        deprecation: None,
++        module: "misc_early",
++    },
++    Lint {
++        name: "redundant_closure_for_method_calls",
++        group: "pedantic",
++        desc: "redundant closures for method calls",
++        deprecation: None,
++        module: "eta_reduction",
++    },
++    Lint {
++        name: "redundant_field_names",
++        group: "style",
++        desc: "checks for fields in struct literals where shorthands could be used",
++        deprecation: None,
++        module: "redundant_field_names",
++    },
++    Lint {
++        name: "redundant_pattern",
++        group: "style",
++        desc: "using `name @ _` in a pattern",
++        deprecation: None,
++        module: "misc_early",
++    },
++    Lint {
++        name: "redundant_pattern_matching",
++        group: "style",
++        desc: "use the proper utility function avoiding an `if let`",
++        deprecation: None,
++        module: "redundant_pattern_matching",
++    },
++    Lint {
++        name: "redundant_pub_crate",
++        group: "nursery",
++        desc: "Using `pub(crate)` visibility on items that are not crate visible due to the visibility of the module that contains them.",
++        deprecation: None,
++        module: "redundant_pub_crate",
++    },
++    Lint {
++        name: "redundant_static_lifetimes",
++        group: "style",
++        desc: "Using explicit `\'static` lifetime for constants or statics when elision rules would allow omitting them.",
++        deprecation: None,
++        module: "redundant_static_lifetimes",
++    },
++    Lint {
++        name: "ref_in_deref",
++        group: "complexity",
++        desc: "Use of reference in auto dereference expression.",
++        deprecation: None,
++        module: "reference",
++    },
++    Lint {
++        name: "regex_macro",
++        group: "style",
++        desc: "use of `regex!(_)` instead of `Regex::new(_)`",
++        deprecation: None,
++        module: "regex",
++    },
++    Lint {
++        name: "rest_pat_in_fully_bound_structs",
++        group: "restriction",
++        desc: "a match on a struct that binds all fields but still uses the wildcard pattern",
++        deprecation: None,
++        module: "matches",
++    },
++    Lint {
++        name: "result_expect_used",
++        group: "restriction",
++        desc: "using `Result.expect()`, which might be better handled",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "result_map_or_into_option",
++        group: "style",
++        desc: "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "result_map_unit_fn",
++        group: "complexity",
++        desc: "using `result.map(f)`, where `f` is a function or closure that returns `()`",
++        deprecation: None,
++        module: "map_unit_fn",
++    },
++    Lint {
++        name: "result_map_unwrap_or_else",
++        group: "pedantic",
++        desc: "using `Result.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `.map_or_else(g, f)`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "result_unwrap_used",
++        group: "restriction",
++        desc: "using `Result.unwrap()`, which might be better handled",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "reverse_range_loop",
++        group: "correctness",
++        desc: "iteration over an empty range, such as `10..0` or `5..5`",
++        deprecation: None,
++        module: "loops",
++    },
++    Lint {
++        name: "same_functions_in_if_condition",
++        group: "pedantic",
++        desc: "consecutive `if`s with the same function call",
++        deprecation: None,
++        module: "copies",
++    },
++    Lint {
++        name: "search_is_some",
++        group: "complexity",
++        desc: "using an iterator search followed by `is_some()`, which is more succinctly expressed as a call to `any()`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "serde_api_misuse",
++        group: "correctness",
++        desc: "various things that will negatively affect your serde experience",
++        deprecation: None,
++        module: "serde_api",
++    },
++    Lint {
++        name: "shadow_reuse",
++        group: "restriction",
++        desc: "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`",
++        deprecation: None,
++        module: "shadow",
++    },
++    Lint {
++        name: "shadow_same",
++        group: "restriction",
++        desc: "rebinding a name to itself, e.g., `let mut x = &mut x`",
++        deprecation: None,
++        module: "shadow",
++    },
++    Lint {
++        name: "shadow_unrelated",
++        group: "pedantic",
++        desc: "rebinding a name without even using the original value",
++        deprecation: None,
++        module: "shadow",
++    },
++    Lint {
++        name: "short_circuit_statement",
++        group: "complexity",
++        desc: "using a short circuit boolean condition as a statement",
++        deprecation: None,
++        module: "misc",
++    },
++    Lint {
++        name: "should_implement_trait",
++        group: "style",
++        desc: "defining a method that should be implementing a std trait",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "similar_names",
++        group: "pedantic",
++        desc: "similarly named items and bindings",
++        deprecation: None,
++        module: "non_expressive_names",
++    },
++    Lint {
++        name: "single_char_pattern",
++        group: "perf",
++        desc: "using a single-character str where a char could be used, e.g., `_.split(\"x\")`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "single_component_path_imports",
++        group: "style",
++        desc: "imports with single component path are redundant",
++        deprecation: None,
++        module: "single_component_path_imports",
++    },
++    Lint {
++        name: "single_match",
++        group: "style",
++        desc: "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`",
++        deprecation: None,
++        module: "matches",
++    },
++    Lint {
++        name: "single_match_else",
++        group: "pedantic",
++        desc: "a `match` statement with two arms where the second arm\'s pattern is a placeholder instead of a specific match pattern",
++        deprecation: None,
++        module: "matches",
++    },
++    Lint {
++        name: "skip_while_next",
++        group: "complexity",
++        desc: "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "slow_vector_initialization",
++        group: "perf",
++        desc: "slow vector initialization",
++        deprecation: None,
++        module: "slow_vector_initialization",
++    },
++    Lint {
++        name: "string_add",
++        group: "restriction",
++        desc: "using `x + ..` where x is a `String` instead of `push_str()`",
++        deprecation: None,
++        module: "strings",
++    },
++    Lint {
++        name: "string_add_assign",
++        group: "pedantic",
++        desc: "using `x = x + ..` where x is a `String` instead of `push_str()`",
++        deprecation: None,
++        module: "strings",
++    },
++    Lint {
++        name: "string_extend_chars",
++        group: "style",
++        desc: "using `x.extend(s.chars())` where s is a `&str` or `String`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "string_lit_as_bytes",
++        group: "style",
++        desc: "calling `as_bytes` on a string literal instead of using a byte string literal",
++        deprecation: None,
++        module: "strings",
++    },
++    Lint {
++        name: "struct_excessive_bools",
++        group: "pedantic",
++        desc: "using too many bools in a struct",
++        deprecation: None,
++        module: "excessive_bools",
++    },
++    Lint {
++        name: "suboptimal_flops",
++        group: "nursery",
++        desc: "usage of sub-optimal floating point operations",
++        deprecation: None,
++        module: "floating_point_arithmetic",
++    },
++    Lint {
++        name: "suspicious_arithmetic_impl",
++        group: "correctness",
++        desc: "suspicious use of operators in impl of arithmetic trait",
++        deprecation: None,
++        module: "suspicious_trait_impl",
++    },
++    Lint {
++        name: "suspicious_assignment_formatting",
++        group: "style",
++        desc: "suspicious formatting of `*=`, `-=` or `!=`",
++        deprecation: None,
++        module: "formatting",
++    },
++    Lint {
++        name: "suspicious_else_formatting",
++        group: "style",
++        desc: "suspicious formatting of `else`",
++        deprecation: None,
++        module: "formatting",
++    },
++    Lint {
++        name: "suspicious_map",
++        group: "complexity",
++        desc: "suspicious usage of map",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "suspicious_op_assign_impl",
++        group: "correctness",
++        desc: "suspicious use of operators in impl of OpAssign trait",
++        deprecation: None,
++        module: "suspicious_trait_impl",
++    },
++    Lint {
++        name: "suspicious_unary_op_formatting",
++        group: "style",
++        desc: "suspicious formatting of unary `-` or `!` on the RHS of a BinOp",
++        deprecation: None,
++        module: "formatting",
++    },
++    Lint {
++        name: "tabs_in_doc_comments",
++        group: "style",
++        desc: "using tabs in doc comments is not recommended",
++        deprecation: None,
++        module: "tabs_in_doc_comments",
++    },
++    Lint {
++        name: "temporary_assignment",
++        group: "complexity",
++        desc: "assignments to temporaries",
++        deprecation: None,
++        module: "temporary_assignment",
++    },
++    Lint {
++        name: "temporary_cstring_as_ptr",
++        group: "correctness",
++        desc: "getting the inner pointer of a temporary `CString`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "to_digit_is_some",
++        group: "style",
++        desc: "`char.is_digit()` is clearer",
++        deprecation: None,
++        module: "to_digit_is_some",
++    },
++    Lint {
++        name: "todo",
++        group: "restriction",
++        desc: "`todo!` should not be present in production code",
++        deprecation: None,
++        module: "panic_unimplemented",
++    },
++    Lint {
++        name: "too_many_arguments",
++        group: "complexity",
++        desc: "functions with too many arguments",
++        deprecation: None,
++        module: "functions",
++    },
++    Lint {
++        name: "too_many_lines",
++        group: "pedantic",
++        desc: "functions with too many lines",
++        deprecation: None,
++        module: "functions",
++    },
++    Lint {
++        name: "toplevel_ref_arg",
++        group: "style",
++        desc: "an entire binding declared as `ref`, in a function argument or a `let` statement",
++        deprecation: None,
++        module: "misc",
++    },
++    Lint {
++        name: "transmute_bytes_to_str",
++        group: "complexity",
++        desc: "transmutes from a `&[u8]` to a `&str`",
++        deprecation: None,
++        module: "transmute",
++    },
++    Lint {
++        name: "transmute_float_to_int",
++        group: "complexity",
++        desc: "transmutes from a float to an integer",
++        deprecation: None,
++        module: "transmute",
++    },
++    Lint {
++        name: "transmute_int_to_bool",
++        group: "complexity",
++        desc: "transmutes from an integer to a `bool`",
++        deprecation: None,
++        module: "transmute",
++    },
++    Lint {
++        name: "transmute_int_to_char",
++        group: "complexity",
++        desc: "transmutes from an integer to a `char`",
++        deprecation: None,
++        module: "transmute",
++    },
++    Lint {
++        name: "transmute_int_to_float",
++        group: "complexity",
++        desc: "transmutes from an integer to a float",
++        deprecation: None,
++        module: "transmute",
++    },
++    Lint {
++        name: "transmute_ptr_to_ptr",
++        group: "complexity",
++        desc: "transmutes from a pointer to a pointer / a reference to a reference",
++        deprecation: None,
++        module: "transmute",
++    },
++    Lint {
++        name: "transmute_ptr_to_ref",
++        group: "complexity",
++        desc: "transmutes from a pointer to a reference type",
++        deprecation: None,
++        module: "transmute",
++    },
++    Lint {
++        name: "transmuting_null",
++        group: "correctness",
++        desc: "transmutes from a null pointer to a reference, which is undefined behavior",
++        deprecation: None,
++        module: "transmuting_null",
++    },
++    Lint {
++        name: "trivial_regex",
++        group: "style",
++        desc: "trivial regular expressions",
++        deprecation: None,
++        module: "regex",
++    },
++    Lint {
++        name: "trivially_copy_pass_by_ref",
++        group: "pedantic",
++        desc: "functions taking small copyable arguments by reference",
++        deprecation: None,
++        module: "trivially_copy_pass_by_ref",
++    },
++    Lint {
++        name: "try_err",
++        group: "style",
++        desc: "return errors explicitly rather than hiding them behind a `?`",
++        deprecation: None,
++        module: "try_err",
++    },
++    Lint {
++        name: "type_complexity",
++        group: "complexity",
++        desc: "usage of very complex types that might be better factored into `type` definitions",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "type_repetition_in_bounds",
++        group: "pedantic",
++        desc: "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`",
++        deprecation: None,
++        module: "trait_bounds",
++    },
++    Lint {
++        name: "unicode_not_nfc",
++        group: "pedantic",
++        desc: "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)",
++        deprecation: None,
++        module: "unicode",
++    },
++    Lint {
++        name: "unimplemented",
++        group: "restriction",
++        desc: "`unimplemented!` should not be present in production code",
++        deprecation: None,
++        module: "panic_unimplemented",
++    },
++    Lint {
++        name: "uninit_assumed_init",
++        group: "correctness",
++        desc: "`MaybeUninit::uninit().assume_init()`",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "unit_arg",
++        group: "complexity",
++        desc: "passing unit to a function",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "unit_cmp",
++        group: "correctness",
++        desc: "comparing unit values",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "unknown_clippy_lints",
++        group: "style",
++        desc: "unknown_lints for scoped Clippy lints",
++        deprecation: None,
++        module: "attrs",
++    },
++    Lint {
++        name: "unnecessary_cast",
++        group: "complexity",
++        desc: "cast to the same type, e.g., `x as i32` where `x: i32`",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "unnecessary_filter_map",
++        group: "complexity",
++        desc: "using `filter_map` when a more succinct alternative exists",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "unnecessary_fold",
++        group: "style",
++        desc: "using `fold` when a more succinct alternative exists",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "unnecessary_mut_passed",
++        group: "style",
++        desc: "an argument passed as a mutable reference although the callee only demands an immutable reference",
++        deprecation: None,
++        module: "mut_reference",
++    },
++    Lint {
++        name: "unnecessary_operation",
++        group: "complexity",
++        desc: "outer expressions with no effect",
++        deprecation: None,
++        module: "no_effect",
++    },
++    Lint {
++        name: "unnecessary_unwrap",
++        group: "complexity",
++        desc: "checks for calls of `unwrap[_err]()` that cannot fail",
++        deprecation: None,
++        module: "unwrap",
++    },
++    Lint {
++        name: "unneeded_field_pattern",
++        group: "restriction",
++        desc: "struct fields bound to a wildcard instead of using `..`",
++        deprecation: None,
++        module: "misc_early",
++    },
++    Lint {
++        name: "unneeded_wildcard_pattern",
++        group: "complexity",
++        desc: "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)",
++        deprecation: None,
++        module: "misc_early",
++    },
++    Lint {
++        name: "unreachable",
++        group: "restriction",
++        desc: "`unreachable!` should not be present in production code",
++        deprecation: None,
++        module: "panic_unimplemented",
++    },
++    Lint {
++        name: "unreadable_literal",
++        group: "pedantic",
++        desc: "long integer literal without underscores",
++        deprecation: None,
++        module: "literal_representation",
++    },
++    Lint {
++        name: "unsafe_derive_deserialize",
++        group: "pedantic",
++        desc: "deriving `serde::Deserialize` on a type that has methods using `unsafe`",
++        deprecation: None,
++        module: "derive",
++    },
++    Lint {
++        name: "unsafe_removed_from_name",
++        group: "style",
++        desc: "`unsafe` removed from API names on import",
++        deprecation: None,
++        module: "unsafe_removed_from_name",
++    },
++    Lint {
++        name: "unseparated_literal_suffix",
++        group: "pedantic",
++        desc: "literals whose suffix is not separated by an underscore",
++        deprecation: None,
++        module: "misc_early",
++    },
++    Lint {
++        name: "unsound_collection_transmute",
++        group: "correctness",
++        desc: "transmute between collections of layout-incompatible types",
++        deprecation: None,
++        module: "transmute",
++    },
++    Lint {
++        name: "unused_io_amount",
++        group: "correctness",
++        desc: "unused written/read amount",
++        deprecation: None,
++        module: "unused_io_amount",
++    },
++    Lint {
++        name: "unused_self",
++        group: "pedantic",
++        desc: "methods that contain a `self` argument but don\'t use it",
++        deprecation: None,
++        module: "unused_self",
++    },
++    Lint {
++        name: "unused_unit",
++        group: "style",
++        desc: "needless unit expression",
++        deprecation: None,
++        module: "returns",
++    },
++    Lint {
++        name: "use_debug",
++        group: "restriction",
++        desc: "use of `Debug`-based formatting",
++        deprecation: None,
++        module: "write",
++    },
++    Lint {
++        name: "use_self",
++        group: "nursery",
++        desc: "Unnecessary structure name repetition whereas `Self` is applicable",
++        deprecation: None,
++        module: "use_self",
++    },
++    Lint {
++        name: "used_underscore_binding",
++        group: "pedantic",
++        desc: "using a binding which is prefixed with an underscore",
++        deprecation: None,
++        module: "misc",
++    },
++    Lint {
++        name: "useless_asref",
++        group: "complexity",
++        desc: "using `as_ref` where the types before and after the call are the same",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "useless_attribute",
++        group: "correctness",
++        desc: "use of lint attributes on `extern crate` items",
++        deprecation: None,
++        module: "attrs",
++    },
++    Lint {
++        name: "useless_format",
++        group: "complexity",
++        desc: "useless use of `format!`",
++        deprecation: None,
++        module: "format",
++    },
++    Lint {
++        name: "useless_let_if_seq",
++        group: "style",
++        desc: "unidiomatic `let mut` declaration followed by initialization in `if`",
++        deprecation: None,
++        module: "let_if_seq",
++    },
++    Lint {
++        name: "useless_transmute",
++        group: "nursery",
++        desc: "transmutes that have the same to and from types or could be a cast/coercion",
++        deprecation: None,
++        module: "transmute",
++    },
++    Lint {
++        name: "useless_vec",
++        group: "perf",
++        desc: "useless `vec!`",
++        deprecation: None,
++        module: "vec",
++    },
++    Lint {
++        name: "vec_box",
++        group: "complexity",
++        desc: "usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap",
++        deprecation: None,
++        module: "types",
++    },
++    Lint {
++        name: "verbose_bit_mask",
++        group: "style",
++        desc: "expressions where a bit mask is less readable than the corresponding method call",
++        deprecation: None,
++        module: "bit_mask",
++    },
++    Lint {
++        name: "verbose_file_reads",
++        group: "restriction",
++        desc: "use of `File::read_to_end` or `File::read_to_string`",
++        deprecation: None,
++        module: "verbose_file_reads",
++    },
++    Lint {
++        name: "vtable_address_comparisons",
++        group: "correctness",
++        desc: "comparison with an address of a trait vtable",
++        deprecation: None,
++        module: "unnamed_address",
++    },
++    Lint {
++        name: "while_immutable_condition",
++        group: "correctness",
++        desc: "variables used within while expression are not mutated in the body",
++        deprecation: None,
++        module: "loops",
++    },
++    Lint {
++        name: "while_let_loop",
++        group: "complexity",
++        desc: "`loop { if let { ... } else break }`, which can be written as a `while let` loop",
++        deprecation: None,
++        module: "loops",
++    },
++    Lint {
++        name: "while_let_on_iterator",
++        group: "style",
++        desc: "using a while-let loop instead of a for loop on an iterator",
++        deprecation: None,
++        module: "loops",
++    },
++    Lint {
++        name: "wildcard_dependencies",
++        group: "cargo",
++        desc: "wildcard dependencies being used",
++        deprecation: None,
++        module: "wildcard_dependencies",
++    },
++    Lint {
++        name: "wildcard_enum_match_arm",
++        group: "restriction",
++        desc: "a wildcard enum match arm using `_`",
++        deprecation: None,
++        module: "matches",
++    },
++    Lint {
++        name: "wildcard_imports",
++        group: "pedantic",
++        desc: "lint `use _::*` statements",
++        deprecation: None,
++        module: "wildcard_imports",
++    },
++    Lint {
++        name: "wildcard_in_or_patterns",
++        group: "complexity",
++        desc: "a wildcard pattern used with others patterns in same match arm",
++        deprecation: None,
++        module: "matches",
++    },
++    Lint {
++        name: "write_literal",
++        group: "style",
++        desc: "writing a literal with a format string",
++        deprecation: None,
++        module: "write",
++    },
++    Lint {
++        name: "write_with_newline",
++        group: "style",
++        desc: "using `write!()` with a format string that ends in a single newline",
++        deprecation: None,
++        module: "write",
++    },
++    Lint {
++        name: "writeln_empty_string",
++        group: "style",
++        desc: "using `writeln!(buf, \"\")` with an empty string",
++        deprecation: None,
++        module: "write",
++    },
++    Lint {
++        name: "wrong_pub_self_convention",
++        group: "restriction",
++        desc: "defining a public method named with an established prefix (like \"into_\") that takes `self` with the wrong convention",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "wrong_self_convention",
++        group: "style",
++        desc: "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention",
++        deprecation: None,
++        module: "methods",
++    },
++    Lint {
++        name: "wrong_transmute",
++        group: "correctness",
++        desc: "transmutes that are confusing at best, undefined behaviour at worst and always useless",
++        deprecation: None,
++        module: "transmute",
++    },
++    Lint {
++        name: "zero_divided_by_zero",
++        group: "complexity",
++        desc: "usage of `0.0 / 0.0` to obtain NaN instead of `f32::NAN` or `f64::NAN`",
++        deprecation: None,
++        module: "zero_div_zero",
++    },
++    Lint {
++        name: "zero_prefixed_literal",
++        group: "complexity",
++        desc: "integer literals starting with `0`",
++        deprecation: None,
++        module: "misc_early",
++    },
++    Lint {
++        name: "zero_ptr",
++        group: "style",
++        desc: "using `0 as *{const, mut} T`",
++        deprecation: None,
++        module: "misc",
++    },
++    Lint {
++        name: "zero_width_space",
++        group: "correctness",
++        desc: "using a zero-width space in a string literal, which is confusing",
++        deprecation: None,
++        module: "unicode",
++    },
++    Lint {
++        name: "zst_offset",
++        group: "correctness",
++        desc: "Check for offset calculations on raw pointers to zero-sized types",
++        deprecation: None,
++        module: "methods",
++    },
++];
++// end lint list, do not remove this comment, it’s used in `update_lints`
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bc43a34ed5d4aaa4e843306abfebab9970a955f2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,219 @@@
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++
++use rustc_tools_util::VersionInfo;
++use std::env;
++use std::ffi::OsString;
++use std::path::PathBuf;
++use std::process::{self, Command};
++
++const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.
++
++Usage:
++    cargo clippy [options] [--] [<opts>...]
++
++Common options:
++    -h, --help               Print this message
++    -V, --version            Print version info and exit
++
++Other options are the same as `cargo check`.
++
++To allow or deny a lint from the command line you can use `cargo clippy --`
++with:
++
++    -W --warn OPT       Set lint warnings
++    -A --allow OPT      Set lint allowed
++    -D --deny OPT       Set lint denied
++    -F --forbid OPT     Set lint forbidden
++
++You can use tool lints to allow or deny lints from your code, eg.:
++
++    #[allow(clippy::needless_lifetimes)]
++"#;
++
++fn show_help() {
++    println!("{}", CARGO_CLIPPY_HELP);
++}
++
++fn show_version() {
++    let version_info = rustc_tools_util::get_version_info!();
++    println!("{}", version_info);
++}
++
++pub fn main() {
++    // Check for version and help flags even when invoked as 'cargo-clippy'
++    if env::args().any(|a| a == "--help" || a == "-h") {
++        show_help();
++        return;
++    }
++
++    if env::args().any(|a| a == "--version" || a == "-V") {
++        show_version();
++        return;
++    }
++
++    if let Err(code) = process(env::args().skip(2)) {
++        process::exit(code);
++    }
++}
++
++struct ClippyCmd {
++    unstable_options: bool,
++    cargo_subcommand: &'static str,
++    args: Vec<String>,
++    clippy_args: String,
++}
++
++impl ClippyCmd {
++    fn new<I>(mut old_args: I) -> Self
++    where
++        I: Iterator<Item = String>,
++    {
++        let mut cargo_subcommand = "check";
++        let mut unstable_options = false;
++        let mut args = vec![];
++
++        for arg in old_args.by_ref() {
++            match arg.as_str() {
++                "--fix" => {
++                    cargo_subcommand = "fix";
++                    continue;
++                },
++                "--" => break,
++                // Cover -Zunstable-options and -Z unstable-options
++                s if s.ends_with("unstable-options") => unstable_options = true,
++                _ => {},
++            }
++
++            args.push(arg);
++        }
++
++        if cargo_subcommand == "fix" && !unstable_options {
++            panic!("Usage of `--fix` requires `-Z unstable-options`");
++        }
++
++        // Run the dogfood tests directly on nightly cargo. This is required due
++        // to a bug in rustup.rs when running cargo on custom toolchains. See issue #3118.
++        if env::var_os("CLIPPY_DOGFOOD").is_some() && cfg!(windows) {
++            args.insert(0, "+nightly".to_string());
++        }
++
++        let clippy_args: String = old_args.map(|arg| format!("{}__CLIPPY_HACKERY__", arg)).collect();
++
++        ClippyCmd {
++            unstable_options,
++            cargo_subcommand,
++            args,
++            clippy_args,
++        }
++    }
++
++    fn path_env(&self) -> &'static str {
++        if self.unstable_options {
++            "RUSTC_WORKSPACE_WRAPPER"
++        } else {
++            "RUSTC_WRAPPER"
++        }
++    }
++
++    fn path() -> PathBuf {
++        let mut path = env::current_exe()
++            .expect("current executable path invalid")
++            .with_file_name("clippy-driver");
++
++        if cfg!(windows) {
++            path.set_extension("exe");
++        }
++
++        path
++    }
++
++    fn target_dir() -> Option<(&'static str, OsString)> {
++        env::var_os("CLIPPY_DOGFOOD")
++            .map(|_| {
++                env::var_os("CARGO_MANIFEST_DIR").map_or_else(
++                    || std::ffi::OsString::from("clippy_dogfood"),
++                    |d| {
++                        std::path::PathBuf::from(d)
++                            .join("target")
++                            .join("dogfood")
++                            .into_os_string()
++                    },
++                )
++            })
++            .map(|p| ("CARGO_TARGET_DIR", p))
++    }
++
++    fn into_std_cmd(self) -> Command {
++        let mut cmd = Command::new("cargo");
++
++        cmd.env(self.path_env(), Self::path())
++            .envs(ClippyCmd::target_dir())
++            .env("CLIPPY_ARGS", self.clippy_args)
++            .arg(self.cargo_subcommand)
++            .args(&self.args);
++
++        cmd
++    }
++}
++
++fn process<I>(old_args: I) -> Result<(), i32>
++where
++    I: Iterator<Item = String>,
++{
++    let cmd = ClippyCmd::new(old_args);
++
++    let mut cmd = cmd.into_std_cmd();
++
++    let exit_status = cmd
++        .spawn()
++        .expect("could not run cargo")
++        .wait()
++        .expect("failed to wait for cargo?");
++
++    if exit_status.success() {
++        Ok(())
++    } else {
++        Err(exit_status.code().unwrap_or(-1))
++    }
++}
++
++#[cfg(test)]
++mod tests {
++    use super::ClippyCmd;
++
++    #[test]
++    #[should_panic]
++    fn fix_without_unstable() {
++        let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
++        let _ = ClippyCmd::new(args);
++    }
++
++    #[test]
++    fn fix_unstable() {
++        let args = "cargo clippy --fix -Zunstable-options"
++            .split_whitespace()
++            .map(ToString::to_string);
++        let cmd = ClippyCmd::new(args);
++        assert_eq!("fix", cmd.cargo_subcommand);
++        assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env());
++        assert!(cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
++    }
++
++    #[test]
++    fn check() {
++        let args = "cargo clippy".split_whitespace().map(ToString::to_string);
++        let cmd = ClippyCmd::new(args);
++        assert_eq!("check", cmd.cargo_subcommand);
++        assert_eq!("RUSTC_WRAPPER", cmd.path_env());
++    }
++
++    #[test]
++    fn check_unstable() {
++        let args = "cargo clippy -Zunstable-options"
++            .split_whitespace()
++            .map(ToString::to_string);
++        let cmd = ClippyCmd::new(args);
++        assert_eq!("check", cmd.cargo_subcommand);
++        assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env());
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..624ca892add310fa3acd8197a9cd33c034781106
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++pub trait A {}
++
++macro_rules! __implicit_hasher_test_macro {
++    (impl< $($impl_arg:tt),* > for $kind:ty where $($bounds:tt)*) => {
++        __implicit_hasher_test_macro!( ($($impl_arg),*) ($kind) ($($bounds)*) );
++    };
++
++    (($($impl_arg:tt)*) ($($kind_arg:tt)*) ($($bounds:tt)*)) => {
++        impl< $($impl_arg)* > test_macro::A for $($kind_arg)* where $($bounds)* { }
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3c385343f7010a276050589bf0e23e9e13bde45d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++use lazy_static::lazy_static;
++use std::env;
++use std::path::PathBuf;
++
++lazy_static! {
++    pub static ref CARGO_TARGET_DIR: PathBuf = {
++        match env::var_os("CARGO_TARGET_DIR") {
++            Some(v) => v.into(),
++            None => env::current_dir().unwrap().join("target"),
++        }
++    };
++    pub static ref TARGET_LIB: PathBuf = {
++        if let Some(path) = option_env!("TARGET_LIBS") {
++            path.into()
++        } else {
++            let mut dir = CARGO_TARGET_DIR.clone();
++            if let Some(target) = env::var_os("CARGO_BUILD_TARGET") {
++                dir.push(target);
++            }
++            dir.push(env!("PROFILE"));
++            dir
++        }
++    };
++}
++
++#[must_use]
++pub fn is_rustc_test_suite() -> bool {
++    option_env!("RUSTC_TEST_SUITE").is_some()
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..de2cf6d7873f8dac5276c3768531eaeb343a0661
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,168 @@@
++#![feature(test)] // compiletest_rs requires this attribute
++
++use compiletest_rs as compiletest;
++use compiletest_rs::common::Mode as TestMode;
++
++use std::env::{self, set_var};
++use std::ffi::OsStr;
++use std::fs;
++use std::io;
++use std::path::{Path, PathBuf};
++
++mod cargo;
++
++fn host_lib() -> PathBuf {
++    if let Some(path) = option_env!("HOST_LIBS") {
++        PathBuf::from(path)
++    } else {
++        cargo::CARGO_TARGET_DIR.join(env!("PROFILE"))
++    }
++}
++
++fn clippy_driver_path() -> PathBuf {
++    if let Some(path) = option_env!("CLIPPY_DRIVER_PATH") {
++        PathBuf::from(path)
++    } else {
++        cargo::TARGET_LIB.join("clippy-driver")
++    }
++}
++
++// When we'll want to use `extern crate ..` for a dependency that is used
++// both by the crate and the compiler itself, we can't simply pass -L flags
++// as we'll get a duplicate matching versions. Instead, disambiguate with
++// `--extern dep=path`.
++// See https://github.com/rust-lang/rust-clippy/issues/4015.
++//
++// FIXME: We cannot use `cargo build --message-format=json` to resolve to dependency files.
++//        Because it would force-rebuild if the options passed to `build` command is not the same
++//        as what we manually pass to `cargo` invocation
++fn third_party_crates() -> String {
++    use std::collections::HashMap;
++    static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints"];
++    let dep_dir = cargo::TARGET_LIB.join("deps");
++    let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len());
++    for entry in fs::read_dir(dep_dir).unwrap() {
++        let path = match entry {
++            Ok(entry) => entry.path(),
++            _ => continue,
++        };
++        if let Some(name) = path.file_name().and_then(OsStr::to_str) {
++            for dep in CRATES {
++                if name.starts_with(&format!("lib{}-", dep)) && name.ends_with(".rlib") {
++                    crates.entry(dep).or_insert(path);
++                    break;
++                }
++            }
++        }
++    }
++
++    let v: Vec<_> = crates
++        .into_iter()
++        .map(|(dep, path)| format!("--extern {}={}", dep, path.display()))
++        .collect();
++    v.join(" ")
++}
++
++fn default_config() -> compiletest::Config {
++    let mut config = compiletest::Config::default();
++
++    if let Ok(name) = env::var("TESTNAME") {
++        config.filter = Some(name);
++    }
++
++    if let Some(path) = option_env!("RUSTC_LIB_PATH") {
++        let path = PathBuf::from(path);
++        config.run_lib_path = path.clone();
++        config.compile_lib_path = path;
++    }
++
++    config.target_rustcflags = Some(format!(
++        "-L {0} -L {1} -Dwarnings -Zui-testing {2}",
++        host_lib().join("deps").display(),
++        cargo::TARGET_LIB.join("deps").display(),
++        third_party_crates(),
++    ));
++
++    config.build_base = if cargo::is_rustc_test_suite() {
++        // This make the stderr files go to clippy OUT_DIR on rustc repo build dir
++        let mut path = PathBuf::from(env!("OUT_DIR"));
++        path.push("test_build_base");
++        path
++    } else {
++        host_lib().join("test_build_base")
++    };
++    config.rustc_path = clippy_driver_path();
++    config
++}
++
++fn run_mode(cfg: &mut compiletest::Config) {
++    cfg.mode = TestMode::Ui;
++    cfg.src_base = Path::new("tests").join("ui");
++    compiletest::run_tests(&cfg);
++}
++
++#[allow(clippy::identity_conversion)]
++fn run_ui_toml_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
++    let mut result = true;
++    let opts = compiletest::test_opts(config);
++    for dir in fs::read_dir(&config.src_base)? {
++        let dir = dir?;
++        if !dir.file_type()?.is_dir() {
++            continue;
++        }
++        let dir_path = dir.path();
++        set_var("CARGO_MANIFEST_DIR", &dir_path);
++        for file in fs::read_dir(&dir_path)? {
++            let file = file?;
++            let file_path = file.path();
++            if file.file_type()?.is_dir() {
++                continue;
++            }
++            if file_path.extension() != Some(OsStr::new("rs")) {
++                continue;
++            }
++            let paths = compiletest::common::TestPaths {
++                file: file_path,
++                base: config.src_base.clone(),
++                relative_dir: dir_path.file_name().unwrap().into(),
++            };
++            let test_name = compiletest::make_test_name(&config, &paths);
++            let index = tests
++                .iter()
++                .position(|test| test.desc.name == test_name)
++                .expect("The test should be in there");
++            result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
++        }
++    }
++    Ok(result)
++}
++
++fn run_ui_toml(config: &mut compiletest::Config) {
++    config.mode = TestMode::Ui;
++    config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap();
++
++    let tests = compiletest::make_tests(&config);
++
++    let res = run_ui_toml_tests(&config, tests);
++    match res {
++        Ok(true) => {},
++        Ok(false) => panic!("Some tests failed"),
++        Err(e) => {
++            println!("I/O failure during tests: {:?}", e);
++        },
++    }
++}
++
++fn prepare_env() {
++    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
++    set_var("CLIPPY_TESTS", "true");
++    //set_var("RUST_BACKTRACE", "0");
++}
++
++#[test]
++fn compile_test() {
++    prepare_env();
++    let mut config = default_config();
++    run_mode(&mut config);
++    run_ui_toml(&mut config);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..81af3d3033b23c268a152258d74a30a8cd0d4ecc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,76 @@@
++// Dogfood cannot run on Windows
++#![cfg(not(windows))]
++
++use lazy_static::lazy_static;
++use std::path::PathBuf;
++use std::process::Command;
++
++mod cargo;
++
++lazy_static! {
++    static ref CLIPPY_PATH: PathBuf = cargo::TARGET_LIB.join("cargo-clippy");
++}
++
++#[test]
++fn dogfood_clippy() {
++    // run clippy on itself and fail the test if lint warnings are reported
++    if cargo::is_rustc_test_suite() {
++        return;
++    }
++    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
++
++    let output = Command::new(&*CLIPPY_PATH)
++        .current_dir(root_dir)
++        .env("CLIPPY_DOGFOOD", "1")
++        .env("CARGO_INCREMENTAL", "0")
++        .arg("clippy-preview")
++        .arg("--all-targets")
++        .arg("--all-features")
++        .arg("--")
++        .args(&["-D", "clippy::all"])
++        .args(&["-D", "clippy::internal"])
++        .args(&["-D", "clippy::pedantic"])
++        .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
++        .output()
++        .unwrap();
++    println!("status: {}", output.status);
++    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
++    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
++
++    assert!(output.status.success());
++}
++
++#[test]
++fn dogfood_subprojects() {
++    // run clippy on remaining subprojects and fail the test if lint warnings are reported
++    if cargo::is_rustc_test_suite() {
++        return;
++    }
++    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
++
++    for d in &[
++        "clippy_workspace_tests",
++        "clippy_workspace_tests/src",
++        "clippy_workspace_tests/subcrate",
++        "clippy_workspace_tests/subcrate/src",
++        "clippy_dev",
++        "rustc_tools_util",
++    ] {
++        let output = Command::new(&*CLIPPY_PATH)
++            .current_dir(root_dir.join(d))
++            .env("CLIPPY_DOGFOOD", "1")
++            .env("CARGO_INCREMENTAL", "0")
++            .arg("clippy")
++            .arg("--")
++            .args(&["-D", "clippy::all"])
++            .args(&["-D", "clippy::pedantic"])
++            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
++            .output()
++            .unwrap();
++        println!("status: {}", output.status);
++        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
++        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
++
++        assert!(output.status.success());
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3aff8741f6051d7e6b95e525bc1a37812b3e2173
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++use std::path::PathBuf;
++use std::process::Command;
++
++#[test]
++fn fmt() {
++    if option_env!("RUSTC_TEST_SUITE").is_some() || option_env!("NO_FMT_TEST").is_some() {
++        return;
++    }
++
++    // Skip this test if rustup nightly is unavailable
++    let rustup_output = Command::new("rustup")
++        .args(&["component", "list", "--toolchain", "nightly"])
++        .output()
++        .unwrap();
++    assert!(rustup_output.status.success());
++    let component_output = String::from_utf8_lossy(&rustup_output.stdout);
++    if !component_output.contains("rustfmt") {
++        return;
++    }
++
++    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
++    let dev_dir = root_dir.join("clippy_dev");
++    let target_dir = root_dir.join("target");
++    let target_dir = target_dir.to_str().unwrap();
++    let output = Command::new("cargo")
++        .current_dir(dev_dir)
++        .args(&["+nightly", "run", "--target-dir", target_dir, "--", "fmt", "--check"])
++        .output()
++        .unwrap();
++
++    println!("status: {}", output.status);
++    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
++    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
++
++    assert!(
++        output.status.success(),
++        "Formatting check failed. Run `cargo dev fmt` to update formatting."
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a78273ce0da41238ae7a2f5ee4842b001cc8a61b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,82 @@@
++#![cfg(feature = "integration")]
++
++use std::env;
++use std::ffi::OsStr;
++use std::process::Command;
++
++#[cfg_attr(feature = "integration", test)]
++fn integration_test() {
++    let repo_name = env::var("INTEGRATION").expect("`INTEGRATION` var not set");
++    let repo_url = format!("https://github.com/{}", repo_name);
++    let crate_name = repo_name
++        .split('/')
++        .nth(1)
++        .expect("repo name should have format `<org>/<name>`");
++
++    let mut repo_dir = tempfile::tempdir().expect("couldn't create temp dir").into_path();
++    repo_dir.push(crate_name);
++
++    let st = Command::new("git")
++        .args(&[
++            OsStr::new("clone"),
++            OsStr::new("--depth=1"),
++            OsStr::new(&repo_url),
++            OsStr::new(&repo_dir),
++        ])
++        .status()
++        .expect("unable to run git");
++    assert!(st.success());
++
++    let root_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
++    let target_dir = std::path::Path::new(&root_dir).join("target");
++    let clippy_binary = target_dir.join(env!("PROFILE")).join("cargo-clippy");
++
++    let output = Command::new(clippy_binary)
++        .current_dir(repo_dir)
++        .env("RUST_BACKTRACE", "full")
++        .env("CARGO_TARGET_DIR", target_dir)
++        .args(&[
++            "clippy",
++            "--all-targets",
++            "--all-features",
++            "--",
++            "--cap-lints",
++            "warn",
++            "-Wclippy::pedantic",
++            "-Wclippy::nursery",
++        ])
++        .output()
++        .expect("unable to run clippy");
++
++    let stderr = String::from_utf8_lossy(&output.stderr);
++    if stderr.contains("internal compiler error") {
++        let backtrace_start = stderr
++            .find("thread 'rustc' panicked at")
++            .expect("start of backtrace not found");
++        let backtrace_end = stderr
++            .rfind("error: internal compiler error")
++            .expect("end of backtrace not found");
++
++        panic!(
++            "internal compiler error\nBacktrace:\n\n{}",
++            &stderr[backtrace_start..backtrace_end]
++        );
++    } else if stderr.contains("query stack during panic") {
++        panic!("query stack during panic in the output");
++    } else if stderr.contains("E0463") {
++        // Encountering E0463 (can't find crate for `x`) did _not_ cause the build to fail in the
++        // past. Even though it should have. That's why we explicitly panic here.
++        // See PR #3552 and issue #3523 for more background.
++        panic!("error: E0463");
++    } else if stderr.contains("E0514") {
++        panic!("incompatible crate versions");
++    } else if stderr.contains("failed to run `rustc` to learn about target-specific information") {
++        panic!("couldn't find librustc_driver, consider setting `LD_LIBRARY_PATH`");
++    }
++
++    match output.status.code() {
++        Some(0) => println!("Compilation successful"),
++        Some(code) => eprintln!("Compilation failed. Exit code: {}", code),
++        None => panic!("Process terminated by signal"),
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d87bb4be3c3f976e26e94ddfad4135d6993cf45d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++#![allow(clippy::assertions_on_constants)]
++
++use std::fs::{self, DirEntry};
++use std::path::Path;
++
++#[test]
++fn test_missing_tests() {
++    let missing_files = explore_directory(Path::new("./tests"));
++    if !missing_files.is_empty() {
++        assert!(
++            false,
++            format!(
++                "Didn't see a test file for the following files:\n\n{}\n",
++                missing_files
++                    .iter()
++                    .map(|s| format!("\t{}", s))
++                    .collect::<Vec<_>>()
++                    .join("\n")
++            )
++        );
++    }
++}
++
++/*
++Test for missing files.
++
++Since rs files are alphabetically before stderr/stdout, we can sort by the full name
++and iter in that order. If we've seen the file stem for the first time and it's not
++a rust file, it means the rust file has to be missing.
++*/
++fn explore_directory(dir: &Path) -> Vec<String> {
++    let mut missing_files: Vec<String> = Vec::new();
++    let mut current_file = String::new();
++    let mut files: Vec<DirEntry> = fs::read_dir(dir).unwrap().filter_map(Result::ok).collect();
++    files.sort_by_key(std::fs::DirEntry::path);
++    for entry in &files {
++        let path = entry.path();
++        if path.is_dir() {
++            missing_files.extend(explore_directory(&path));
++        } else {
++            let file_stem = path.file_stem().unwrap().to_str().unwrap().to_string();
++            if let Some(ext) = path.extension() {
++                match ext.to_str().unwrap() {
++                    "rs" => current_file = file_stem.clone(),
++                    "stderr" | "stdout" => {
++                        if file_stem != current_file {
++                            missing_files.push(path.to_str().unwrap().to_string());
++                        }
++                    },
++                    _ => continue,
++                };
++            }
++        }
++    }
++    missing_files
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..823e01a33b96172e13855c6c023599f2852e71f9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++fn this_is_obviously(not: a, toml: file) {
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3b9458fc2840e7494cab39c77c379854f0ddc777
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++// error-pattern: error reading Clippy's configuration file
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..28c1a568a632befdff4d59cf085b3fbe065f458a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++error: error reading Clippy's configuration file `$DIR/clippy.toml`: expected an equals, found an identifier at line 1 column 4
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..168675394d7f45124940fcfa3433aab378c1fbd8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++blacklisted-names = 42
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8a0062423ad168f2cf09730cbb234d2c689a9f47
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++// error-pattern: error reading Clippy's configuration file: `blacklisted-names` is expected to be a
++// `Vec < String >` but is a `integer`
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..efd02bcbb6e28968bdb1e227e74a4190289c1110
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ac47b195042ebfb3682978d7847888ef49a2f40f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++# that one is an error
++cyclomatic-complexity-threshold = 42
++
++# that one is white-listed
++[third-party]
++clippy-feature = "nightly"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2577c1eef92bc5b987edef4cf3aa9d91be3e81f7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++// error-pattern: error reading Clippy's configuration file: found deprecated field
++// `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..34267c0daf7c2cfd50fc647b6cd3f2b891e32856
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++error: error reading Clippy's configuration file `$DIR/clippy.toml`: found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..022eec3e0e2bf2f021fd61709cb87746ee59425b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++max-fn-params-bools = 1
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..42897b389edfc6e82d4279b78d1ca7633541ec20
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++#![warn(clippy::fn_params_excessive_bools)]
++
++fn f(_: bool) {}
++fn g(_: bool, _: bool) {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d05adc3d36e3350d97cc31a27c3704321af0b254
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++error: more than 1 bools in function parameters
++  --> $DIR/test.rs:4:1
++   |
++LL | fn g(_: bool, _: bool) {}
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::fn-params-excessive-bools` implied by `-D warnings`
++   = help: consider refactoring bools into two-variant enums
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..951dbb523d9558c5b75e5197f0df52e4b8ee9448
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++too-many-lines-threshold = 1
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a47677a1f3a2e02c8bb6a5b3bdf3f742c6954e6c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++#![warn(clippy::too_many_lines)]
++
++// This function should be considered one line.
++fn many_comments_but_one_line_of_code() {
++    /* println!("This is good."); */
++    // println!("This is good.");
++    /* */ // println!("This is good.");
++    /* */ // println!("This is good.");
++    /* */ // println!("This is good.");
++    /* */ // println!("This is good.");
++    /* println!("This is good.");
++    println!("This is good.");
++    println!("This is good."); */
++    println!("This is good.");
++}
++
++// This should be considered two and a fail.
++fn too_many_lines() {
++    println!("This is bad.");
++    println!("This is bad.");
++}
++
++// This should be considered one line.
++#[rustfmt::skip]
++fn comment_starts_after_code() {
++    let _ = 5; /* closing comment. */ /*
++    this line shouldn't be counted theoretically.
++    */
++}
++
++// This should be considered one line.
++fn comment_after_code() {
++    let _ = 5; /* this line should get counted once. */
++}
++
++// This should fail since it is technically two lines.
++#[rustfmt::skip]
++fn comment_before_code() {
++    let _ = "test";
++    /* This comment extends to the front of
++    the code but this line should still count. */ let _ = 5;
++}
++
++// This should be considered one line.
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4b77ac551e770f174b59e666841fbb2267cc2b76
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++error: This function has a large number of lines.
++  --> $DIR/test.rs:18:1
++   |
++LL | / fn too_many_lines() {
++LL | |     println!("This is bad.");
++LL | |     println!("This is bad.");
++LL | | }
++   | |_^
++   |
++   = note: `-D clippy::too-many-lines` implied by `-D warnings`
++
++error: This function has a large number of lines.
++  --> $DIR/test.rs:38:1
++   |
++LL | / fn comment_before_code() {
++LL | |     let _ = "test";
++LL | |     /* This comment extends to the front of
++LL | |     the code but this line should still count. */ let _ = 5;
++LL | | }
++   | |_^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a1dd6b2f0819cb67361baacf14d6e51f1fcac992
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++# that one is white-listed
++[third-party]
++clippy-feature = "nightly"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..270b9c5c43c138883d76aa4c7e4733576b6d68c7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++// error-pattern: should give absolutely no error
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3912ab542777dc5723cd4d602b157cec48469e41
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++max-struct-bools = 0
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..242984680e163bd9eef1de3b132ffe93c7663c73
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++#![warn(clippy::struct_excessive_bools)]
++
++struct S {
++    a: bool,
++}
++
++struct Foo {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..65861d10d0fdb52a37ac512694ce1f9161d75ebc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++error: more than 0 bools in a struct
++  --> $DIR/test.rs:3:1
++   |
++LL | / struct S {
++LL | |     a: bool,
++LL | | }
++   | |_^
++   |
++   = note: `-D clippy::struct-excessive-bools` implied by `-D warnings`
++   = help: consider using a state machine or refactoring bools into two-variant enums
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6abe5a3bbc2737feba84d51919176142eb8c7fd4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++blacklisted-names = ["toto", "tata", "titi"]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cb35d0e8589d2e4f2052da05477757d0d609b5d3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++#![allow(dead_code)]
++#![allow(clippy::single_match)]
++#![allow(unused_variables)]
++#![warn(clippy::blacklisted_name)]
++
++fn test(toto: ()) {}
++
++fn main() {
++    let toto = 42;
++    let tata = 42;
++    let titi = 42;
++
++    let tatab = 42;
++    let tatatataic = 42;
++
++    match (42, Some(1337), Some(0)) {
++        (toto, Some(tata), titi @ Some(_)) => (),
++        _ => (),
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..84ba77851f77e91bae30f9241053a44bf08e3170
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++error: use of a blacklisted/placeholder name `toto`
++  --> $DIR/conf_french_blacklisted_name.rs:6:9
++   |
++LL | fn test(toto: ()) {}
++   |         ^^^^
++   |
++   = note: `-D clippy::blacklisted-name` implied by `-D warnings`
++
++error: use of a blacklisted/placeholder name `toto`
++  --> $DIR/conf_french_blacklisted_name.rs:9:9
++   |
++LL |     let toto = 42;
++   |         ^^^^
++
++error: use of a blacklisted/placeholder name `tata`
++  --> $DIR/conf_french_blacklisted_name.rs:10:9
++   |
++LL |     let tata = 42;
++   |         ^^^^
++
++error: use of a blacklisted/placeholder name `titi`
++  --> $DIR/conf_french_blacklisted_name.rs:11:9
++   |
++LL |     let titi = 42;
++   |         ^^^^
++
++error: use of a blacklisted/placeholder name `toto`
++  --> $DIR/conf_french_blacklisted_name.rs:17:10
++   |
++LL |         (toto, Some(tata), titi @ Some(_)) => (),
++   |          ^^^^
++
++error: use of a blacklisted/placeholder name `tata`
++  --> $DIR/conf_french_blacklisted_name.rs:17:21
++   |
++LL |         (toto, Some(tata), titi @ Some(_)) => (),
++   |                     ^^^^
++
++error: use of a blacklisted/placeholder name `titi`
++  --> $DIR/conf_french_blacklisted_name.rs:17:28
++   |
++LL |         (toto, Some(tata), titi @ Some(_)) => (),
++   |                            ^^^^
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3b96f1fd000bb0323c3ab37ae533ffd5637cc78f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++trivial-copy-size-limit = 2
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..19019a2541631767abf56555c0411d03465af1af
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)"
++// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)"
++
++#![deny(clippy::trivially_copy_pass_by_ref)]
++#![allow(clippy::many_single_char_names)]
++
++#[derive(Copy, Clone)]
++struct Foo(u8);
++
++#[derive(Copy, Clone)]
++struct Bar(u32);
++
++fn good(a: &mut u32, b: u32, c: &Bar, d: &u32) {}
++
++fn bad(x: &u16, y: &Foo) {}
++
++fn main() {
++    let (mut a, b, c, d, x, y) = (0, 0, Bar(0), 0, 0, Foo(0));
++    good(&mut a, b, &c, &d);
++    bad(&x, &y);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..912761a8f009c2dfd2e856d7e62d12ad5113e94c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/test.rs:15:11
++   |
++LL | fn bad(x: &u16, y: &Foo) {}
++   |           ^^^^ help: consider passing by value instead: `u16`
++   |
++note: the lint level is defined here
++  --> $DIR/test.rs:4:9
++   |
++LL | #![deny(clippy::trivially_copy_pass_by_ref)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/test.rs:15:20
++   |
++LL | fn bad(x: &u16, y: &Foo) {}
++   |                    ^^^^ help: consider passing by value instead: `Foo`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..554b87cc50be176bd8cb1920fbc26b11d0e557a0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++# that one is an error
++foobar = 42
++
++# that one is white-listed
++[third-party]
++clippy-feature = "nightly"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a47569f62a3229590feb453f4a543ed7ab98317d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++// error-pattern: error reading Clippy's configuration file: unknown key `foobar`
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..18f5d994ba8aaf1df45a039a5275c14c6264f49f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-struct-bools`, `max-fn-params-bools`, `third-party` at line 5 column 1
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7028b251ea0307bb669524afb702a8d62a038924
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++#!/bin/bash
++#
++# A script to update the references for all tests. The idea is that
++# you do a run, which will generate files in the build directory
++# containing the (normalized) actual output of the compiler. You then
++# run this script, which will copy those files over. If you find
++# yourself manually editing a foo.stderr file, you're doing it wrong.
++#
++# See all `update-references.sh`, if you just want to update a single test.
++
++if [[ "$1" == "--help" || "$1" == "-h" ]]; then
++    echo "usage: $0"
++fi
++
++BUILD_DIR=$PWD/target/debug/test_build_base
++MY_DIR=$(dirname "$0")
++cd "$MY_DIR" || exit
++find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..50d42678734e9caca32b8d6c806ed576f4b04a8e
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++#!/bin/bash
++
++# A script to update the references for particular tests. The idea is
++# that you do a run, which will generate files in the build directory
++# containing the (normalized) actual output of the compiler. This
++# script will then copy that output and replace the "expected output"
++# files. You can then commit the changes.
++#
++# If you find yourself manually editing a foo.stderr file, you're
++# doing it wrong.
++
++if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then
++    echo "usage: $0 <build-directory> <relative-path-to-rs-files>"
++    echo ""
++    echo "For example:"
++    echo "   $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs"
++fi
++
++MYDIR=$(dirname "$0")
++
++BUILD_DIR="$1"
++shift
++
++while [[ "$1" != "" ]]; do
++    STDERR_NAME="${1/%.rs/.stderr}"
++    STDOUT_NAME="${1/%.rs/.stdout}"
++    shift
++    if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \
++           ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then
++        echo updating "$MYDIR"/"$STDOUT_NAME"
++        cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"
++    fi
++    if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \
++           ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then
++        echo updating "$MYDIR"/"$STDERR_NAME"
++        cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"
++    fi
++done
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..039ea47fc32d888f3a6233ab476d11eba84566e2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++vec-box-size-threshold = 4
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bf04bee16373c3c9e22f09bc896632540e6705f5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++struct S {
++    x: u64,
++}
++
++struct C {
++    y: u16,
++}
++
++struct Foo(Vec<Box<u8>>);
++struct Bar(Vec<Box<u32>>);
++struct Baz(Vec<Box<(u32, u32)>>);
++struct BarBaz(Vec<Box<S>>);
++struct FooBarBaz(Vec<Box<C>>);
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3bdeca0bc8774a77fdd77f42f0ccdfd23fc03bc5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: `Vec<T>` is already on the heap, the boxing is unnecessary.
++  --> $DIR/test.rs:9:12
++   |
++LL | struct Foo(Vec<Box<u8>>);
++   |            ^^^^^^^^^^^^ help: try: `Vec<u8>`
++   |
++   = note: `-D clippy::vec-box` implied by `-D warnings`
++
++error: `Vec<T>` is already on the heap, the boxing is unnecessary.
++  --> $DIR/test.rs:10:12
++   |
++LL | struct Bar(Vec<Box<u32>>);
++   |            ^^^^^^^^^^^^^ help: try: `Vec<u32>`
++
++error: `Vec<T>` is already on the heap, the boxing is unnecessary.
++  --> $DIR/test.rs:13:18
++   |
++LL | struct FooBarBaz(Vec<Box<C>>);
++   |                  ^^^^^^^^^^^ help: try: `Vec<C>`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..42a1067b95edd2a3d4590c72c12bfb4a0dbc23b1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++single-char-binding-names-threshold = 0
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..22aaa242b9b9d94641798dac2ccaf349b1d9d8ca
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++#![warn(clippy::many_single_char_names)]
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d205b383d1ff64c745e1dd5b2842c22a1f66b570
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++#![warn(clippy::absurd_extreme_comparisons)]
++#![allow(
++    unused,
++    clippy::eq_op,
++    clippy::no_effect,
++    clippy::unnecessary_operation,
++    clippy::needless_pass_by_value
++)]
++
++#[rustfmt::skip]
++fn main() {
++    const Z: u32 = 0;
++    let u: u32 = 42;
++    u <= 0;
++    u <= Z;
++    u < Z;
++    Z >= u;
++    Z > u;
++    u > u32::MAX;
++    u >= u32::MAX;
++    u32::MAX < u;
++    u32::MAX <= u;
++    1-1 > u;
++    u >= !0;
++    u <= 12 - 2*6;
++    let i: i8 = 0;
++    i < -127 - 1;
++    i8::MAX >= i;
++    3-7 < i32::MIN;
++    let b = false;
++    b >= true;
++    false > b;
++    u > 0; // ok
++    // this is handled by clippy::unit_cmp
++    () < {};
++}
++
++use std::cmp::{Ordering, PartialEq, PartialOrd};
++
++#[derive(PartialEq, PartialOrd)]
++pub struct U(u64);
++
++impl PartialEq<u32> for U {
++    fn eq(&self, other: &u32) -> bool {
++        self.eq(&U(u64::from(*other)))
++    }
++}
++impl PartialOrd<u32> for U {
++    fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
++        self.partial_cmp(&U(u64::from(*other)))
++    }
++}
++
++pub fn foo(val: U) -> bool {
++    val > u32::MAX
++}
++
++pub fn bar(len: u64) -> bool {
++    // This is OK as we are casting from target sized to fixed size
++    len >= usize::MAX as u64
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6de554378aaa97f1d0537bb1b7703ae59b9f46fc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,147 @@@
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++  --> $DIR/absurd-extreme-comparisons.rs:14:5
++   |
++LL |     u <= 0;
++   |     ^^^^^^
++   |
++   = note: `-D clippy::absurd-extreme-comparisons` implied by `-D warnings`
++   = help: because `0` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `u == 0` instead
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++  --> $DIR/absurd-extreme-comparisons.rs:15:5
++   |
++LL |     u <= Z;
++   |     ^^^^^^
++   |
++   = help: because `Z` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `u == Z` instead
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++  --> $DIR/absurd-extreme-comparisons.rs:16:5
++   |
++LL |     u < Z;
++   |     ^^^^^
++   |
++   = help: because `Z` is the minimum value for this type, this comparison is always false
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++  --> $DIR/absurd-extreme-comparisons.rs:17:5
++   |
++LL |     Z >= u;
++   |     ^^^^^^
++   |
++   = help: because `Z` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `Z == u` instead
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++  --> $DIR/absurd-extreme-comparisons.rs:18:5
++   |
++LL |     Z > u;
++   |     ^^^^^
++   |
++   = help: because `Z` is the minimum value for this type, this comparison is always false
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++  --> $DIR/absurd-extreme-comparisons.rs:19:5
++   |
++LL |     u > u32::MAX;
++   |     ^^^^^^^^^^^^
++   |
++   = help: because `u32::MAX` is the maximum value for this type, this comparison is always false
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++  --> $DIR/absurd-extreme-comparisons.rs:20:5
++   |
++LL |     u >= u32::MAX;
++   |     ^^^^^^^^^^^^^
++   |
++   = help: because `u32::MAX` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u == u32::MAX` instead
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++  --> $DIR/absurd-extreme-comparisons.rs:21:5
++   |
++LL |     u32::MAX < u;
++   |     ^^^^^^^^^^^^
++   |
++   = help: because `u32::MAX` is the maximum value for this type, this comparison is always false
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++  --> $DIR/absurd-extreme-comparisons.rs:22:5
++   |
++LL |     u32::MAX <= u;
++   |     ^^^^^^^^^^^^^
++   |
++   = help: because `u32::MAX` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u32::MAX == u` instead
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++  --> $DIR/absurd-extreme-comparisons.rs:23:5
++   |
++LL |     1-1 > u;
++   |     ^^^^^^^
++   |
++   = help: because `1-1` is the minimum value for this type, this comparison is always false
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++  --> $DIR/absurd-extreme-comparisons.rs:24:5
++   |
++LL |     u >= !0;
++   |     ^^^^^^^
++   |
++   = help: because `!0` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u == !0` instead
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++  --> $DIR/absurd-extreme-comparisons.rs:25:5
++   |
++LL |     u <= 12 - 2*6;
++   |     ^^^^^^^^^^^^^
++   |
++   = help: because `12 - 2*6` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `u == 12 - 2*6` instead
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++  --> $DIR/absurd-extreme-comparisons.rs:27:5
++   |
++LL |     i < -127 - 1;
++   |     ^^^^^^^^^^^^
++   |
++   = help: because `-127 - 1` is the minimum value for this type, this comparison is always false
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++  --> $DIR/absurd-extreme-comparisons.rs:28:5
++   |
++LL |     i8::MAX >= i;
++   |     ^^^^^^^^^^^^
++   |
++   = help: because `i8::MAX` is the maximum value for this type, this comparison is always true
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++  --> $DIR/absurd-extreme-comparisons.rs:29:5
++   |
++LL |     3-7 < i32::MIN;
++   |     ^^^^^^^^^^^^^^
++   |
++   = help: because `i32::MIN` is the minimum value for this type, this comparison is always false
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++  --> $DIR/absurd-extreme-comparisons.rs:31:5
++   |
++LL |     b >= true;
++   |     ^^^^^^^^^
++   |
++   = help: because `true` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `b == true` instead
++
++error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
++  --> $DIR/absurd-extreme-comparisons.rs:32:5
++   |
++LL |     false > b;
++   |     ^^^^^^^^^
++   |
++   = help: because `false` is the minimum value for this type, this comparison is always false
++
++error: <-comparison of unit values detected. This will always be false
++  --> $DIR/absurd-extreme-comparisons.rs:35:5
++   |
++LL |     () < {};
++   |     ^^^^^^^
++   |
++   = note: `#[deny(clippy::unit_cmp)]` on by default
++
++error: aborting due to 18 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fb57a0becbb25e81b751bef0e1be0ddb0b559253
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,60 @@@
++#[warn(clippy::approx_constant)]
++#[allow(unused, clippy::shadow_unrelated, clippy::similar_names)]
++fn main() {
++    let my_e = 2.7182;
++    let almost_e = 2.718;
++    let no_e = 2.71;
++
++    let my_1_frac_pi = 0.3183;
++    let no_1_frac_pi = 0.31;
++
++    let my_frac_1_sqrt_2 = 0.70710678;
++    let almost_frac_1_sqrt_2 = 0.70711;
++    let my_frac_1_sqrt_2 = 0.707;
++
++    let my_frac_2_pi = 0.63661977;
++    let no_frac_2_pi = 0.636;
++
++    let my_frac_2_sq_pi = 1.128379;
++    let no_frac_2_sq_pi = 1.128;
++
++    let my_frac_pi_2 = 1.57079632679;
++    let no_frac_pi_2 = 1.5705;
++
++    let my_frac_pi_3 = 1.04719755119;
++    let no_frac_pi_3 = 1.047;
++
++    let my_frac_pi_4 = 0.785398163397;
++    let no_frac_pi_4 = 0.785;
++
++    let my_frac_pi_6 = 0.523598775598;
++    let no_frac_pi_6 = 0.523;
++
++    let my_frac_pi_8 = 0.3926990816987;
++    let no_frac_pi_8 = 0.392;
++
++    let my_ln_10 = 2.302585092994046;
++    let no_ln_10 = 2.303;
++
++    let my_ln_2 = 0.6931471805599453;
++    let no_ln_2 = 0.693;
++
++    let my_log10_e = 0.4342944819032518;
++    let no_log10_e = 0.434;
++
++    let my_log2_e = 1.4426950408889634;
++    let no_log2_e = 1.442;
++
++    let log2_10 = 3.321928094887362;
++    let no_log2_10 = 3.321;
++
++    let log10_2 = 0.301029995663981;
++    let no_log10_2 = 0.301;
++
++    let my_pi = 3.1415;
++    let almost_pi = 3.14;
++    let no_pi = 3.15;
++
++    let my_sq2 = 1.4142;
++    let no_sq2 = 1.414;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..98b85443f0b70dcab0ab82e4a8b2e087cc1acb35
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,130 @@@
++error: approximate value of `f{32, 64}::consts::E` found. Consider using it directly
++  --> $DIR/approx_const.rs:4:16
++   |
++LL |     let my_e = 2.7182;
++   |                ^^^^^^
++   |
++   = note: `-D clippy::approx-constant` implied by `-D warnings`
++
++error: approximate value of `f{32, 64}::consts::E` found. Consider using it directly
++  --> $DIR/approx_const.rs:5:20
++   |
++LL |     let almost_e = 2.718;
++   |                    ^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_1_PI` found. Consider using it directly
++  --> $DIR/approx_const.rs:8:24
++   |
++LL |     let my_1_frac_pi = 0.3183;
++   |                        ^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found. Consider using it directly
++  --> $DIR/approx_const.rs:11:28
++   |
++LL |     let my_frac_1_sqrt_2 = 0.70710678;
++   |                            ^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found. Consider using it directly
++  --> $DIR/approx_const.rs:12:32
++   |
++LL |     let almost_frac_1_sqrt_2 = 0.70711;
++   |                                ^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_2_PI` found. Consider using it directly
++  --> $DIR/approx_const.rs:15:24
++   |
++LL |     let my_frac_2_pi = 0.63661977;
++   |                        ^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_2_SQRT_PI` found. Consider using it directly
++  --> $DIR/approx_const.rs:18:27
++   |
++LL |     let my_frac_2_sq_pi = 1.128379;
++   |                           ^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_PI_2` found. Consider using it directly
++  --> $DIR/approx_const.rs:21:24
++   |
++LL |     let my_frac_pi_2 = 1.57079632679;
++   |                        ^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_PI_3` found. Consider using it directly
++  --> $DIR/approx_const.rs:24:24
++   |
++LL |     let my_frac_pi_3 = 1.04719755119;
++   |                        ^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_PI_4` found. Consider using it directly
++  --> $DIR/approx_const.rs:27:24
++   |
++LL |     let my_frac_pi_4 = 0.785398163397;
++   |                        ^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_PI_6` found. Consider using it directly
++  --> $DIR/approx_const.rs:30:24
++   |
++LL |     let my_frac_pi_6 = 0.523598775598;
++   |                        ^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::FRAC_PI_8` found. Consider using it directly
++  --> $DIR/approx_const.rs:33:24
++   |
++LL |     let my_frac_pi_8 = 0.3926990816987;
++   |                        ^^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::LN_10` found. Consider using it directly
++  --> $DIR/approx_const.rs:36:20
++   |
++LL |     let my_ln_10 = 2.302585092994046;
++   |                    ^^^^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::LN_2` found. Consider using it directly
++  --> $DIR/approx_const.rs:39:19
++   |
++LL |     let my_ln_2 = 0.6931471805599453;
++   |                   ^^^^^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::LOG10_E` found. Consider using it directly
++  --> $DIR/approx_const.rs:42:22
++   |
++LL |     let my_log10_e = 0.4342944819032518;
++   |                      ^^^^^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::LOG2_E` found. Consider using it directly
++  --> $DIR/approx_const.rs:45:21
++   |
++LL |     let my_log2_e = 1.4426950408889634;
++   |                     ^^^^^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::LOG2_10` found. Consider using it directly
++  --> $DIR/approx_const.rs:48:19
++   |
++LL |     let log2_10 = 3.321928094887362;
++   |                   ^^^^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::LOG10_2` found. Consider using it directly
++  --> $DIR/approx_const.rs:51:19
++   |
++LL |     let log10_2 = 0.301029995663981;
++   |                   ^^^^^^^^^^^^^^^^^
++
++error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
++  --> $DIR/approx_const.rs:54:17
++   |
++LL |     let my_pi = 3.1415;
++   |                 ^^^^^^
++
++error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
++  --> $DIR/approx_const.rs:55:21
++   |
++LL |     let almost_pi = 3.14;
++   |                     ^^^^
++
++error: approximate value of `f{32, 64}::consts::SQRT_2` found. Consider using it directly
++  --> $DIR/approx_const.rs:58:18
++   |
++LL |     let my_sq2 = 1.4142;
++   |                  ^^^^^^
++
++error: aborting due to 21 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e01ba0c64df325535f88bb0909640657002f4047
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++#[warn(clippy::as_conversions)]
++
++fn main() {
++    let i = 0u32 as u64;
++
++    let j = &i as *const u64 as *mut u64;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..312d3a7460ebe4a34a3753bcf01e9e17dee2693c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++error: using a potentially dangerous silent `as` conversion
++  --> $DIR/as_conversions.rs:4:13
++   |
++LL |     let i = 0u32 as u64;
++   |             ^^^^^^^^^^^
++   |
++   = note: `-D clippy::as-conversions` implied by `-D warnings`
++   = help: consider using a safe wrapper for this conversion
++
++error: using a potentially dangerous silent `as` conversion
++  --> $DIR/as_conversions.rs:6:13
++   |
++LL |     let j = &i as *const u64 as *mut u64;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using a safe wrapper for this conversion
++
++error: using a potentially dangerous silent `as` conversion
++  --> $DIR/as_conversions.rs:6:13
++   |
++LL |     let j = &i as *const u64 as *mut u64;
++   |             ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using a safe wrapper for this conversion
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..60d721c2f20495367b9a8b530845dc93e89c1cbb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++macro_rules! assert_const {
++    ($len:expr) => {
++        assert!($len > 0);
++        debug_assert!($len < 0);
++    };
++}
++
++fn main() {
++    assert!(true);
++    assert!(false);
++    assert!(true, "true message");
++    assert!(false, "false message");
++
++    let msg = "panic message";
++    assert!(false, msg.to_uppercase());
++
++    const B: bool = true;
++    assert!(B);
++
++    const C: bool = false;
++    assert!(C);
++    assert!(C, "C message");
++
++    debug_assert!(true);
++    // Don't lint this, since there is no better way for expressing "Only panic in debug mode".
++    debug_assert!(false); // #3948
++    assert_const!(3);
++    assert_const!(-1);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8f09c8ce9d52aec82b1fb2193ccb6df05dc480ba
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,84 @@@
++error: `assert!(true)` will be optimized out by the compiler
++  --> $DIR/assertions_on_constants.rs:9:5
++   |
++LL |     assert!(true);
++   |     ^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::assertions-on-constants` implied by `-D warnings`
++   = help: remove it
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `assert!(false)` should probably be replaced
++  --> $DIR/assertions_on_constants.rs:10:5
++   |
++LL |     assert!(false);
++   |     ^^^^^^^^^^^^^^^
++   |
++   = help: use `panic!()` or `unreachable!()`
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `assert!(true)` will be optimized out by the compiler
++  --> $DIR/assertions_on_constants.rs:11:5
++   |
++LL |     assert!(true, "true message");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: remove it
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `assert!(false, "false message")` should probably be replaced
++  --> $DIR/assertions_on_constants.rs:12:5
++   |
++LL |     assert!(false, "false message");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: use `panic!("false message")` or `unreachable!("false message")`
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `assert!(false, msg.to_uppercase())` should probably be replaced
++  --> $DIR/assertions_on_constants.rs:15:5
++   |
++LL |     assert!(false, msg.to_uppercase());
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: use `panic!(msg.to_uppercase())` or `unreachable!(msg.to_uppercase())`
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `assert!(true)` will be optimized out by the compiler
++  --> $DIR/assertions_on_constants.rs:18:5
++   |
++LL |     assert!(B);
++   |     ^^^^^^^^^^^
++   |
++   = help: remove it
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `assert!(false)` should probably be replaced
++  --> $DIR/assertions_on_constants.rs:21:5
++   |
++LL |     assert!(C);
++   |     ^^^^^^^^^^^
++   |
++   = help: use `panic!()` or `unreachable!()`
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `assert!(false, "C message")` should probably be replaced
++  --> $DIR/assertions_on_constants.rs:22:5
++   |
++LL |     assert!(C, "C message");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: use `panic!("C message")` or `unreachable!("C message")`
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `debug_assert!(true)` will be optimized out by the compiler
++  --> $DIR/assertions_on_constants.rs:24:5
++   |
++LL |     debug_assert!(true);
++   |     ^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: remove it
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..52b1b3afe16fe2ca265cbd0c7b420c9bc8a1e391
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++// run-rustfix
++
++#[allow(dead_code, unused_assignments)]
++#[warn(clippy::assign_op_pattern)]
++fn main() {
++    let mut a = 5;
++    a += 1;
++    a += 1;
++    a -= 1;
++    a *= 99;
++    a *= 42;
++    a /= 2;
++    a %= 5;
++    a &= 1;
++    a = 1 - a;
++    a = 5 / a;
++    a = 42 % a;
++    a = 6 << a;
++    let mut s = String::new();
++    s += "bla";
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..527a46b2c2bb90f4e0485bc925bdd5e55f4c6d14
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++// run-rustfix
++
++#[allow(dead_code, unused_assignments)]
++#[warn(clippy::assign_op_pattern)]
++fn main() {
++    let mut a = 5;
++    a = a + 1;
++    a = 1 + a;
++    a = a - 1;
++    a = a * 99;
++    a = 42 * a;
++    a = a / 2;
++    a = a % 5;
++    a = a & 1;
++    a = 1 - a;
++    a = 5 / a;
++    a = 42 % a;
++    a = 6 << a;
++    let mut s = String::new();
++    s = s + "bla";
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3486bd8da4d07ed98b2ec55cf49949508d3c9603
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++error: manual implementation of an assign operation
++  --> $DIR/assign_ops.rs:7:5
++   |
++LL |     a = a + 1;
++   |     ^^^^^^^^^ help: replace it with: `a += 1`
++   |
++   = note: `-D clippy::assign-op-pattern` implied by `-D warnings`
++
++error: manual implementation of an assign operation
++  --> $DIR/assign_ops.rs:8:5
++   |
++LL |     a = 1 + a;
++   |     ^^^^^^^^^ help: replace it with: `a += 1`
++
++error: manual implementation of an assign operation
++  --> $DIR/assign_ops.rs:9:5
++   |
++LL |     a = a - 1;
++   |     ^^^^^^^^^ help: replace it with: `a -= 1`
++
++error: manual implementation of an assign operation
++  --> $DIR/assign_ops.rs:10:5
++   |
++LL |     a = a * 99;
++   |     ^^^^^^^^^^ help: replace it with: `a *= 99`
++
++error: manual implementation of an assign operation
++  --> $DIR/assign_ops.rs:11:5
++   |
++LL |     a = 42 * a;
++   |     ^^^^^^^^^^ help: replace it with: `a *= 42`
++
++error: manual implementation of an assign operation
++  --> $DIR/assign_ops.rs:12:5
++   |
++LL |     a = a / 2;
++   |     ^^^^^^^^^ help: replace it with: `a /= 2`
++
++error: manual implementation of an assign operation
++  --> $DIR/assign_ops.rs:13:5
++   |
++LL |     a = a % 5;
++   |     ^^^^^^^^^ help: replace it with: `a %= 5`
++
++error: manual implementation of an assign operation
++  --> $DIR/assign_ops.rs:14:5
++   |
++LL |     a = a & 1;
++   |     ^^^^^^^^^ help: replace it with: `a &= 1`
++
++error: manual implementation of an assign operation
++  --> $DIR/assign_ops.rs:20:5
++   |
++LL |     s = s + "bla";
++   |     ^^^^^^^^^^^^^ help: replace it with: `s += "bla"`
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4703a8c77778857405266a60d5f20977e1bc59ef
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++#[allow(unused_assignments)]
++#[warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)]
++fn main() {
++    let mut a = 5;
++    a += a + 1;
++    a += 1 + a;
++    a -= a - 1;
++    a *= a * 99;
++    a *= 42 * a;
++    a /= a / 2;
++    a %= a % 5;
++    a &= a & 1;
++    a *= a * a;
++    a = a * a * a;
++    a = a * 42 * a;
++    a = a * 2 + a;
++    a -= 1 - a;
++    a /= 5 / a;
++    a %= 42 % a;
++    a <<= 6 << a;
++}
++
++// check that we don't lint on op assign impls, because that's just the way to impl them
++
++use std::ops::{Mul, MulAssign};
++
++#[derive(Copy, Clone, Debug, PartialEq)]
++pub struct Wrap(i64);
++
++impl Mul<i64> for Wrap {
++    type Output = Self;
++
++    fn mul(self, rhs: i64) -> Self {
++        Wrap(self.0 * rhs)
++    }
++}
++
++impl MulAssign<i64> for Wrap {
++    fn mul_assign(&mut self, rhs: i64) {
++        *self = *self * rhs
++    }
++}
++
++fn cow_add_assign() {
++    use std::borrow::Cow;
++    let mut buf = Cow::Owned(String::from("bar"));
++    let cows = Cow::Borrowed("foo");
++
++    // this can be linted
++    buf = buf + cows.clone();
++
++    // this should not as cow<str> Add is not commutative
++    buf = cows + buf;
++    println!("{}", buf);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..70b15d18a568b878ea39cb6c2efe9869b1285828
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,146 @@@
++error: variable appears on both sides of an assignment operation
++  --> $DIR/assign_ops2.rs:5:5
++   |
++LL |     a += a + 1;
++   |     ^^^^^^^^^^
++   |
++   = note: `-D clippy::misrefactored-assign-op` implied by `-D warnings`
++help: Did you mean `a = a + 1` or `a = a + a + 1`? Consider replacing it with
++   |
++LL |     a += 1;
++   |     ^^^^^^
++help: or
++   |
++LL |     a = a + a + 1;
++   |     ^^^^^^^^^^^^^
++
++error: variable appears on both sides of an assignment operation
++  --> $DIR/assign_ops2.rs:6:5
++   |
++LL |     a += 1 + a;
++   |     ^^^^^^^^^^
++   |
++help: Did you mean `a = a + 1` or `a = a + 1 + a`? Consider replacing it with
++   |
++LL |     a += 1;
++   |     ^^^^^^
++help: or
++   |
++LL |     a = a + 1 + a;
++   |     ^^^^^^^^^^^^^
++
++error: variable appears on both sides of an assignment operation
++  --> $DIR/assign_ops2.rs:7:5
++   |
++LL |     a -= a - 1;
++   |     ^^^^^^^^^^
++   |
++help: Did you mean `a = a - 1` or `a = a - (a - 1)`? Consider replacing it with
++   |
++LL |     a -= 1;
++   |     ^^^^^^
++help: or
++   |
++LL |     a = a - (a - 1);
++   |     ^^^^^^^^^^^^^^^
++
++error: variable appears on both sides of an assignment operation
++  --> $DIR/assign_ops2.rs:8:5
++   |
++LL |     a *= a * 99;
++   |     ^^^^^^^^^^^
++   |
++help: Did you mean `a = a * 99` or `a = a * a * 99`? Consider replacing it with
++   |
++LL |     a *= 99;
++   |     ^^^^^^^
++help: or
++   |
++LL |     a = a * a * 99;
++   |     ^^^^^^^^^^^^^^
++
++error: variable appears on both sides of an assignment operation
++  --> $DIR/assign_ops2.rs:9:5
++   |
++LL |     a *= 42 * a;
++   |     ^^^^^^^^^^^
++   |
++help: Did you mean `a = a * 42` or `a = a * 42 * a`? Consider replacing it with
++   |
++LL |     a *= 42;
++   |     ^^^^^^^
++help: or
++   |
++LL |     a = a * 42 * a;
++   |     ^^^^^^^^^^^^^^
++
++error: variable appears on both sides of an assignment operation
++  --> $DIR/assign_ops2.rs:10:5
++   |
++LL |     a /= a / 2;
++   |     ^^^^^^^^^^
++   |
++help: Did you mean `a = a / 2` or `a = a / (a / 2)`? Consider replacing it with
++   |
++LL |     a /= 2;
++   |     ^^^^^^
++help: or
++   |
++LL |     a = a / (a / 2);
++   |     ^^^^^^^^^^^^^^^
++
++error: variable appears on both sides of an assignment operation
++  --> $DIR/assign_ops2.rs:11:5
++   |
++LL |     a %= a % 5;
++   |     ^^^^^^^^^^
++   |
++help: Did you mean `a = a % 5` or `a = a % (a % 5)`? Consider replacing it with
++   |
++LL |     a %= 5;
++   |     ^^^^^^
++help: or
++   |
++LL |     a = a % (a % 5);
++   |     ^^^^^^^^^^^^^^^
++
++error: variable appears on both sides of an assignment operation
++  --> $DIR/assign_ops2.rs:12:5
++   |
++LL |     a &= a & 1;
++   |     ^^^^^^^^^^
++   |
++help: Did you mean `a = a & 1` or `a = a & a & 1`? Consider replacing it with
++   |
++LL |     a &= 1;
++   |     ^^^^^^
++help: or
++   |
++LL |     a = a & a & 1;
++   |     ^^^^^^^^^^^^^
++
++error: variable appears on both sides of an assignment operation
++  --> $DIR/assign_ops2.rs:13:5
++   |
++LL |     a *= a * a;
++   |     ^^^^^^^^^^
++   |
++help: Did you mean `a = a * a` or `a = a * a * a`? Consider replacing it with
++   |
++LL |     a *= a;
++   |     ^^^^^^
++help: or
++   |
++LL |     a = a * a * a;
++   |     ^^^^^^^^^^^^^
++
++error: manual implementation of an assign operation
++  --> $DIR/assign_ops2.rs:50:5
++   |
++LL |     buf = buf + cows.clone();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()`
++   |
++   = note: `-D clippy::assign-op-pattern` implied by `-D warnings`
++
++error: aborting due to 10 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cdbde79b19ebf53f8db58975f6258a9bd13e6abc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++#![warn(clippy::invalid_atomic_ordering)]
++
++use std::sync::atomic::{AtomicBool, Ordering};
++
++fn main() {
++    let x = AtomicBool::new(true);
++
++    // Allowed load ordering modes
++    let _ = x.load(Ordering::Acquire);
++    let _ = x.load(Ordering::SeqCst);
++    let _ = x.load(Ordering::Relaxed);
++
++    // Disallowed load ordering modes
++    let _ = x.load(Ordering::Release);
++    let _ = x.load(Ordering::AcqRel);
++
++    // Allowed store ordering modes
++    x.store(false, Ordering::Release);
++    x.store(false, Ordering::SeqCst);
++    x.store(false, Ordering::Relaxed);
++
++    // Disallowed store ordering modes
++    x.store(false, Ordering::Acquire);
++    x.store(false, Ordering::AcqRel);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..397b893aed96478901847e26a6b0ae032b57ca51
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_bool.rs:14:20
++   |
++LL |     let _ = x.load(Ordering::Release);
++   |                    ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings`
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_bool.rs:15:20
++   |
++LL |     let _ = x.load(Ordering::AcqRel);
++   |                    ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_bool.rs:23:20
++   |
++LL |     x.store(false, Ordering::Acquire);
++   |                    ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_bool.rs:24:20
++   |
++LL |     x.store(false, Ordering::AcqRel);
++   |                    ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5ee5182ca051ade720278e764c4d9b5edb3d3d7a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++#![warn(clippy::invalid_atomic_ordering)]
++
++use std::sync::atomic::{compiler_fence, fence, Ordering};
++
++fn main() {
++    // Allowed fence ordering modes
++    fence(Ordering::Acquire);
++    fence(Ordering::Release);
++    fence(Ordering::AcqRel);
++    fence(Ordering::SeqCst);
++
++    // Disallowed fence ordering modes
++    fence(Ordering::Relaxed);
++
++    compiler_fence(Ordering::Acquire);
++    compiler_fence(Ordering::Release);
++    compiler_fence(Ordering::AcqRel);
++    compiler_fence(Ordering::SeqCst);
++    compiler_fence(Ordering::Relaxed);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3ceff27d9ad5e7164e95f565a9e906140e2b5550
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++error: memory fences cannot have `Relaxed` ordering
++  --> $DIR/atomic_ordering_fence.rs:13:11
++   |
++LL |     fence(Ordering::Relaxed);
++   |           ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings`
++   = help: consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`
++
++error: memory fences cannot have `Relaxed` ordering
++  --> $DIR/atomic_ordering_fence.rs:19:20
++   |
++LL |     compiler_fence(Ordering::Relaxed);
++   |                    ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..40a00ba3de3505435770d984a270a36bd98d88ae
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,86 @@@
++#![warn(clippy::invalid_atomic_ordering)]
++
++use std::sync::atomic::{AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, Ordering};
++
++fn main() {
++    // `AtomicI8` test cases
++    let x = AtomicI8::new(0);
++
++    // Allowed load ordering modes
++    let _ = x.load(Ordering::Acquire);
++    let _ = x.load(Ordering::SeqCst);
++    let _ = x.load(Ordering::Relaxed);
++
++    // Disallowed load ordering modes
++    let _ = x.load(Ordering::Release);
++    let _ = x.load(Ordering::AcqRel);
++
++    // Allowed store ordering modes
++    x.store(1, Ordering::Release);
++    x.store(1, Ordering::SeqCst);
++    x.store(1, Ordering::Relaxed);
++
++    // Disallowed store ordering modes
++    x.store(1, Ordering::Acquire);
++    x.store(1, Ordering::AcqRel);
++
++    // `AtomicI16` test cases
++    let x = AtomicI16::new(0);
++
++    let _ = x.load(Ordering::Acquire);
++    let _ = x.load(Ordering::SeqCst);
++    let _ = x.load(Ordering::Relaxed);
++    let _ = x.load(Ordering::Release);
++    let _ = x.load(Ordering::AcqRel);
++
++    x.store(1, Ordering::Release);
++    x.store(1, Ordering::SeqCst);
++    x.store(1, Ordering::Relaxed);
++    x.store(1, Ordering::Acquire);
++    x.store(1, Ordering::AcqRel);
++
++    // `AtomicI32` test cases
++    let x = AtomicI32::new(0);
++
++    let _ = x.load(Ordering::Acquire);
++    let _ = x.load(Ordering::SeqCst);
++    let _ = x.load(Ordering::Relaxed);
++    let _ = x.load(Ordering::Release);
++    let _ = x.load(Ordering::AcqRel);
++
++    x.store(1, Ordering::Release);
++    x.store(1, Ordering::SeqCst);
++    x.store(1, Ordering::Relaxed);
++    x.store(1, Ordering::Acquire);
++    x.store(1, Ordering::AcqRel);
++
++    // `AtomicI64` test cases
++    let x = AtomicI64::new(0);
++
++    let _ = x.load(Ordering::Acquire);
++    let _ = x.load(Ordering::SeqCst);
++    let _ = x.load(Ordering::Relaxed);
++    let _ = x.load(Ordering::Release);
++    let _ = x.load(Ordering::AcqRel);
++
++    x.store(1, Ordering::Release);
++    x.store(1, Ordering::SeqCst);
++    x.store(1, Ordering::Relaxed);
++    x.store(1, Ordering::Acquire);
++    x.store(1, Ordering::AcqRel);
++
++    // `AtomicIsize` test cases
++    let x = AtomicIsize::new(0);
++
++    let _ = x.load(Ordering::Acquire);
++    let _ = x.load(Ordering::SeqCst);
++    let _ = x.load(Ordering::Relaxed);
++    let _ = x.load(Ordering::Release);
++    let _ = x.load(Ordering::AcqRel);
++
++    x.store(1, Ordering::Release);
++    x.store(1, Ordering::SeqCst);
++    x.store(1, Ordering::Relaxed);
++    x.store(1, Ordering::Acquire);
++    x.store(1, Ordering::AcqRel);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bbaf234d3c9f8d0ef3d5ba2a828d9bb05d2af2fb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,163 @@@
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:15:20
++   |
++LL |     let _ = x.load(Ordering::Release);
++   |                    ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings`
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:16:20
++   |
++LL |     let _ = x.load(Ordering::AcqRel);
++   |                    ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:24:16
++   |
++LL |     x.store(1, Ordering::Acquire);
++   |                ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:25:16
++   |
++LL |     x.store(1, Ordering::AcqRel);
++   |                ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:33:20
++   |
++LL |     let _ = x.load(Ordering::Release);
++   |                    ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:34:20
++   |
++LL |     let _ = x.load(Ordering::AcqRel);
++   |                    ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:39:16
++   |
++LL |     x.store(1, Ordering::Acquire);
++   |                ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:40:16
++   |
++LL |     x.store(1, Ordering::AcqRel);
++   |                ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:48:20
++   |
++LL |     let _ = x.load(Ordering::Release);
++   |                    ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:49:20
++   |
++LL |     let _ = x.load(Ordering::AcqRel);
++   |                    ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:54:16
++   |
++LL |     x.store(1, Ordering::Acquire);
++   |                ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:55:16
++   |
++LL |     x.store(1, Ordering::AcqRel);
++   |                ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:63:20
++   |
++LL |     let _ = x.load(Ordering::Release);
++   |                    ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:64:20
++   |
++LL |     let _ = x.load(Ordering::AcqRel);
++   |                    ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:69:16
++   |
++LL |     x.store(1, Ordering::Acquire);
++   |                ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:70:16
++   |
++LL |     x.store(1, Ordering::AcqRel);
++   |                ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:78:20
++   |
++LL |     let _ = x.load(Ordering::Release);
++   |                    ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:79:20
++   |
++LL |     let _ = x.load(Ordering::AcqRel);
++   |                    ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:84:16
++   |
++LL |     x.store(1, Ordering::Acquire);
++   |                ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_int.rs:85:16
++   |
++LL |     x.store(1, Ordering::AcqRel);
++   |                ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: aborting due to 20 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ecbb05c7fbc39508e993bc56b5822fbedf5f03d8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++#![warn(clippy::invalid_atomic_ordering)]
++
++use std::sync::atomic::{AtomicPtr, Ordering};
++
++fn main() {
++    let ptr = &mut 5;
++    let other_ptr = &mut 10;
++    let x = AtomicPtr::new(ptr);
++
++    // Allowed load ordering modes
++    let _ = x.load(Ordering::Acquire);
++    let _ = x.load(Ordering::SeqCst);
++    let _ = x.load(Ordering::Relaxed);
++
++    // Disallowed load ordering modes
++    let _ = x.load(Ordering::Release);
++    let _ = x.load(Ordering::AcqRel);
++
++    // Allowed store ordering modes
++    x.store(other_ptr, Ordering::Release);
++    x.store(other_ptr, Ordering::SeqCst);
++    x.store(other_ptr, Ordering::Relaxed);
++
++    // Disallowed store ordering modes
++    x.store(other_ptr, Ordering::Acquire);
++    x.store(other_ptr, Ordering::AcqRel);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..558ae55518d5a9e26e302a715d0976beeeb2593f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_ptr.rs:16:20
++   |
++LL |     let _ = x.load(Ordering::Release);
++   |                    ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings`
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_ptr.rs:17:20
++   |
++LL |     let _ = x.load(Ordering::AcqRel);
++   |                    ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_ptr.rs:25:24
++   |
++LL |     x.store(other_ptr, Ordering::Acquire);
++   |                        ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_ptr.rs:26:24
++   |
++LL |     x.store(other_ptr, Ordering::AcqRel);
++   |                        ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a0d5d7c401035d686cd18a90617d5d7c81c28a1e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,86 @@@
++#![warn(clippy::invalid_atomic_ordering)]
++
++use std::sync::atomic::{AtomicU16, AtomicU32, AtomicU64, AtomicU8, AtomicUsize, Ordering};
++
++fn main() {
++    // `AtomicU8` test cases
++    let x = AtomicU8::new(0);
++
++    // Allowed load ordering modes
++    let _ = x.load(Ordering::Acquire);
++    let _ = x.load(Ordering::SeqCst);
++    let _ = x.load(Ordering::Relaxed);
++
++    // Disallowed load ordering modes
++    let _ = x.load(Ordering::Release);
++    let _ = x.load(Ordering::AcqRel);
++
++    // Allowed store ordering modes
++    x.store(1, Ordering::Release);
++    x.store(1, Ordering::SeqCst);
++    x.store(1, Ordering::Relaxed);
++
++    // Disallowed store ordering modes
++    x.store(1, Ordering::Acquire);
++    x.store(1, Ordering::AcqRel);
++
++    // `AtomicU16` test cases
++    let x = AtomicU16::new(0);
++
++    let _ = x.load(Ordering::Acquire);
++    let _ = x.load(Ordering::SeqCst);
++    let _ = x.load(Ordering::Relaxed);
++    let _ = x.load(Ordering::Release);
++    let _ = x.load(Ordering::AcqRel);
++
++    x.store(1, Ordering::Release);
++    x.store(1, Ordering::SeqCst);
++    x.store(1, Ordering::Relaxed);
++    x.store(1, Ordering::Acquire);
++    x.store(1, Ordering::AcqRel);
++
++    // `AtomicU32` test cases
++    let x = AtomicU32::new(0);
++
++    let _ = x.load(Ordering::Acquire);
++    let _ = x.load(Ordering::SeqCst);
++    let _ = x.load(Ordering::Relaxed);
++    let _ = x.load(Ordering::Release);
++    let _ = x.load(Ordering::AcqRel);
++
++    x.store(1, Ordering::Release);
++    x.store(1, Ordering::SeqCst);
++    x.store(1, Ordering::Relaxed);
++    x.store(1, Ordering::Acquire);
++    x.store(1, Ordering::AcqRel);
++
++    // `AtomicU64` test cases
++    let x = AtomicU64::new(0);
++
++    let _ = x.load(Ordering::Acquire);
++    let _ = x.load(Ordering::SeqCst);
++    let _ = x.load(Ordering::Relaxed);
++    let _ = x.load(Ordering::Release);
++    let _ = x.load(Ordering::AcqRel);
++
++    x.store(1, Ordering::Release);
++    x.store(1, Ordering::SeqCst);
++    x.store(1, Ordering::Relaxed);
++    x.store(1, Ordering::Acquire);
++    x.store(1, Ordering::AcqRel);
++
++    // `AtomicUsize` test cases
++    let x = AtomicUsize::new(0);
++
++    let _ = x.load(Ordering::Acquire);
++    let _ = x.load(Ordering::SeqCst);
++    let _ = x.load(Ordering::Relaxed);
++    let _ = x.load(Ordering::Release);
++    let _ = x.load(Ordering::AcqRel);
++
++    x.store(1, Ordering::Release);
++    x.store(1, Ordering::SeqCst);
++    x.store(1, Ordering::Relaxed);
++    x.store(1, Ordering::Acquire);
++    x.store(1, Ordering::AcqRel);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5703135bcf1e2a4bb613eba2bfa984f7002ae9b0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,163 @@@
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:15:20
++   |
++LL |     let _ = x.load(Ordering::Release);
++   |                    ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings`
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:16:20
++   |
++LL |     let _ = x.load(Ordering::AcqRel);
++   |                    ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:24:16
++   |
++LL |     x.store(1, Ordering::Acquire);
++   |                ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:25:16
++   |
++LL |     x.store(1, Ordering::AcqRel);
++   |                ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:33:20
++   |
++LL |     let _ = x.load(Ordering::Release);
++   |                    ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:34:20
++   |
++LL |     let _ = x.load(Ordering::AcqRel);
++   |                    ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:39:16
++   |
++LL |     x.store(1, Ordering::Acquire);
++   |                ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:40:16
++   |
++LL |     x.store(1, Ordering::AcqRel);
++   |                ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:48:20
++   |
++LL |     let _ = x.load(Ordering::Release);
++   |                    ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:49:20
++   |
++LL |     let _ = x.load(Ordering::AcqRel);
++   |                    ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:54:16
++   |
++LL |     x.store(1, Ordering::Acquire);
++   |                ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:55:16
++   |
++LL |     x.store(1, Ordering::AcqRel);
++   |                ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:63:20
++   |
++LL |     let _ = x.load(Ordering::Release);
++   |                    ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:64:20
++   |
++LL |     let _ = x.load(Ordering::AcqRel);
++   |                    ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:69:16
++   |
++LL |     x.store(1, Ordering::Acquire);
++   |                ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:70:16
++   |
++LL |     x.store(1, Ordering::AcqRel);
++   |                ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:78:20
++   |
++LL |     let _ = x.load(Ordering::Release);
++   |                    ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic loads cannot have `Release` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:79:20
++   |
++LL |     let _ = x.load(Ordering::AcqRel);
++   |                    ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:84:16
++   |
++LL |     x.store(1, Ordering::Acquire);
++   |                ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: atomic stores cannot have `Acquire` and `AcqRel` ordering
++  --> $DIR/atomic_ordering_uint.rs:85:16
++   |
++LL |     x.store(1, Ordering::AcqRel);
++   |                ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed`
++
++error: aborting due to 20 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..91b65a43be77fa5fdb3aa2a1fe66f2227347a7cf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++#![warn(clippy::inline_always, clippy::deprecated_semver)]
++#![allow(clippy::assertions_on_constants)]
++#[inline(always)]
++fn test_attr_lint() {
++    assert!(true)
++}
++
++#[inline(always)]
++fn false_positive_expr() {
++    unreachable!()
++}
++
++#[inline(always)]
++fn false_positive_stmt() {
++    unreachable!();
++}
++
++#[inline(always)]
++fn empty_and_false_positive_stmt() {
++    unreachable!();
++}
++
++#[deprecated(since = "forever")]
++pub const SOME_CONST: u8 = 42;
++
++#[deprecated(since = "1")]
++pub const ANOTHER_CONST: u8 = 23;
++
++#[deprecated(since = "0.1.1")]
++pub const YET_ANOTHER_CONST: u8 = 0;
++
++fn main() {
++    test_attr_lint();
++    if false {
++        false_positive_expr()
++    }
++    if false {
++        false_positive_stmt()
++    }
++    if false {
++        empty_and_false_positive_stmt()
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..39ddf6f226d95482da927fb934b3973c79179892
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea
++  --> $DIR/attrs.rs:3:1
++   |
++LL | #[inline(always)]
++   | ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::inline-always` implied by `-D warnings`
++
++error: the since field must contain a semver-compliant version
++  --> $DIR/attrs.rs:23:14
++   |
++LL | #[deprecated(since = "forever")]
++   |              ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::deprecated-semver` implied by `-D warnings`
++
++error: the since field must contain a semver-compliant version
++  --> $DIR/attrs.rs:26:14
++   |
++LL | #[deprecated(since = "1")]
++   |              ^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0a1be35689670f3c024efdcf702479c474db547d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++fn main() {
++    #[clippy::author]
++    let x: char = 0x45 as char;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..211d11c7c1aa33ba9c8f4342c4ecad5d50fda8f7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++if_chain! {
++    if let StmtKind::Local(ref local) = stmt.kind;
++    if let Some(ref init) = local.init;
++    if let ExprKind::Cast(ref expr, ref cast_ty) = init.kind;
++    if let TyKind::Path(ref qp) = cast_ty.kind;
++    if match_qpath(qp, &["char"]);
++    if let ExprKind::Lit(ref lit) = expr.kind;
++    if let LitKind::Int(69, _) = lit.node;
++    if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
++    if name.as_str() == "x";
++    then {
++        // report your lint here
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a8068436b70bf021737a64925f55d8b367aa63e0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++#![feature(stmt_expr_attributes)]
++#![allow(redundant_semicolons, clippy::no_effect)]
++
++#[rustfmt::skip]
++fn main() {
++    #[clippy::author]
++    {
++        ;;;;
++    }
++}
++
++#[clippy::author]
++fn foo() {
++    let x = 42i32;
++    -x;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c8ef75e48fc220ecf79eecbbcd62323cc39a8a70
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++if_chain! {
++    if let ExprKind::Block(ref block) = expr.kind;
++    if let Some(trailing_expr) = &block.expr;
++    if block.stmts.len() == 0;
++    then {
++        // report your lint here
++    }
++}
++if_chain! {
++    then {
++        // report your lint here
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e99c3c41dc4e1db83e357bfeab36c3d3d5d65309
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++fn main() {
++    #[clippy::author]
++    let _ = ::std::cmp::min(3, 4);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4dccf666631a928a15e4beb142dde1eaedd36135
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++if_chain! {
++    if let StmtKind::Local(ref local) = stmt.kind;
++    if let Some(ref init) = local.init;
++    if let ExprKind::Call(ref func, ref args) = init.kind;
++    if let ExprKind::Path(ref path) = func.kind;
++    if match_qpath(path, &["{{root}}", "std", "cmp", "min"]);
++    if args.len() == 2;
++    if let ExprKind::Lit(ref lit) = args[0].kind;
++    if let LitKind::Int(3, _) = lit.node;
++    if let ExprKind::Lit(ref lit1) = args[1].kind;
++    if let LitKind::Int(4, _) = lit1.node;
++    if let PatKind::Wild = local.pat.kind;
++    then {
++        // report your lint here
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b3dec876535c5e5e374f39baa5d6d89ba9535fad
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++#![feature(stmt_expr_attributes)]
++
++fn main() {
++    #[clippy::author]
++    for y in 0..10 {
++        let z = y;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..81ede955347d73b57d16791eddd3f1e489e2497a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++if_chain! {
++    if let ExprKind::DropTemps(ref expr) = expr.kind;
++    if let ExprKind::Match(ref expr1, ref arms, MatchSource::ForLoopDesugar) = expr.kind;
++    if let ExprKind::Call(ref func, ref args) = expr1.kind;
++    if let ExprKind::Path(ref path) = func.kind;
++    if match_qpath(path, &["{{root}}", "std", "iter", "IntoIterator", "into_iter"]);
++    if args.len() == 1;
++    if let ExprKind::Struct(ref path1, ref fields, None) = args[0].kind;
++    if match_qpath(path1, &["{{root}}", "std", "ops", "Range"]);
++    if fields.len() == 2;
++    // unimplemented: field checks
++    if arms.len() == 1;
++    if let ExprKind::Loop(ref body, ref label, LoopSource::ForLoop) = arms[0].body.kind;
++    if let Some(trailing_expr) = &body.expr;
++    if body.stmts.len() == 4;
++    if let StmtKind::Local(ref local) = body.stmts[0].kind;
++    if let PatKind::Binding(BindingAnnotation::Mutable, _, name, None) = local.pat.kind;
++    if name.as_str() == "__next";
++    if let StmtKind::Expr(ref e, _) = body.stmts[1].kind
++    if let ExprKind::Match(ref expr2, ref arms1, MatchSource::ForLoopDesugar) = e.kind;
++    if let ExprKind::Call(ref func1, ref args1) = expr2.kind;
++    if let ExprKind::Path(ref path2) = func1.kind;
++    if match_qpath(path2, &["{{root}}", "std", "iter", "Iterator", "next"]);
++    if args1.len() == 1;
++    if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref inner) = args1[0].kind;
++    if let ExprKind::Path(ref path3) = inner.kind;
++    if match_qpath(path3, &["iter"]);
++    if arms1.len() == 2;
++    if let ExprKind::Assign(ref target, ref value, ref _span) = arms1[0].body.kind;
++    if let ExprKind::Path(ref path4) = target.kind;
++    if match_qpath(path4, &["__next"]);
++    if let ExprKind::Path(ref path5) = value.kind;
++    if match_qpath(path5, &["val"]);
++    if let PatKind::TupleStruct(ref path6, ref fields1, None) = arms1[0].pat.kind;
++    if match_qpath(path6, &["{{root}}", "std", "option", "Option", "Some"]);
++    if fields1.len() == 1;
++    // unimplemented: field checks
++    if let ExprKind::Break(ref destination, None) = arms1[1].body.kind;
++    if let PatKind::Path(ref path7) = arms1[1].pat.kind;
++    if match_qpath(path7, &["{{root}}", "std", "option", "Option", "None"]);
++    if let StmtKind::Local(ref local1) = body.stmts[2].kind;
++    if let Some(ref init) = local1.init;
++    if let ExprKind::Path(ref path8) = init.kind;
++    if match_qpath(path8, &["__next"]);
++    if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local1.pat.kind;
++    if name1.as_str() == "y";
++    if let StmtKind::Expr(ref e1, _) = body.stmts[3].kind
++    if let ExprKind::Block(ref block) = e1.kind;
++    if let Some(trailing_expr1) = &block.expr;
++    if block.stmts.len() == 1;
++    if let StmtKind::Local(ref local2) = block.stmts[0].kind;
++    if let Some(ref init1) = local2.init;
++    if let ExprKind::Path(ref path9) = init1.kind;
++    if match_qpath(path9, &["y"]);
++    if let PatKind::Binding(BindingAnnotation::Unannotated, _, name2, None) = local2.pat.kind;
++    if name2.as_str() == "z";
++    if let PatKind::Binding(BindingAnnotation::Mutable, _, name3, None) = arms[0].pat.kind;
++    if name3.as_str() == "iter";
++    then {
++        // report your lint here
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2e9cb1466d0b5616a85770274cff4b602e7b687d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++#[allow(clippy::all)]
++
++fn main() {
++    #[clippy::author]
++    let _ = if true {
++        1 == 1;
++    } else {
++        2 == 2;
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c18d035953e539b97fea337b2f9bb9a866d94761
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++if_chain! {
++    if let StmtKind::Local(ref local) = stmt.kind;
++    if let Some(ref init) = local.init;
++    if let Some((ref cond, ref then, Some(else_))) = higher::if_block(&init);
++    if let ExprKind::Block(ref block) = else_.kind;
++    if let Some(trailing_expr) = &block.expr;
++    if block.stmts.len() == 1;
++    if let StmtKind::Semi(ref e, _) = block.stmts[0].kind
++    if let ExprKind::Binary(ref op, ref left, ref right) = e.kind;
++    if BinOpKind::Eq == op.node;
++    if let ExprKind::Lit(ref lit) = left.kind;
++    if let LitKind::Int(2, _) = lit.node;
++    if let ExprKind::Lit(ref lit1) = right.kind;
++    if let LitKind::Int(2, _) = lit1.node;
++    if let ExprKind::Lit(ref lit2) = cond.kind;
++    if let LitKind::Bool(true) = lit2.node;
++    if let ExprKind::Block(ref block1) = then.kind;
++    if let Some(trailing_expr1) = &block1.expr;
++    if block1.stmts.len() == 1;
++    if let StmtKind::Semi(ref e1, _) = block1.stmts[0].kind
++    if let ExprKind::Binary(ref op1, ref left1, ref right1) = e1.kind;
++    if BinOpKind::Eq == op1.node;
++    if let ExprKind::Lit(ref lit3) = left1.kind;
++    if let LitKind::Int(1, _) = lit3.node;
++    if let ExprKind::Lit(ref lit4) = right1.kind;
++    if let LitKind::Int(1, _) = lit4.node;
++    if let PatKind::Wild = local.pat.kind;
++    then {
++        // report your lint here
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bae4570e539a1b834d60014aafa2f6b627fb753d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++#![allow(dead_code)]
++#![allow(clippy::zero_ptr)]
++#![allow(clippy::transmute_ptr_to_ref)]
++#![allow(clippy::transmuting_null)]
++
++pub const ZPTR: *const usize = 0 as *const _;
++
++fn main() {
++    unsafe {
++        #[clippy::author]
++        let _: &i32 = std::mem::transmute(ZPTR);
++        let _: &i32 = std::mem::transmute(0 as *const i32);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..65f93f3cdc06b9aa4385f611f3342867cbc4a94b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++if_chain! {
++    if let StmtKind::Local(ref local) = stmt.kind;
++    if let Some(ref init) = local.init;
++    if let ExprKind::Call(ref func, ref args) = init.kind;
++    if let ExprKind::Path(ref path) = func.kind;
++    if match_qpath(path, &["std", "mem", "transmute"]);
++    if args.len() == 1;
++    if let ExprKind::Path(ref path1) = args[0].kind;
++    if match_qpath(path1, &["ZPTR"]);
++    if let PatKind::Wild = local.pat.kind;
++    then {
++        // report your lint here
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..674e07ec2d3da960a33079c121652875c3f4a1b8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++#![allow(clippy::let_and_return)]
++
++fn main() {
++    #[clippy::author]
++    let a = match 42 {
++        16 => 5,
++        17 => {
++            let x = 3;
++            x
++        },
++        _ => 1,
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2e8f8227dca12e551905dd9be112ff5daad18320
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++if_chain! {
++    if let StmtKind::Local(ref local) = stmt.kind;
++    if let Some(ref init) = local.init;
++    if let ExprKind::Match(ref expr, ref arms, MatchSource::Normal) = init.kind;
++    if let ExprKind::Lit(ref lit) = expr.kind;
++    if let LitKind::Int(42, _) = lit.node;
++    if arms.len() == 3;
++    if let ExprKind::Lit(ref lit1) = arms[0].body.kind;
++    if let LitKind::Int(5, _) = lit1.node;
++    if let PatKind::Lit(ref lit_expr) = arms[0].pat.kind
++    if let ExprKind::Lit(ref lit2) = lit_expr.kind;
++    if let LitKind::Int(16, _) = lit2.node;
++    if let ExprKind::Block(ref block) = arms[1].body.kind;
++    if let Some(trailing_expr) = &block.expr;
++    if block.stmts.len() == 1;
++    if let StmtKind::Local(ref local1) = block.stmts[0].kind;
++    if let Some(ref init1) = local1.init;
++    if let ExprKind::Lit(ref lit3) = init1.kind;
++    if let LitKind::Int(3, _) = lit3.node;
++    if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local1.pat.kind;
++    if name.as_str() == "x";
++    if let PatKind::Lit(ref lit_expr1) = arms[1].pat.kind
++    if let ExprKind::Lit(ref lit4) = lit_expr1.kind;
++    if let LitKind::Int(17, _) = lit4.node;
++    if let ExprKind::Lit(ref lit5) = arms[2].body.kind;
++    if let LitKind::Int(1, _) = lit5.node;
++    if let PatKind::Wild = arms[2].pat.kind;
++    if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local.pat.kind;
++    if name1.as_str() == "a";
++    then {
++        // report your lint here
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..869672d1eda5e7c549105fb7dd9d42404865935c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++#[macro_export]
++macro_rules! undocd_unsafe {
++    () => {
++        pub unsafe fn oy_vey() {
++            unimplemented!();
++        }
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1eb77c531835a165f898ba04d35ab50b64ae05bb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++#[macro_export]
++macro_rules! implicit_hasher_fn {
++    () => {
++        pub fn f(input: &HashMap<u32, u32>) {}
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0bbb9534928effd12458dfd4960534422a237f79
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++#![allow(dead_code)]
++
++//! Used to test that certain lints don't trigger in imported external macros
++
++#[macro_export]
++macro_rules! foofoo {
++    () => {
++        loop {}
++    };
++}
++
++#[macro_export]
++macro_rules! must_use_unit {
++    () => {
++        #[must_use]
++        fn foo() {}
++    };
++}
++
++#[macro_export]
++macro_rules! try_err {
++    () => {
++        pub fn try_err_fn() -> Result<i32, i32> {
++            let err: i32 = 1;
++            // To avoid warnings during rustfix
++            if true {
++                Err(err)?
++            } else {
++                Ok(2)
++            }
++        }
++    };
++}
++
++#[macro_export]
++macro_rules! string_add {
++    () => {
++        let y = "".to_owned();
++        let z = y + "...";
++    };
++}
++
++#[macro_export]
++macro_rules! take_external {
++    ($s:expr) => {
++        std::mem::replace($s, Default::default())
++    };
++}
++
++#[macro_export]
++macro_rules! option_env_unwrap_external {
++    ($env: expr) => {
++        option_env!($env).unwrap()
++    };
++    ($env: expr, $message: expr) => {
++        option_env!($env).expect($message)
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ed11c41e21c1a7e9a738918926bfcc8d933fd8bc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++#![allow(dead_code, unused_variables)]
++
++/// Utility macro to test linting behavior in `option_methods()`
++/// The lints included in `option_methods()` should not lint if the call to map is partially
++/// within a macro
++#[macro_export]
++macro_rules! opt_map {
++    ($opt:expr, $map:expr) => {
++        ($opt).map($map)
++    };
++}
++
++/// Struct to generate false positive for Iterator-based lints
++#[derive(Copy, Clone)]
++pub struct IteratorFalsePositives {
++    pub foo: u32,
++}
++
++impl IteratorFalsePositives {
++    pub fn filter(self) -> IteratorFalsePositives {
++        self
++    }
++
++    pub fn next(self) -> IteratorFalsePositives {
++        self
++    }
++
++    pub fn find(self) -> Option<u32> {
++        Some(self.foo)
++    }
++
++    pub fn position(self) -> Option<u32> {
++        Some(self.foo)
++    }
++
++    pub fn rposition(self) -> Option<u32> {
++        Some(self.foo)
++    }
++
++    pub fn nth(self, n: usize) -> Option<u32> {
++        Some(self.foo)
++    }
++
++    pub fn skip(self, _: usize) -> IteratorFalsePositives {
++        self
++    }
++
++    pub fn skip_while(self) -> IteratorFalsePositives {
++        self
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..21bb5b01e02b5ec683c99f73167e0efd2f964b75
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++// no-prefer-dynamic
++
++#![crate_type = "proc-macro"]
++#![feature(repr128, proc_macro_hygiene, proc_macro_quote)]
++
++extern crate proc_macro;
++
++use proc_macro::{quote, TokenStream};
++
++#[proc_macro_derive(DeriveSomething)]
++pub fn derive(_: TokenStream) -> TokenStream {
++    // Shound not trigger `used_underscore_binding`
++    let _inside_derive = 1;
++    assert_eq!(_inside_derive, _inside_derive);
++
++    let output = quote! {
++        // Should not trigger `useless_attribute`
++        #[allow(dead_code)]
++        extern crate rustc_middle;
++    };
++    output
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a8a85b4baefb728a4cd663ee47c98bceb02aea53
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++macro_rules! use_self {
++    (
++        impl $ty:ident {
++            fn func(&$this:ident) {
++                [fields($($field:ident)*)]
++            }
++        }
++    ) => (
++        impl  $ty {
++            fn func(&$this) {
++                let $ty { $($field),* } = $this;
++            }
++        }
++    )
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..414477aedd78323ccb46ebe0eea1be00f3d7b841
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++pub use crate::extern_exports::*;
++
++pub fn extern_foo() {}
++pub fn extern_bar() {}
++
++pub struct ExternA;
++
++pub mod inner {
++    pub mod inner_for_self_import {
++        pub fn inner_extern_foo() {}
++        pub fn inner_extern_bar() {}
++    }
++}
++
++mod extern_exports {
++    pub fn extern_exported() {}
++    pub struct ExternExportedStruct;
++    pub enum ExternExportedEnum {
++        A,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5c1fdd83efb0da25f4578355532ee4101126cb11
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,64 @@@
++// edition:2018
++#![warn(clippy::await_holding_lock)]
++
++use std::sync::Mutex;
++
++async fn bad(x: &Mutex<u32>) -> u32 {
++    let guard = x.lock().unwrap();
++    baz().await
++}
++
++async fn good(x: &Mutex<u32>) -> u32 {
++    {
++        let guard = x.lock().unwrap();
++        let y = *guard + 1;
++    }
++    baz().await;
++    let guard = x.lock().unwrap();
++    47
++}
++
++async fn baz() -> u32 {
++    42
++}
++
++async fn also_bad(x: &Mutex<u32>) -> u32 {
++    let first = baz().await;
++
++    let guard = x.lock().unwrap();
++
++    let second = baz().await;
++
++    let third = baz().await;
++
++    first + second + third
++}
++
++async fn not_good(x: &Mutex<u32>) -> u32 {
++    let first = baz().await;
++
++    let second = {
++        let guard = x.lock().unwrap();
++        baz().await
++    };
++
++    let third = baz().await;
++
++    first + second + third
++}
++
++fn block_bad(x: &Mutex<u32>) -> impl std::future::Future<Output = u32> + '_ {
++    async move {
++        let guard = x.lock().unwrap();
++        baz().await
++    }
++}
++
++fn main() {
++    let m = Mutex::new(100);
++    good(&m);
++    bad(&m);
++    also_bad(&m);
++    not_good(&m);
++    block_bad(&m);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8c47cb37d8c997230423bf3c4b899fa31e455ac1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.
++  --> $DIR/await_holding_lock.rs:7:9
++   |
++LL |     let guard = x.lock().unwrap();
++   |         ^^^^^
++   |
++   = note: `-D clippy::await-holding-lock` implied by `-D warnings`
++note: these are all the await points this lock is held through
++  --> $DIR/await_holding_lock.rs:7:5
++   |
++LL | /     let guard = x.lock().unwrap();
++LL | |     baz().await
++LL | | }
++   | |_^
++
++error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.
++  --> $DIR/await_holding_lock.rs:28:9
++   |
++LL |     let guard = x.lock().unwrap();
++   |         ^^^^^
++   |
++note: these are all the await points this lock is held through
++  --> $DIR/await_holding_lock.rs:28:5
++   |
++LL | /     let guard = x.lock().unwrap();
++LL | |
++LL | |     let second = baz().await;
++LL | |
++...  |
++LL | |     first + second + third
++LL | | }
++   | |_^
++
++error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.
++  --> $DIR/await_holding_lock.rs:41:13
++   |
++LL |         let guard = x.lock().unwrap();
++   |             ^^^^^
++   |
++note: these are all the await points this lock is held through
++  --> $DIR/await_holding_lock.rs:41:9
++   |
++LL | /         let guard = x.lock().unwrap();
++LL | |         baz().await
++LL | |     };
++   | |_____^
++
++error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.
++  --> $DIR/await_holding_lock.rs:52:13
++   |
++LL |         let guard = x.lock().unwrap();
++   |             ^^^^^
++   |
++note: these are all the await points this lock is held through
++  --> $DIR/await_holding_lock.rs:52:9
++   |
++LL | /         let guard = x.lock().unwrap();
++LL | |         baz().await
++LL | |     }
++   | |_____^
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cfb493fb52afb4d1ad23b5b6d2a1cc1688ecb26f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++const THREE_BITS: i64 = 7;
++const EVEN_MORE_REDIRECTION: i64 = THREE_BITS;
++
++#[warn(clippy::bad_bit_mask)]
++#[allow(
++    clippy::ineffective_bit_mask,
++    clippy::identity_op,
++    clippy::no_effect,
++    clippy::unnecessary_operation
++)]
++fn main() {
++    let x = 5;
++
++    x & 0 == 0;
++    x & 1 == 1; //ok, distinguishes bit 0
++    x & 1 == 0; //ok, compared with zero
++    x & 2 == 1;
++    x | 0 == 0; //ok, equals x == 0 (maybe warn?)
++    x | 1 == 3; //ok, equals x == 2 || x == 3
++    x | 3 == 3; //ok, equals x <= 3
++    x | 3 == 2;
++
++    x & 1 > 1;
++    x & 2 > 1; // ok, distinguishes x & 2 == 2 from x & 2 == 0
++    x & 2 < 1; // ok, distinguishes x & 2 == 2 from x & 2 == 0
++    x | 1 > 1; // ok (if a bit silly), equals x > 1
++    x | 2 > 1;
++    x | 2 <= 2; // ok (if a bit silly), equals x <= 2
++
++    x & 192 == 128; // ok, tests for bit 7 and not bit 6
++    x & 0xffc0 == 0xfe80; // ok
++
++    // this also now works with constants
++    x & THREE_BITS == 8;
++    x | EVEN_MORE_REDIRECTION < 7;
++
++    0 & x == 0;
++    1 | x > 1;
++
++    // and should now also match uncommon usage
++    1 < 2 | x;
++    2 == 3 | x;
++    1 == x & 2;
++
++    x | 1 > 2; // no error, because we allowed ineffective bit masks
++    ineffective();
++}
++
++#[warn(clippy::ineffective_bit_mask)]
++#[allow(clippy::bad_bit_mask, clippy::no_effect, clippy::unnecessary_operation)]
++fn ineffective() {
++    let x = 5;
++
++    x | 1 > 3;
++    x | 1 < 4;
++    x | 1 <= 3;
++    x | 1 >= 8;
++
++    x | 1 > 2; // not an error (yet), better written as x >= 2
++    x | 1 >= 7; // not an error (yet), better written as x >= 6
++    x | 3 > 4; // not an error (yet), better written as x >= 4
++    x | 4 <= 19;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dc5ad6dfbdff99b390755739f763c1eb3f0cc5a9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,110 @@@
++error: &-masking with zero
++  --> $DIR/bit_masks.rs:14:5
++   |
++LL |     x & 0 == 0;
++   |     ^^^^^^^^^^
++   |
++   = note: `-D clippy::bad-bit-mask` implied by `-D warnings`
++
++error: this operation will always return zero. This is likely not the intended outcome
++  --> $DIR/bit_masks.rs:14:5
++   |
++LL |     x & 0 == 0;
++   |     ^^^^^
++   |
++   = note: `#[deny(clippy::erasing_op)]` on by default
++
++error: incompatible bit mask: `_ & 2` can never be equal to `1`
++  --> $DIR/bit_masks.rs:17:5
++   |
++LL |     x & 2 == 1;
++   |     ^^^^^^^^^^
++
++error: incompatible bit mask: `_ | 3` can never be equal to `2`
++  --> $DIR/bit_masks.rs:21:5
++   |
++LL |     x | 3 == 2;
++   |     ^^^^^^^^^^
++
++error: incompatible bit mask: `_ & 1` will never be higher than `1`
++  --> $DIR/bit_masks.rs:23:5
++   |
++LL |     x & 1 > 1;
++   |     ^^^^^^^^^
++
++error: incompatible bit mask: `_ | 2` will always be higher than `1`
++  --> $DIR/bit_masks.rs:27:5
++   |
++LL |     x | 2 > 1;
++   |     ^^^^^^^^^
++
++error: incompatible bit mask: `_ & 7` can never be equal to `8`
++  --> $DIR/bit_masks.rs:34:5
++   |
++LL |     x & THREE_BITS == 8;
++   |     ^^^^^^^^^^^^^^^^^^^
++
++error: incompatible bit mask: `_ | 7` will never be lower than `7`
++  --> $DIR/bit_masks.rs:35:5
++   |
++LL |     x | EVEN_MORE_REDIRECTION < 7;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: &-masking with zero
++  --> $DIR/bit_masks.rs:37:5
++   |
++LL |     0 & x == 0;
++   |     ^^^^^^^^^^
++
++error: this operation will always return zero. This is likely not the intended outcome
++  --> $DIR/bit_masks.rs:37:5
++   |
++LL |     0 & x == 0;
++   |     ^^^^^
++
++error: incompatible bit mask: `_ | 2` will always be higher than `1`
++  --> $DIR/bit_masks.rs:41:5
++   |
++LL |     1 < 2 | x;
++   |     ^^^^^^^^^
++
++error: incompatible bit mask: `_ | 3` can never be equal to `2`
++  --> $DIR/bit_masks.rs:42:5
++   |
++LL |     2 == 3 | x;
++   |     ^^^^^^^^^^
++
++error: incompatible bit mask: `_ & 2` can never be equal to `1`
++  --> $DIR/bit_masks.rs:43:5
++   |
++LL |     1 == x & 2;
++   |     ^^^^^^^^^^
++
++error: ineffective bit mask: `x | 1` compared to `3`, is the same as x compared directly
++  --> $DIR/bit_masks.rs:54:5
++   |
++LL |     x | 1 > 3;
++   |     ^^^^^^^^^
++   |
++   = note: `-D clippy::ineffective-bit-mask` implied by `-D warnings`
++
++error: ineffective bit mask: `x | 1` compared to `4`, is the same as x compared directly
++  --> $DIR/bit_masks.rs:55:5
++   |
++LL |     x | 1 < 4;
++   |     ^^^^^^^^^
++
++error: ineffective bit mask: `x | 1` compared to `3`, is the same as x compared directly
++  --> $DIR/bit_masks.rs:56:5
++   |
++LL |     x | 1 <= 3;
++   |     ^^^^^^^^^^
++
++error: ineffective bit mask: `x | 1` compared to `8`, is the same as x compared directly
++  --> $DIR/bit_masks.rs:57:5
++   |
++LL |     x | 1 >= 8;
++   |     ^^^^^^^^^^
++
++error: aborting due to 17 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ca9d8d16b787df5af6c08f834306dd49d9ed3cf2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++#![allow(
++    dead_code,
++    clippy::similar_names,
++    clippy::single_match,
++    clippy::toplevel_ref_arg,
++    unused_mut,
++    unused_variables
++)]
++#![warn(clippy::blacklisted_name)]
++
++fn test(foo: ()) {}
++
++fn main() {
++    let foo = 42;
++    let bar = 42;
++    let baz = 42;
++
++    let barb = 42;
++    let barbaric = 42;
++
++    match (42, Some(1337), Some(0)) {
++        (foo, Some(bar), baz @ Some(_)) => (),
++        _ => (),
++    }
++}
++
++fn issue_1647(mut foo: u8) {
++    let mut bar = 0;
++    if let Some(mut baz) = Some(42) {}
++}
++
++fn issue_1647_ref() {
++    let ref bar = 0;
++    if let Some(ref baz) = Some(42) {}
++}
++
++fn issue_1647_ref_mut() {
++    let ref mut bar = 0;
++    if let Some(ref mut baz) = Some(42) {}
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..44123829fb0f651208a28aa35f981a94f42c99a5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,88 @@@
++error: use of a blacklisted/placeholder name `foo`
++  --> $DIR/blacklisted_name.rs:11:9
++   |
++LL | fn test(foo: ()) {}
++   |         ^^^
++   |
++   = note: `-D clippy::blacklisted-name` implied by `-D warnings`
++
++error: use of a blacklisted/placeholder name `foo`
++  --> $DIR/blacklisted_name.rs:14:9
++   |
++LL |     let foo = 42;
++   |         ^^^
++
++error: use of a blacklisted/placeholder name `bar`
++  --> $DIR/blacklisted_name.rs:15:9
++   |
++LL |     let bar = 42;
++   |         ^^^
++
++error: use of a blacklisted/placeholder name `baz`
++  --> $DIR/blacklisted_name.rs:16:9
++   |
++LL |     let baz = 42;
++   |         ^^^
++
++error: use of a blacklisted/placeholder name `foo`
++  --> $DIR/blacklisted_name.rs:22:10
++   |
++LL |         (foo, Some(bar), baz @ Some(_)) => (),
++   |          ^^^
++
++error: use of a blacklisted/placeholder name `bar`
++  --> $DIR/blacklisted_name.rs:22:20
++   |
++LL |         (foo, Some(bar), baz @ Some(_)) => (),
++   |                    ^^^
++
++error: use of a blacklisted/placeholder name `baz`
++  --> $DIR/blacklisted_name.rs:22:26
++   |
++LL |         (foo, Some(bar), baz @ Some(_)) => (),
++   |                          ^^^
++
++error: use of a blacklisted/placeholder name `foo`
++  --> $DIR/blacklisted_name.rs:27:19
++   |
++LL | fn issue_1647(mut foo: u8) {
++   |                   ^^^
++
++error: use of a blacklisted/placeholder name `bar`
++  --> $DIR/blacklisted_name.rs:28:13
++   |
++LL |     let mut bar = 0;
++   |             ^^^
++
++error: use of a blacklisted/placeholder name `baz`
++  --> $DIR/blacklisted_name.rs:29:21
++   |
++LL |     if let Some(mut baz) = Some(42) {}
++   |                     ^^^
++
++error: use of a blacklisted/placeholder name `bar`
++  --> $DIR/blacklisted_name.rs:33:13
++   |
++LL |     let ref bar = 0;
++   |             ^^^
++
++error: use of a blacklisted/placeholder name `baz`
++  --> $DIR/blacklisted_name.rs:34:21
++   |
++LL |     if let Some(ref baz) = Some(42) {}
++   |                     ^^^
++
++error: use of a blacklisted/placeholder name `bar`
++  --> $DIR/blacklisted_name.rs:38:17
++   |
++LL |     let ref mut bar = 0;
++   |                 ^^^
++
++error: use of a blacklisted/placeholder name `baz`
++  --> $DIR/blacklisted_name.rs:39:25
++   |
++LL |     if let Some(ref mut baz) = Some(42) {}
++   |                         ^^^
++
++error: aborting due to 14 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..955801e40f9b7659d15336a93ecfeb27d71f2545
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,75 @@@
++// run-rustfix
++#![warn(clippy::block_in_if_condition_expr)]
++#![warn(clippy::block_in_if_condition_stmt)]
++#![allow(unused, clippy::let_and_return)]
++#![warn(clippy::nonminimal_bool)]
++
++macro_rules! blocky {
++    () => {{
++        true
++    }};
++}
++
++macro_rules! blocky_too {
++    () => {{
++        let r = true;
++        r
++    }};
++}
++
++fn macro_if() {
++    if blocky!() {}
++
++    if blocky_too!() {}
++}
++
++fn condition_has_block() -> i32 {
++    let res = {
++        let x = 3;
++        x == 3
++    }; if res {
++        6
++    } else {
++        10
++    }
++}
++
++fn condition_has_block_with_single_expression() -> i32 {
++    if true {
++        6
++    } else {
++        10
++    }
++}
++
++fn condition_is_normal() -> i32 {
++    let x = 3;
++    if x == 3 {
++        6
++    } else {
++        10
++    }
++}
++
++fn condition_is_unsafe_block() {
++    let a: i32 = 1;
++
++    // this should not warn because the condition is an unsafe block
++    if unsafe { 1u32 == std::mem::transmute(a) } {
++        println!("1u32 == a");
++    }
++}
++
++fn block_in_assert() {
++    let opt = Some(42);
++    assert!(opt
++        .as_ref()
++        .and_then(|val| {
++            let mut v = val * 2;
++            v -= 1;
++            Some(v * 3)
++        })
++        .is_some());
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a6ea01d5fc5f901a7e49e593dab65c514c6e7c7e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,75 @@@
++// run-rustfix
++#![warn(clippy::block_in_if_condition_expr)]
++#![warn(clippy::block_in_if_condition_stmt)]
++#![allow(unused, clippy::let_and_return)]
++#![warn(clippy::nonminimal_bool)]
++
++macro_rules! blocky {
++    () => {{
++        true
++    }};
++}
++
++macro_rules! blocky_too {
++    () => {{
++        let r = true;
++        r
++    }};
++}
++
++fn macro_if() {
++    if blocky!() {}
++
++    if blocky_too!() {}
++}
++
++fn condition_has_block() -> i32 {
++    if {
++        let x = 3;
++        x == 3
++    } {
++        6
++    } else {
++        10
++    }
++}
++
++fn condition_has_block_with_single_expression() -> i32 {
++    if { true } {
++        6
++    } else {
++        10
++    }
++}
++
++fn condition_is_normal() -> i32 {
++    let x = 3;
++    if true && x == 3 {
++        6
++    } else {
++        10
++    }
++}
++
++fn condition_is_unsafe_block() {
++    let a: i32 = 1;
++
++    // this should not warn because the condition is an unsafe block
++    if unsafe { 1u32 == std::mem::transmute(a) } {
++        println!("1u32 == a");
++    }
++}
++
++fn block_in_assert() {
++    let opt = Some(42);
++    assert!(opt
++        .as_ref()
++        .and_then(|val| {
++            let mut v = val * 2;
++            v -= 1;
++            Some(v * 3)
++        })
++        .is_some());
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b0a0a276c89088eb6061c4603989a0463a07ed43
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
++  --> $DIR/block_in_if_condition.rs:27:5
++   |
++LL | /     if {
++LL | |         let x = 3;
++LL | |         x == 3
++LL | |     } {
++   | |_____^
++   |
++   = note: `-D clippy::block-in-if-condition-stmt` implied by `-D warnings`
++help: try
++   |
++LL |     let res = {
++LL |         let x = 3;
++LL |         x == 3
++LL |     }; if res {
++   |
++
++error: omit braces around single expression condition
++  --> $DIR/block_in_if_condition.rs:38:8
++   |
++LL |     if { true } {
++   |        ^^^^^^^^ help: try: `true`
++   |
++   = note: `-D clippy::block-in-if-condition-expr` implied by `-D warnings`
++
++error: this boolean expression can be simplified
++  --> $DIR/block_in_if_condition.rs:47:8
++   |
++LL |     if true && x == 3 {
++   |        ^^^^^^^^^^^^^^ help: try: `x == 3`
++   |
++   = note: `-D clippy::nonminimal-bool` implied by `-D warnings`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bac3eda5e7f37b505a3c1f44465484ed14a5f7e1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,48 @@@
++#![warn(clippy::block_in_if_condition_expr)]
++#![warn(clippy::block_in_if_condition_stmt)]
++#![allow(unused, clippy::let_and_return)]
++
++fn predicate<F: FnOnce(T) -> bool, T>(pfn: F, val: T) -> bool {
++    pfn(val)
++}
++
++fn pred_test() {
++    let v = 3;
++    let sky = "blue";
++    // This is a sneaky case, where the block isn't directly in the condition,
++    // but is actually nside a closure that the condition is using.
++    // The same principle applies -- add some extra expressions to make sure
++    // linter isn't confused by them.
++    if v == 3
++        && sky == "blue"
++        && predicate(
++            |x| {
++                let target = 3;
++                x == target
++            },
++            v,
++        )
++    {}
++
++    if predicate(
++        |x| {
++            let target = 3;
++            x == target
++        },
++        v,
++    ) {}
++}
++
++fn closure_without_block() {
++    if predicate(|x| x == 3, 6) {}
++}
++
++fn macro_in_closure() {
++    let option = Some(true);
++
++    if option.unwrap_or_else(|| unimplemented!()) {
++        unimplemented!()
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..86cd24fe76321b0813813f0eb34b9765b5d9d126
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
++  --> $DIR/block_in_if_condition_closure.rs:19:17
++   |
++LL |               |x| {
++   |  _________________^
++LL | |                 let target = 3;
++LL | |                 x == target
++LL | |             },
++   | |_____________^
++   |
++   = note: `-D clippy::block-in-if-condition-stmt` implied by `-D warnings`
++
++error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let`
++  --> $DIR/block_in_if_condition_closure.rs:28:13
++   |
++LL |           |x| {
++   |  _____________^
++LL | |             let target = 3;
++LL | |             x == target
++LL | |         },
++   | |_________^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9121176475938728696f5cde36e5c52544652d9c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,129 @@@
++// run-rustfix
++
++#[warn(clippy::bool_comparison)]
++fn main() {
++    let x = true;
++    if x {
++        "yes"
++    } else {
++        "no"
++    };
++    if !x {
++        "yes"
++    } else {
++        "no"
++    };
++    if x {
++        "yes"
++    } else {
++        "no"
++    };
++    if !x {
++        "yes"
++    } else {
++        "no"
++    };
++    if !x {
++        "yes"
++    } else {
++        "no"
++    };
++    if x {
++        "yes"
++    } else {
++        "no"
++    };
++    if !x {
++        "yes"
++    } else {
++        "no"
++    };
++    if x {
++        "yes"
++    } else {
++        "no"
++    };
++    if !x {
++        "yes"
++    } else {
++        "no"
++    };
++    if x {
++        "yes"
++    } else {
++        "no"
++    };
++    if x {
++        "yes"
++    } else {
++        "no"
++    };
++    if !x {
++        "yes"
++    } else {
++        "no"
++    };
++    let y = true;
++    if !x & y {
++        "yes"
++    } else {
++        "no"
++    };
++    if x & !y {
++        "yes"
++    } else {
++        "no"
++    };
++}
++
++#[allow(dead_code)]
++fn issue3703() {
++    struct Foo;
++    impl PartialEq<bool> for Foo {
++        fn eq(&self, _: &bool) -> bool {
++            true
++        }
++    }
++    impl PartialEq<Foo> for bool {
++        fn eq(&self, _: &Foo) -> bool {
++            true
++        }
++    }
++    impl PartialOrd<bool> for Foo {
++        fn partial_cmp(&self, _: &bool) -> Option<std::cmp::Ordering> {
++            None
++        }
++    }
++    impl PartialOrd<Foo> for bool {
++        fn partial_cmp(&self, _: &Foo) -> Option<std::cmp::Ordering> {
++            None
++        }
++    }
++
++    if Foo == true {}
++    if true == Foo {}
++    if Foo != true {}
++    if true != Foo {}
++    if Foo == false {}
++    if false == Foo {}
++    if Foo != false {}
++    if false != Foo {}
++    if Foo < false {}
++    if false < Foo {}
++}
++
++#[allow(dead_code)]
++fn issue4983() {
++    let a = true;
++    let b = false;
++
++    if a != b {};
++    if a != b {};
++    if a == b {};
++    if !a == !b {};
++
++    if b != a {};
++    if b != a {};
++    if b == a {};
++    if !b == !a {};
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..01ee35859f0da64c055a3eb6a9b313b77e597cb7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,129 @@@
++// run-rustfix
++
++#[warn(clippy::bool_comparison)]
++fn main() {
++    let x = true;
++    if x == true {
++        "yes"
++    } else {
++        "no"
++    };
++    if x == false {
++        "yes"
++    } else {
++        "no"
++    };
++    if true == x {
++        "yes"
++    } else {
++        "no"
++    };
++    if false == x {
++        "yes"
++    } else {
++        "no"
++    };
++    if x != true {
++        "yes"
++    } else {
++        "no"
++    };
++    if x != false {
++        "yes"
++    } else {
++        "no"
++    };
++    if true != x {
++        "yes"
++    } else {
++        "no"
++    };
++    if false != x {
++        "yes"
++    } else {
++        "no"
++    };
++    if x < true {
++        "yes"
++    } else {
++        "no"
++    };
++    if false < x {
++        "yes"
++    } else {
++        "no"
++    };
++    if x > false {
++        "yes"
++    } else {
++        "no"
++    };
++    if true > x {
++        "yes"
++    } else {
++        "no"
++    };
++    let y = true;
++    if x < y {
++        "yes"
++    } else {
++        "no"
++    };
++    if x > y {
++        "yes"
++    } else {
++        "no"
++    };
++}
++
++#[allow(dead_code)]
++fn issue3703() {
++    struct Foo;
++    impl PartialEq<bool> for Foo {
++        fn eq(&self, _: &bool) -> bool {
++            true
++        }
++    }
++    impl PartialEq<Foo> for bool {
++        fn eq(&self, _: &Foo) -> bool {
++            true
++        }
++    }
++    impl PartialOrd<bool> for Foo {
++        fn partial_cmp(&self, _: &bool) -> Option<std::cmp::Ordering> {
++            None
++        }
++    }
++    impl PartialOrd<Foo> for bool {
++        fn partial_cmp(&self, _: &Foo) -> Option<std::cmp::Ordering> {
++            None
++        }
++    }
++
++    if Foo == true {}
++    if true == Foo {}
++    if Foo != true {}
++    if true != Foo {}
++    if Foo == false {}
++    if false == Foo {}
++    if Foo != false {}
++    if false != Foo {}
++    if Foo < false {}
++    if false < Foo {}
++}
++
++#[allow(dead_code)]
++fn issue4983() {
++    let a = true;
++    let b = false;
++
++    if a == !b {};
++    if !a == b {};
++    if a == b {};
++    if !a == !b {};
++
++    if b == !a {};
++    if !b == a {};
++    if b == a {};
++    if !b == !a {};
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eeb1f20ee894d89b0d267ff67bb4098054f6ab04
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,112 @@@
++error: equality checks against true are unnecessary
++  --> $DIR/bool_comparison.rs:6:8
++   |
++LL |     if x == true {
++   |        ^^^^^^^^^ help: try simplifying it as shown: `x`
++   |
++   = note: `-D clippy::bool-comparison` implied by `-D warnings`
++
++error: equality checks against false can be replaced by a negation
++  --> $DIR/bool_comparison.rs:11:8
++   |
++LL |     if x == false {
++   |        ^^^^^^^^^^ help: try simplifying it as shown: `!x`
++
++error: equality checks against true are unnecessary
++  --> $DIR/bool_comparison.rs:16:8
++   |
++LL |     if true == x {
++   |        ^^^^^^^^^ help: try simplifying it as shown: `x`
++
++error: equality checks against false can be replaced by a negation
++  --> $DIR/bool_comparison.rs:21:8
++   |
++LL |     if false == x {
++   |        ^^^^^^^^^^ help: try simplifying it as shown: `!x`
++
++error: inequality checks against true can be replaced by a negation
++  --> $DIR/bool_comparison.rs:26:8
++   |
++LL |     if x != true {
++   |        ^^^^^^^^^ help: try simplifying it as shown: `!x`
++
++error: inequality checks against false are unnecessary
++  --> $DIR/bool_comparison.rs:31:8
++   |
++LL |     if x != false {
++   |        ^^^^^^^^^^ help: try simplifying it as shown: `x`
++
++error: inequality checks against true can be replaced by a negation
++  --> $DIR/bool_comparison.rs:36:8
++   |
++LL |     if true != x {
++   |        ^^^^^^^^^ help: try simplifying it as shown: `!x`
++
++error: inequality checks against false are unnecessary
++  --> $DIR/bool_comparison.rs:41:8
++   |
++LL |     if false != x {
++   |        ^^^^^^^^^^ help: try simplifying it as shown: `x`
++
++error: less than comparison against true can be replaced by a negation
++  --> $DIR/bool_comparison.rs:46:8
++   |
++LL |     if x < true {
++   |        ^^^^^^^^ help: try simplifying it as shown: `!x`
++
++error: greater than checks against false are unnecessary
++  --> $DIR/bool_comparison.rs:51:8
++   |
++LL |     if false < x {
++   |        ^^^^^^^^^ help: try simplifying it as shown: `x`
++
++error: greater than checks against false are unnecessary
++  --> $DIR/bool_comparison.rs:56:8
++   |
++LL |     if x > false {
++   |        ^^^^^^^^^ help: try simplifying it as shown: `x`
++
++error: less than comparison against true can be replaced by a negation
++  --> $DIR/bool_comparison.rs:61:8
++   |
++LL |     if true > x {
++   |        ^^^^^^^^ help: try simplifying it as shown: `!x`
++
++error: order comparisons between booleans can be simplified
++  --> $DIR/bool_comparison.rs:67:8
++   |
++LL |     if x < y {
++   |        ^^^^^ help: try simplifying it as shown: `!x & y`
++
++error: order comparisons between booleans can be simplified
++  --> $DIR/bool_comparison.rs:72:8
++   |
++LL |     if x > y {
++   |        ^^^^^ help: try simplifying it as shown: `x & !y`
++
++error: This comparison might be written more concisely
++  --> $DIR/bool_comparison.rs:120:8
++   |
++LL |     if a == !b {};
++   |        ^^^^^^^ help: try simplifying it as shown: `a != b`
++
++error: This comparison might be written more concisely
++  --> $DIR/bool_comparison.rs:121:8
++   |
++LL |     if !a == b {};
++   |        ^^^^^^^ help: try simplifying it as shown: `a != b`
++
++error: This comparison might be written more concisely
++  --> $DIR/bool_comparison.rs:125:8
++   |
++LL |     if b == !a {};
++   |        ^^^^^^^ help: try simplifying it as shown: `b != a`
++
++error: This comparison might be written more concisely
++  --> $DIR/bool_comparison.rs:126:8
++   |
++LL |     if !b == a {};
++   |        ^^^^^^^ help: try simplifying it as shown: `b != a`
++
++error: aborting due to 18 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1901de46ca8940a4d029c09a76b800002eb540b4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,99 @@@
++#![deny(clippy::borrowed_box)]
++#![allow(clippy::blacklisted_name)]
++#![allow(unused_variables)]
++#![allow(dead_code)]
++
++pub fn test1(foo: &mut Box<bool>) {
++    // Although this function could be changed to "&mut bool",
++    // avoiding the Box, mutable references to boxes are not
++    // flagged by this lint.
++    //
++    // This omission is intentional: By passing a mutable Box,
++    // the memory location of the pointed-to object could be
++    // modified. By passing a mutable reference, the contents
++    // could change, but not the location.
++    println!("{:?}", foo)
++}
++
++pub fn test2() {
++    let foo: &Box<bool>;
++}
++
++struct Test3<'a> {
++    foo: &'a Box<bool>,
++}
++
++trait Test4 {
++    fn test4(a: &Box<bool>);
++}
++
++impl<'a> Test4 for Test3<'a> {
++    fn test4(a: &Box<bool>) {
++        unimplemented!();
++    }
++}
++
++use std::any::Any;
++
++pub fn test5(foo: &mut Box<dyn Any>) {
++    println!("{:?}", foo)
++}
++
++pub fn test6() {
++    let foo: &Box<dyn Any>;
++}
++
++struct Test7<'a> {
++    foo: &'a Box<dyn Any>,
++}
++
++trait Test8 {
++    fn test8(a: &Box<dyn Any>);
++}
++
++impl<'a> Test8 for Test7<'a> {
++    fn test8(a: &Box<dyn Any>) {
++        unimplemented!();
++    }
++}
++
++pub fn test9(foo: &mut Box<dyn Any + Send + Sync>) {
++    let _ = foo;
++}
++
++pub fn test10() {
++    let foo: &Box<dyn Any + Send + 'static>;
++}
++
++struct Test11<'a> {
++    foo: &'a Box<dyn Any + Send>,
++}
++
++trait Test12 {
++    fn test4(a: &Box<dyn Any + 'static>);
++}
++
++impl<'a> Test12 for Test11<'a> {
++    fn test4(a: &Box<dyn Any + 'static>) {
++        unimplemented!();
++    }
++}
++
++pub fn test13(boxed_slice: &mut Box<[i32]>) {
++    // Unconditionally replaces the box pointer.
++    //
++    // This cannot be accomplished if "&mut [i32]" is passed,
++    // and provides a test case where passing a reference to
++    // a Box is valid.
++    let mut data = vec![12];
++    *boxed_slice = data.into_boxed_slice();
++}
++
++fn main() {
++    test1(&mut Box::new(false));
++    test2();
++    test5(&mut (Box::new(false) as Box<dyn Any>));
++    test6();
++    test9(&mut (Box::new(false) as Box<dyn Any + Send + Sync>));
++    test10();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b5db691f89f3936848d9856ef287690513816a15
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
++  --> $DIR/borrow_box.rs:19:14
++   |
++LL |     let foo: &Box<bool>;
++   |              ^^^^^^^^^^ help: try: `&bool`
++   |
++note: the lint level is defined here
++  --> $DIR/borrow_box.rs:1:9
++   |
++LL | #![deny(clippy::borrowed_box)]
++   |         ^^^^^^^^^^^^^^^^^^^^
++
++error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
++  --> $DIR/borrow_box.rs:23:10
++   |
++LL |     foo: &'a Box<bool>,
++   |          ^^^^^^^^^^^^^ help: try: `&'a bool`
++
++error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
++  --> $DIR/borrow_box.rs:27:17
++   |
++LL |     fn test4(a: &Box<bool>);
++   |                 ^^^^^^^^^^ help: try: `&bool`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fef9f4f39f80941d56d58225a086c526bcfa9a43
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,85 @@@
++#![warn(clippy::borrow_interior_mutable_const)]
++#![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)]
++
++use std::borrow::Cow;
++use std::cell::Cell;
++use std::fmt::Display;
++use std::sync::atomic::{AtomicUsize, Ordering};
++use std::sync::Once;
++
++const ATOMIC: AtomicUsize = AtomicUsize::new(5);
++const CELL: Cell<usize> = Cell::new(6);
++const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
++const INTEGER: u8 = 8;
++const STRING: String = String::new();
++const STR: &str = "012345";
++const COW: Cow<str> = Cow::Borrowed("abcdef");
++const NO_ANN: &dyn Display = &70;
++static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
++const ONCE_INIT: Once = Once::new();
++
++trait Trait<T>: Copy {
++    type NonCopyType;
++
++    const ATOMIC: AtomicUsize;
++}
++
++impl Trait<u32> for u64 {
++    type NonCopyType = u16;
++
++    const ATOMIC: AtomicUsize = AtomicUsize::new(9);
++}
++
++fn main() {
++    ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
++    assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
++
++    let _once = ONCE_INIT;
++    let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
++    let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
++    let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
++    let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
++    let _atomic_into_inner = ATOMIC.into_inner();
++    // these should be all fine.
++    let _twice = (ONCE_INIT, ONCE_INIT);
++    let _ref_twice = &(ONCE_INIT, ONCE_INIT);
++    let _ref_once = &(ONCE_INIT, ONCE_INIT).0;
++    let _array_twice = [ONCE_INIT, ONCE_INIT];
++    let _ref_array_twice = &[ONCE_INIT, ONCE_INIT];
++    let _ref_array_once = &[ONCE_INIT, ONCE_INIT][0];
++
++    // referencing projection is still bad.
++    let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
++    let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
++    let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
++    let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
++    let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability
++    let _ = &*ATOMIC_TUPLE.1; //~ ERROR interior mutability
++    let _ = &ATOMIC_TUPLE.2;
++    let _ = (&&&&ATOMIC_TUPLE).0;
++    let _ = (&&&&ATOMIC_TUPLE).2;
++    let _ = ATOMIC_TUPLE.0;
++    let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
++    let _ = ATOMIC_TUPLE.1.into_iter();
++    let _ = ATOMIC_TUPLE.2;
++    let _ = &{ ATOMIC_TUPLE };
++
++    CELL.set(2); //~ ERROR interior mutability
++    assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
++
++    assert_eq!(INTEGER, 8);
++    assert!(STRING.is_empty());
++
++    let a = ATOMIC;
++    a.store(4, Ordering::SeqCst);
++    assert_eq!(a.load(Ordering::SeqCst), 4);
++
++    STATIC_TUPLE.0.store(3, Ordering::SeqCst);
++    assert_eq!(STATIC_TUPLE.0.load(Ordering::SeqCst), 3);
++    assert!(STATIC_TUPLE.1.is_empty());
++
++    u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
++    assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
++
++    assert_eq!(NO_ANN.to_string(), "70"); // should never lint this.
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dc738064a17184ddf9c1177e36d18a9db60c31b8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,131 @@@
++error: a `const` item with interior mutability should not be borrowed
++  --> $DIR/borrow_interior_mutable_const.rs:34:5
++   |
++LL |     ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
++   |     ^^^^^^
++   |
++   = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings`
++   = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++  --> $DIR/borrow_interior_mutable_const.rs:35:16
++   |
++LL |     assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
++   |                ^^^^^^
++   |
++   = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++  --> $DIR/borrow_interior_mutable_const.rs:38:22
++   |
++LL |     let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
++   |                      ^^^^^^^^^
++   |
++   = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++  --> $DIR/borrow_interior_mutable_const.rs:39:25
++   |
++LL |     let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
++   |                         ^^^^^^^^^
++   |
++   = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++  --> $DIR/borrow_interior_mutable_const.rs:40:27
++   |
++LL |     let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
++   |                           ^^^^^^^^^
++   |
++   = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++  --> $DIR/borrow_interior_mutable_const.rs:41:26
++   |
++LL |     let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
++   |                          ^^^^^^^^^
++   |
++   = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++  --> $DIR/borrow_interior_mutable_const.rs:52:14
++   |
++LL |     let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
++   |              ^^^^^^^^^^^^
++   |
++   = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++  --> $DIR/borrow_interior_mutable_const.rs:53:14
++   |
++LL |     let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
++   |              ^^^^^^^^^^^^
++   |
++   = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++  --> $DIR/borrow_interior_mutable_const.rs:54:19
++   |
++LL |     let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
++   |                   ^^^^^^^^^^^^
++   |
++   = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++  --> $DIR/borrow_interior_mutable_const.rs:55:14
++   |
++LL |     let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
++   |              ^^^^^^^^^^^^
++   |
++   = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++  --> $DIR/borrow_interior_mutable_const.rs:56:13
++   |
++LL |     let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability
++   |             ^^^^^^^^^^^^
++   |
++   = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++  --> $DIR/borrow_interior_mutable_const.rs:62:13
++   |
++LL |     let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
++   |             ^^^^^^^^^^^^
++   |
++   = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++  --> $DIR/borrow_interior_mutable_const.rs:67:5
++   |
++LL |     CELL.set(2); //~ ERROR interior mutability
++   |     ^^^^
++   |
++   = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++  --> $DIR/borrow_interior_mutable_const.rs:68:16
++   |
++LL |     assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
++   |                ^^^^
++   |
++   = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++  --> $DIR/borrow_interior_mutable_const.rs:81:5
++   |
++LL |     u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
++   |     ^^^^^^^^^^^
++   |
++   = help: assign this const to a local or static variable, and use the variable here
++
++error: a `const` item with interior mutability should not be borrowed
++  --> $DIR/borrow_interior_mutable_const.rs:82:16
++   |
++LL |     assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
++   |                ^^^^^^^^^^^
++   |
++   = help: assign this const to a local or static variable, and use the variable here
++
++error: aborting due to 16 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..87b67c23704c9e71ba4cec847a89cef943d8498e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++#![warn(clippy::all)]
++#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
++#![allow(clippy::blacklisted_name)]
++
++macro_rules! boxit {
++    ($init:expr, $x:ty) => {
++        let _: Box<$x> = Box::new($init);
++    };
++}
++
++fn test_macro() {
++    boxit!(Vec::new(), Vec<u8>);
++}
++pub fn test(foo: Box<Vec<bool>>) {
++    println!("{:?}", foo.get(0))
++}
++
++pub fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
++    // pass if #31 is fixed
++    foo(vec![1, 2, 3])
++}
++
++pub fn test_local_not_linted() {
++    let _: Box<Vec<bool>>;
++}
++
++fn main() {
++    test(Box::new(Vec::new()));
++    test2(Box::new(|v| println!("{:?}", v)));
++    test_macro();
++    test_local_not_linted();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fca12eddd573f04c1721bf2143d235ad2e3a06aa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++error: you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`
++  --> $DIR/box_vec.rs:14:18
++   |
++LL | pub fn test(foo: Box<Vec<bool>>) {
++   |                  ^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::box-vec` implied by `-D warnings`
++   = help: `Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation.
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..69b8b6a0e68c3af97530ff3316c953e96c2d4efc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++#![warn(clippy::builtin_type_shadow)]
++#![allow(non_camel_case_types)]
++
++fn foo<u32>(a: u32) -> u32 {
++    42
++    // ^ rustc's type error
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b6a4adde8488403c24eebbaa81681463ad59f14f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++error: This generic shadows the built-in type `u32`
++  --> $DIR/builtin-type-shadow.rs:4:8
++   |
++LL | fn foo<u32>(a: u32) -> u32 {
++   |        ^^^
++   |
++   = note: `-D clippy::builtin-type-shadow` implied by `-D warnings`
++
++error[E0308]: mismatched types
++  --> $DIR/builtin-type-shadow.rs:5:5
++   |
++LL | fn foo<u32>(a: u32) -> u32 {
++   |        ---             --- expected `u32` because of return type
++   |        |
++   |        this type parameter
++LL |     42
++   |     ^^ expected type parameter `u32`, found integer
++   |
++   = note: expected type parameter `u32`
++                        found type `{integer}`
++   = help: type parameters must be constrained to match other types
++   = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
++
++error: aborting due to 2 previous errors
++
++For more information about this error, try `rustc --explain E0308`.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c724ee21be310b4a8d9d3376eeb36797304bcd65
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++#[deny(clippy::naive_bytecount)]
++fn main() {
++    let x = vec![0_u8; 16];
++
++    let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count
++
++    let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count
++
++    let _ = x.iter().filter(|a| **a > 0).count(); // not an equality count, OK.
++
++    let _ = x.iter().map(|a| a + 1).filter(|&a| a < 15).count(); // not a slice
++
++    let b = 0;
++
++    let _ = x.iter().filter(|_| b > 0).count(); // woah there
++
++    let _ = x.iter().filter(|_a| b == b + 1).count(); // nothing to see here, move along
++
++    let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count
++
++    let y = vec![0_u16; 3];
++
++    let _ = y.iter().filter(|&&a| a == 0).count(); // naive count, but not bytes
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..436f5d86a062760926b60ad31cf8ec8211bbed06
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++error: You appear to be counting bytes the naive way
++  --> $DIR/bytecount.rs:5:13
++   |
++LL |     let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, 0)`
++   |
++note: the lint level is defined here
++  --> $DIR/bytecount.rs:1:8
++   |
++LL | #[deny(clippy::naive_bytecount)]
++   |        ^^^^^^^^^^^^^^^^^^^^^^^
++
++error: You appear to be counting bytes the naive way
++  --> $DIR/bytecount.rs:7:13
++   |
++LL |     let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count((&x[..]), 0)`
++
++error: You appear to be counting bytes the naive way
++  --> $DIR/bytecount.rs:19:13
++   |
++LL |     let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, b + 1)`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7e0b211d862ca907a5fe1b3c9e3dd9bd07015b0c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,95 @@@
++#[warn(
++    clippy::cast_precision_loss,
++    clippy::cast_possible_truncation,
++    clippy::cast_sign_loss,
++    clippy::cast_possible_wrap
++)]
++#[allow(clippy::no_effect, clippy::unnecessary_operation)]
++fn main() {
++    // Test clippy::cast_precision_loss
++    let x0 = 1i32;
++    x0 as f32;
++    let x1 = 1i64;
++    x1 as f32;
++    x1 as f64;
++    let x2 = 1u32;
++    x2 as f32;
++    let x3 = 1u64;
++    x3 as f32;
++    x3 as f64;
++    // Test clippy::cast_possible_truncation
++    1f32 as i32;
++    1f32 as u32;
++    1f64 as f32;
++    1i32 as i8;
++    1i32 as u8;
++    1f64 as isize;
++    1f64 as usize;
++    // Test clippy::cast_possible_wrap
++    1u8 as i8;
++    1u16 as i16;
++    1u32 as i32;
++    1u64 as i64;
++    1usize as isize;
++    // Test clippy::cast_sign_loss
++    1i32 as u32;
++    -1i32 as u32;
++    1isize as usize;
++    -1isize as usize;
++    0i8 as u8;
++    i8::max_value() as u8;
++    i16::max_value() as u16;
++    i32::max_value() as u32;
++    i64::max_value() as u64;
++    i128::max_value() as u128;
++
++    (-1i8).abs() as u8;
++    (-1i16).abs() as u16;
++    (-1i32).abs() as u32;
++    (-1i64).abs() as u64;
++    (-1isize).abs() as usize;
++
++    (-1i8).checked_abs().unwrap() as u8;
++    (-1i16).checked_abs().unwrap() as u16;
++    (-1i32).checked_abs().unwrap() as u32;
++    (-1i64).checked_abs().unwrap() as u64;
++    (-1isize).checked_abs().unwrap() as usize;
++
++    (-1i8).rem_euclid(1i8) as u8;
++    (-1i8).rem_euclid(1i8) as u16;
++    (-1i16).rem_euclid(1i16) as u16;
++    (-1i16).rem_euclid(1i16) as u32;
++    (-1i32).rem_euclid(1i32) as u32;
++    (-1i32).rem_euclid(1i32) as u64;
++    (-1i64).rem_euclid(1i64) as u64;
++    (-1i64).rem_euclid(1i64) as u128;
++    (-1isize).rem_euclid(1isize) as usize;
++    (1i8).rem_euclid(-1i8) as u8;
++    (1i8).rem_euclid(-1i8) as u16;
++    (1i16).rem_euclid(-1i16) as u16;
++    (1i16).rem_euclid(-1i16) as u32;
++    (1i32).rem_euclid(-1i32) as u32;
++    (1i32).rem_euclid(-1i32) as u64;
++    (1i64).rem_euclid(-1i64) as u64;
++    (1i64).rem_euclid(-1i64) as u128;
++    (1isize).rem_euclid(-1isize) as usize;
++
++    (-1i8).checked_rem_euclid(1i8).unwrap() as u8;
++    (-1i8).checked_rem_euclid(1i8).unwrap() as u16;
++    (-1i16).checked_rem_euclid(1i16).unwrap() as u16;
++    (-1i16).checked_rem_euclid(1i16).unwrap() as u32;
++    (-1i32).checked_rem_euclid(1i32).unwrap() as u32;
++    (-1i32).checked_rem_euclid(1i32).unwrap() as u64;
++    (-1i64).checked_rem_euclid(1i64).unwrap() as u64;
++    (-1i64).checked_rem_euclid(1i64).unwrap() as u128;
++    (-1isize).checked_rem_euclid(1isize).unwrap() as usize;
++    (1i8).checked_rem_euclid(-1i8).unwrap() as u8;
++    (1i8).checked_rem_euclid(-1i8).unwrap() as u16;
++    (1i16).checked_rem_euclid(-1i16).unwrap() as u16;
++    (1i16).checked_rem_euclid(-1i16).unwrap() as u32;
++    (1i32).checked_rem_euclid(-1i32).unwrap() as u32;
++    (1i32).checked_rem_euclid(-1i32).unwrap() as u64;
++    (1i64).checked_rem_euclid(-1i64).unwrap() as u64;
++    (1i64).checked_rem_euclid(-1i64).unwrap() as u128;
++    (1isize).checked_rem_euclid(-1isize).unwrap() as usize;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4c66d736494843955903b39f917ac5ee187f61d5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,142 @@@
++error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
++  --> $DIR/cast.rs:11:5
++   |
++LL |     x0 as f32;
++   |     ^^^^^^^^^
++   |
++   = note: `-D clippy::cast-precision-loss` implied by `-D warnings`
++
++error: casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
++  --> $DIR/cast.rs:13:5
++   |
++LL |     x1 as f32;
++   |     ^^^^^^^^^
++
++error: casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
++  --> $DIR/cast.rs:14:5
++   |
++LL |     x1 as f64;
++   |     ^^^^^^^^^
++
++error: casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
++  --> $DIR/cast.rs:16:5
++   |
++LL |     x2 as f32;
++   |     ^^^^^^^^^
++
++error: casting `u64` to `f32` causes a loss of precision (`u64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
++  --> $DIR/cast.rs:18:5
++   |
++LL |     x3 as f32;
++   |     ^^^^^^^^^
++
++error: casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
++  --> $DIR/cast.rs:19:5
++   |
++LL |     x3 as f64;
++   |     ^^^^^^^^^
++
++error: casting `f32` to `i32` may truncate the value
++  --> $DIR/cast.rs:21:5
++   |
++LL |     1f32 as i32;
++   |     ^^^^^^^^^^^
++   |
++   = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
++
++error: casting `f32` to `u32` may truncate the value
++  --> $DIR/cast.rs:22:5
++   |
++LL |     1f32 as u32;
++   |     ^^^^^^^^^^^
++
++error: casting `f32` to `u32` may lose the sign of the value
++  --> $DIR/cast.rs:22:5
++   |
++LL |     1f32 as u32;
++   |     ^^^^^^^^^^^
++   |
++   = note: `-D clippy::cast-sign-loss` implied by `-D warnings`
++
++error: casting `f64` to `f32` may truncate the value
++  --> $DIR/cast.rs:23:5
++   |
++LL |     1f64 as f32;
++   |     ^^^^^^^^^^^
++
++error: casting `i32` to `i8` may truncate the value
++  --> $DIR/cast.rs:24:5
++   |
++LL |     1i32 as i8;
++   |     ^^^^^^^^^^
++
++error: casting `i32` to `u8` may truncate the value
++  --> $DIR/cast.rs:25:5
++   |
++LL |     1i32 as u8;
++   |     ^^^^^^^^^^
++
++error: casting `f64` to `isize` may truncate the value
++  --> $DIR/cast.rs:26:5
++   |
++LL |     1f64 as isize;
++   |     ^^^^^^^^^^^^^
++
++error: casting `f64` to `usize` may truncate the value
++  --> $DIR/cast.rs:27:5
++   |
++LL |     1f64 as usize;
++   |     ^^^^^^^^^^^^^
++
++error: casting `f64` to `usize` may lose the sign of the value
++  --> $DIR/cast.rs:27:5
++   |
++LL |     1f64 as usize;
++   |     ^^^^^^^^^^^^^
++
++error: casting `u8` to `i8` may wrap around the value
++  --> $DIR/cast.rs:29:5
++   |
++LL |     1u8 as i8;
++   |     ^^^^^^^^^
++   |
++   = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
++
++error: casting `u16` to `i16` may wrap around the value
++  --> $DIR/cast.rs:30:5
++   |
++LL |     1u16 as i16;
++   |     ^^^^^^^^^^^
++
++error: casting `u32` to `i32` may wrap around the value
++  --> $DIR/cast.rs:31:5
++   |
++LL |     1u32 as i32;
++   |     ^^^^^^^^^^^
++
++error: casting `u64` to `i64` may wrap around the value
++  --> $DIR/cast.rs:32:5
++   |
++LL |     1u64 as i64;
++   |     ^^^^^^^^^^^
++
++error: casting `usize` to `isize` may wrap around the value
++  --> $DIR/cast.rs:33:5
++   |
++LL |     1usize as isize;
++   |     ^^^^^^^^^^^^^^^
++
++error: casting `i32` to `u32` may lose the sign of the value
++  --> $DIR/cast.rs:36:5
++   |
++LL |     -1i32 as u32;
++   |     ^^^^^^^^^^^^
++
++error: casting `isize` to `usize` may lose the sign of the value
++  --> $DIR/cast.rs:38:5
++   |
++LL |     -1isize as usize;
++   |     ^^^^^^^^^^^^^^^^
++
++error: aborting due to 22 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4c08935639f1f3a5ae5fd847de7c3398ae71b31e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++//! Test casts for alignment issues
++
++#![feature(rustc_private)]
++extern crate libc;
++
++#[warn(clippy::cast_ptr_alignment)]
++#[allow(clippy::no_effect, clippy::unnecessary_operation, clippy::cast_lossless)]
++fn main() {
++    /* These should be warned against */
++
++    // cast to more-strictly-aligned type
++    (&1u8 as *const u8) as *const u16;
++    (&mut 1u8 as *mut u8) as *mut u16;
++
++    /* These should be ok */
++
++    // not a pointer type
++    1u8 as u16;
++    // cast to less-strictly-aligned type
++    (&1u16 as *const u16) as *const u8;
++    (&mut 1u16 as *mut u16) as *mut u8;
++    // For c_void, we should trust the user. See #2677
++    (&1u32 as *const u32 as *const std::os::raw::c_void) as *const u32;
++    (&1u32 as *const u32 as *const libc::c_void) as *const u32;
++    // For ZST, we should trust the user. See #4256
++    (&1u32 as *const u32 as *const ()) as *const u32;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..79219f86155a4458fe95c46dbc9b699b0738bca1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes)
++  --> $DIR/cast_alignment.rs:12:5
++   |
++LL |     (&1u8 as *const u8) as *const u16;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::cast-ptr-alignment` implied by `-D warnings`
++
++error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes)
++  --> $DIR/cast_alignment.rs:13:5
++   |
++LL |     (&mut 1u8 as *mut u8) as *mut u16;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..709d58b596c83ad7de53cdc40d43fc73c58fe59b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++// run-rustfix
++
++#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)]
++#![warn(clippy::cast_lossless)]
++
++fn main() {
++    // Test clippy::cast_lossless with casts to floating-point types
++    let x0 = 1i8;
++    f32::from(x0);
++    f64::from(x0);
++    let x1 = 1u8;
++    f32::from(x1);
++    f64::from(x1);
++    let x2 = 1i16;
++    f32::from(x2);
++    f64::from(x2);
++    let x3 = 1u16;
++    f32::from(x3);
++    f64::from(x3);
++    let x4 = 1i32;
++    f64::from(x4);
++    let x5 = 1u32;
++    f64::from(x5);
++
++    // Test with casts from floating-point types
++    f64::from(1.0f32);
++}
++
++// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const,
++// so we skip the lint if the expression is in a const fn.
++// See #3656
++const fn abc(input: f32) -> f64 {
++    input as f64
++}
++
++// Same as the above issue. We can't suggest `::from` in const fns in impls
++mod cast_lossless_in_impl {
++    struct A;
++
++    impl A {
++        pub const fn convert(x: f32) -> f64 {
++            x as f64
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eb0aab8864290978ee6505e616f3a7050d731a1c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++// run-rustfix
++
++#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)]
++#![warn(clippy::cast_lossless)]
++
++fn main() {
++    // Test clippy::cast_lossless with casts to floating-point types
++    let x0 = 1i8;
++    x0 as f32;
++    x0 as f64;
++    let x1 = 1u8;
++    x1 as f32;
++    x1 as f64;
++    let x2 = 1i16;
++    x2 as f32;
++    x2 as f64;
++    let x3 = 1u16;
++    x3 as f32;
++    x3 as f64;
++    let x4 = 1i32;
++    x4 as f64;
++    let x5 = 1u32;
++    x5 as f64;
++
++    // Test with casts from floating-point types
++    1.0f32 as f64;
++}
++
++// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const,
++// so we skip the lint if the expression is in a const fn.
++// See #3656
++const fn abc(input: f32) -> f64 {
++    input as f64
++}
++
++// Same as the above issue. We can't suggest `::from` in const fns in impls
++mod cast_lossless_in_impl {
++    struct A;
++
++    impl A {
++        pub const fn convert(x: f32) -> f64 {
++            x as f64
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0ed09f3083c285687efcd53a7afc0fcb920903c4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,70 @@@
++error: casting `i8` to `f32` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_float.rs:9:5
++   |
++LL |     x0 as f32;
++   |     ^^^^^^^^^ help: try: `f32::from(x0)`
++   |
++   = note: `-D clippy::cast-lossless` implied by `-D warnings`
++
++error: casting `i8` to `f64` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_float.rs:10:5
++   |
++LL |     x0 as f64;
++   |     ^^^^^^^^^ help: try: `f64::from(x0)`
++
++error: casting `u8` to `f32` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_float.rs:12:5
++   |
++LL |     x1 as f32;
++   |     ^^^^^^^^^ help: try: `f32::from(x1)`
++
++error: casting `u8` to `f64` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_float.rs:13:5
++   |
++LL |     x1 as f64;
++   |     ^^^^^^^^^ help: try: `f64::from(x1)`
++
++error: casting `i16` to `f32` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_float.rs:15:5
++   |
++LL |     x2 as f32;
++   |     ^^^^^^^^^ help: try: `f32::from(x2)`
++
++error: casting `i16` to `f64` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_float.rs:16:5
++   |
++LL |     x2 as f64;
++   |     ^^^^^^^^^ help: try: `f64::from(x2)`
++
++error: casting `u16` to `f32` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_float.rs:18:5
++   |
++LL |     x3 as f32;
++   |     ^^^^^^^^^ help: try: `f32::from(x3)`
++
++error: casting `u16` to `f64` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_float.rs:19:5
++   |
++LL |     x3 as f64;
++   |     ^^^^^^^^^ help: try: `f64::from(x3)`
++
++error: casting `i32` to `f64` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_float.rs:21:5
++   |
++LL |     x4 as f64;
++   |     ^^^^^^^^^ help: try: `f64::from(x4)`
++
++error: casting `u32` to `f64` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_float.rs:23:5
++   |
++LL |     x5 as f64;
++   |     ^^^^^^^^^ help: try: `f64::from(x5)`
++
++error: casting `f32` to `f64` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_float.rs:26:5
++   |
++LL |     1.0f32 as f64;
++   |     ^^^^^^^^^^^^^ help: try: `f64::from(1.0f32)`
++
++error: aborting due to 11 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..03e49adb117db00d9bce74596e78d20e04f3cf11
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++// run-rustfix
++
++#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)]
++#![warn(clippy::cast_lossless)]
++
++fn main() {
++    // Test clippy::cast_lossless with casts to integer types
++    i16::from(1i8);
++    i32::from(1i8);
++    i64::from(1i8);
++    i16::from(1u8);
++    i32::from(1u8);
++    i64::from(1u8);
++    u16::from(1u8);
++    u32::from(1u8);
++    u64::from(1u8);
++    i32::from(1i16);
++    i64::from(1i16);
++    i32::from(1u16);
++    i64::from(1u16);
++    u32::from(1u16);
++    u64::from(1u16);
++    i64::from(1i32);
++    i64::from(1u32);
++    u64::from(1u32);
++
++    // Test with an expression wrapped in parens
++    u16::from(1u8 + 1u8);
++}
++
++// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const,
++// so we skip the lint if the expression is in a const fn.
++// See #3656
++const fn abc(input: u16) -> u32 {
++    input as u32
++}
++
++// Same as the above issue. We can't suggest `::from` in const fns in impls
++mod cast_lossless_in_impl {
++    struct A;
++
++    impl A {
++        pub const fn convert(x: u32) -> u64 {
++            x as u64
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6a984d245963f761bcca28ca73e03ec748e9a2bf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++// run-rustfix
++
++#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)]
++#![warn(clippy::cast_lossless)]
++
++fn main() {
++    // Test clippy::cast_lossless with casts to integer types
++    1i8 as i16;
++    1i8 as i32;
++    1i8 as i64;
++    1u8 as i16;
++    1u8 as i32;
++    1u8 as i64;
++    1u8 as u16;
++    1u8 as u32;
++    1u8 as u64;
++    1i16 as i32;
++    1i16 as i64;
++    1u16 as i32;
++    1u16 as i64;
++    1u16 as u32;
++    1u16 as u64;
++    1i32 as i64;
++    1u32 as i64;
++    1u32 as u64;
++
++    // Test with an expression wrapped in parens
++    (1u8 + 1u8) as u16;
++}
++
++// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const,
++// so we skip the lint if the expression is in a const fn.
++// See #3656
++const fn abc(input: u16) -> u32 {
++    input as u32
++}
++
++// Same as the above issue. We can't suggest `::from` in const fns in impls
++mod cast_lossless_in_impl {
++    struct A;
++
++    impl A {
++        pub const fn convert(x: u32) -> u64 {
++            x as u64
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8e2890f9c28d02a6c6d87b25590c5c13c34c4e5a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,118 @@@
++error: casting `i8` to `i16` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:8:5
++   |
++LL |     1i8 as i16;
++   |     ^^^^^^^^^^ help: try: `i16::from(1i8)`
++   |
++   = note: `-D clippy::cast-lossless` implied by `-D warnings`
++
++error: casting `i8` to `i32` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:9:5
++   |
++LL |     1i8 as i32;
++   |     ^^^^^^^^^^ help: try: `i32::from(1i8)`
++
++error: casting `i8` to `i64` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:10:5
++   |
++LL |     1i8 as i64;
++   |     ^^^^^^^^^^ help: try: `i64::from(1i8)`
++
++error: casting `u8` to `i16` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:11:5
++   |
++LL |     1u8 as i16;
++   |     ^^^^^^^^^^ help: try: `i16::from(1u8)`
++
++error: casting `u8` to `i32` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:12:5
++   |
++LL |     1u8 as i32;
++   |     ^^^^^^^^^^ help: try: `i32::from(1u8)`
++
++error: casting `u8` to `i64` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:13:5
++   |
++LL |     1u8 as i64;
++   |     ^^^^^^^^^^ help: try: `i64::from(1u8)`
++
++error: casting `u8` to `u16` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:14:5
++   |
++LL |     1u8 as u16;
++   |     ^^^^^^^^^^ help: try: `u16::from(1u8)`
++
++error: casting `u8` to `u32` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:15:5
++   |
++LL |     1u8 as u32;
++   |     ^^^^^^^^^^ help: try: `u32::from(1u8)`
++
++error: casting `u8` to `u64` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:16:5
++   |
++LL |     1u8 as u64;
++   |     ^^^^^^^^^^ help: try: `u64::from(1u8)`
++
++error: casting `i16` to `i32` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:17:5
++   |
++LL |     1i16 as i32;
++   |     ^^^^^^^^^^^ help: try: `i32::from(1i16)`
++
++error: casting `i16` to `i64` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:18:5
++   |
++LL |     1i16 as i64;
++   |     ^^^^^^^^^^^ help: try: `i64::from(1i16)`
++
++error: casting `u16` to `i32` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:19:5
++   |
++LL |     1u16 as i32;
++   |     ^^^^^^^^^^^ help: try: `i32::from(1u16)`
++
++error: casting `u16` to `i64` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:20:5
++   |
++LL |     1u16 as i64;
++   |     ^^^^^^^^^^^ help: try: `i64::from(1u16)`
++
++error: casting `u16` to `u32` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:21:5
++   |
++LL |     1u16 as u32;
++   |     ^^^^^^^^^^^ help: try: `u32::from(1u16)`
++
++error: casting `u16` to `u64` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:22:5
++   |
++LL |     1u16 as u64;
++   |     ^^^^^^^^^^^ help: try: `u64::from(1u16)`
++
++error: casting `i32` to `i64` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:23:5
++   |
++LL |     1i32 as i64;
++   |     ^^^^^^^^^^^ help: try: `i64::from(1i32)`
++
++error: casting `u32` to `i64` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:24:5
++   |
++LL |     1u32 as i64;
++   |     ^^^^^^^^^^^ help: try: `i64::from(1u32)`
++
++error: casting `u32` to `u64` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:25:5
++   |
++LL |     1u32 as u64;
++   |     ^^^^^^^^^^^ help: try: `u64::from(1u32)`
++
++error: casting `u8` to `u16` may become silently lossy if you later change the type
++  --> $DIR/cast_lossless_integer.rs:28:5
++   |
++LL |     (1u8 + 1u8) as u16;
++   |     ^^^^^^^^^^^^^^^^^^ help: try: `u16::from(1u8 + 1u8)`
++
++error: aborting due to 19 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..089e5cfabe4b912fb6b819fa334d6476862a8455
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++#![warn(clippy::cast_ref_to_mut)]
++#![allow(clippy::no_effect)]
++
++extern "C" {
++    // N.B., mutability can be easily incorrect in FFI calls -- as
++    // in C, the default is mutable pointers.
++    fn ffi(c: *mut u8);
++    fn int_ffi(c: *mut i32);
++}
++
++fn main() {
++    let s = String::from("Hello");
++    let a = &s;
++    unsafe {
++        let num = &3i32;
++        let mut_num = &mut 3i32;
++        // Should be warned against
++        (*(a as *const _ as *mut String)).push_str(" world");
++        *(a as *const _ as *mut _) = String::from("Replaced");
++        *(a as *const _ as *mut String) += " world";
++        // Shouldn't be warned against
++        println!("{}", *(num as *const _ as *const i16));
++        println!("{}", *(mut_num as *mut _ as *mut i16));
++        ffi(a.as_ptr() as *mut _);
++        int_ffi(num as *const _ as *mut _);
++        int_ffi(&3 as *const _ as *mut _);
++        let mut value = 3;
++        let value: *const i32 = &mut value;
++        *(value as *const i16 as *mut i16) = 42;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aacd99437d9fc3eadde5b8a60a5b0a11e9de8b30
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`
++  --> $DIR/cast_ref_to_mut.rs:18:9
++   |
++LL |         (*(a as *const _ as *mut String)).push_str(" world");
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::cast-ref-to-mut` implied by `-D warnings`
++
++error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`
++  --> $DIR/cast_ref_to_mut.rs:19:9
++   |
++LL |         *(a as *const _ as *mut _) = String::from("Replaced");
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`
++  --> $DIR/cast_ref_to_mut.rs:20:9
++   |
++LL |         *(a as *const _ as *mut String) += " world";
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..595109be46bb878f3ce9b4e94c0223c4fb7c72f1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++// ignore-32bit
++#[warn(
++    clippy::cast_precision_loss,
++    clippy::cast_possible_truncation,
++    clippy::cast_sign_loss,
++    clippy::cast_possible_wrap,
++    clippy::cast_lossless
++)]
++#[allow(clippy::no_effect, clippy::unnecessary_operation)]
++fn main() {
++    // Casting from *size
++    1isize as i8;
++    let x0 = 1isize;
++    let x1 = 1usize;
++    x0 as f64;
++    x1 as f64;
++    x0 as f32;
++    x1 as f32;
++    1isize as i32;
++    1isize as u32;
++    1usize as u32;
++    1usize as i32;
++    // Casting to *size
++    1i64 as isize;
++    1i64 as usize;
++    1u64 as isize;
++    1u64 as usize;
++    1u32 as isize;
++    1u32 as usize; // Should not trigger any lint
++    1i32 as isize; // Neither should this
++    1i32 as usize;
++    // Big integer literal to float
++    999_999_999 as f32;
++    9_999_999_999_999_999usize as f64;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..95552f2e285396dd05db7fb3b6993d9113ee408c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,116 @@@
++error: casting `isize` to `i8` may truncate the value
++  --> $DIR/cast_size.rs:12:5
++   |
++LL |     1isize as i8;
++   |     ^^^^^^^^^^^^
++   |
++   = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
++
++error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
++  --> $DIR/cast_size.rs:15:5
++   |
++LL |     x0 as f64;
++   |     ^^^^^^^^^
++   |
++   = note: `-D clippy::cast-precision-loss` implied by `-D warnings`
++
++error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
++  --> $DIR/cast_size.rs:16:5
++   |
++LL |     x1 as f64;
++   |     ^^^^^^^^^
++
++error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
++  --> $DIR/cast_size.rs:17:5
++   |
++LL |     x0 as f32;
++   |     ^^^^^^^^^
++
++error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
++  --> $DIR/cast_size.rs:18:5
++   |
++LL |     x1 as f32;
++   |     ^^^^^^^^^
++
++error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers
++  --> $DIR/cast_size.rs:19:5
++   |
++LL |     1isize as i32;
++   |     ^^^^^^^^^^^^^
++
++error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers
++  --> $DIR/cast_size.rs:20:5
++   |
++LL |     1isize as u32;
++   |     ^^^^^^^^^^^^^
++
++error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers
++  --> $DIR/cast_size.rs:21:5
++   |
++LL |     1usize as u32;
++   |     ^^^^^^^^^^^^^
++
++error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers
++  --> $DIR/cast_size.rs:22:5
++   |
++LL |     1usize as i32;
++   |     ^^^^^^^^^^^^^
++
++error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers
++  --> $DIR/cast_size.rs:22:5
++   |
++LL |     1usize as i32;
++   |     ^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
++
++error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers
++  --> $DIR/cast_size.rs:24:5
++   |
++LL |     1i64 as isize;
++   |     ^^^^^^^^^^^^^
++
++error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers
++  --> $DIR/cast_size.rs:25:5
++   |
++LL |     1i64 as usize;
++   |     ^^^^^^^^^^^^^
++
++error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers
++  --> $DIR/cast_size.rs:26:5
++   |
++LL |     1u64 as isize;
++   |     ^^^^^^^^^^^^^
++
++error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers
++  --> $DIR/cast_size.rs:26:5
++   |
++LL |     1u64 as isize;
++   |     ^^^^^^^^^^^^^
++
++error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers
++  --> $DIR/cast_size.rs:27:5
++   |
++LL |     1u64 as usize;
++   |     ^^^^^^^^^^^^^
++
++error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers
++  --> $DIR/cast_size.rs:28:5
++   |
++LL |     1u32 as isize;
++   |     ^^^^^^^^^^^^^
++
++error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
++  --> $DIR/cast_size.rs:33:5
++   |
++LL |     999_999_999 as f32;
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
++  --> $DIR/cast_size.rs:34:5
++   |
++LL |     9_999_999_999_999_999usize as f64;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 18 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..99aac6deca3240248356da5f3cc903cb2477178c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++// ignore-64bit
++#[warn(
++    clippy::cast_precision_loss,
++    clippy::cast_possible_truncation,
++    clippy::cast_sign_loss,
++    clippy::cast_possible_wrap,
++    clippy::cast_lossless
++)]
++#[allow(clippy::no_effect, clippy::unnecessary_operation)]
++fn main() {
++    // Casting from *size
++    1isize as i8;
++    let x0 = 1isize;
++    let x1 = 1usize;
++    x0 as f64;
++    x1 as f64;
++    x0 as f32;
++    x1 as f32;
++    1isize as i32;
++    1isize as u32;
++    1usize as u32;
++    1usize as i32;
++    // Casting to *size
++    1i64 as isize;
++    1i64 as usize;
++    1u64 as isize;
++    1u64 as usize;
++    1u32 as isize;
++    1u32 as usize; // Should not trigger any lint
++    1i32 as isize; // Neither should this
++    1i32 as usize;
++    // Big integer literal to float
++    999_999_999 as f32;
++    3_999_999_999usize as f64;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2eec51895f59fc3e4f2eae1f2d346c9adde55d6b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,132 @@@
++error: casting `isize` to `i8` may truncate the value
++  --> $DIR/cast_size_32bit.rs:12:5
++   |
++LL |     1isize as i8;
++   |     ^^^^^^^^^^^^
++   |
++   = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
++
++error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
++  --> $DIR/cast_size_32bit.rs:15:5
++   |
++LL |     x0 as f64;
++   |     ^^^^^^^^^
++   |
++   = note: `-D clippy::cast-precision-loss` implied by `-D warnings`
++
++error: casting `isize` to `f64` may become silently lossy if you later change the type
++  --> $DIR/cast_size_32bit.rs:15:5
++   |
++LL |     x0 as f64;
++   |     ^^^^^^^^^ help: try: `f64::from(x0)`
++   |
++   = note: `-D clippy::cast-lossless` implied by `-D warnings`
++
++error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
++  --> $DIR/cast_size_32bit.rs:16:5
++   |
++LL |     x1 as f64;
++   |     ^^^^^^^^^
++
++error: casting `usize` to `f64` may become silently lossy if you later change the type
++  --> $DIR/cast_size_32bit.rs:16:5
++   |
++LL |     x1 as f64;
++   |     ^^^^^^^^^ help: try: `f64::from(x1)`
++
++error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
++  --> $DIR/cast_size_32bit.rs:17:5
++   |
++LL |     x0 as f32;
++   |     ^^^^^^^^^
++
++error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
++  --> $DIR/cast_size_32bit.rs:18:5
++   |
++LL |     x1 as f32;
++   |     ^^^^^^^^^
++
++error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers
++  --> $DIR/cast_size_32bit.rs:19:5
++   |
++LL |     1isize as i32;
++   |     ^^^^^^^^^^^^^
++
++error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers
++  --> $DIR/cast_size_32bit.rs:20:5
++   |
++LL |     1isize as u32;
++   |     ^^^^^^^^^^^^^
++
++error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers
++  --> $DIR/cast_size_32bit.rs:21:5
++   |
++LL |     1usize as u32;
++   |     ^^^^^^^^^^^^^
++
++error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers
++  --> $DIR/cast_size_32bit.rs:22:5
++   |
++LL |     1usize as i32;
++   |     ^^^^^^^^^^^^^
++
++error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers
++  --> $DIR/cast_size_32bit.rs:22:5
++   |
++LL |     1usize as i32;
++   |     ^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
++
++error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers
++  --> $DIR/cast_size_32bit.rs:24:5
++   |
++LL |     1i64 as isize;
++   |     ^^^^^^^^^^^^^
++
++error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers
++  --> $DIR/cast_size_32bit.rs:25:5
++   |
++LL |     1i64 as usize;
++   |     ^^^^^^^^^^^^^
++
++error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers
++  --> $DIR/cast_size_32bit.rs:26:5
++   |
++LL |     1u64 as isize;
++   |     ^^^^^^^^^^^^^
++
++error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers
++  --> $DIR/cast_size_32bit.rs:26:5
++   |
++LL |     1u64 as isize;
++   |     ^^^^^^^^^^^^^
++
++error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers
++  --> $DIR/cast_size_32bit.rs:27:5
++   |
++LL |     1u64 as usize;
++   |     ^^^^^^^^^^^^^
++
++error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers
++  --> $DIR/cast_size_32bit.rs:28:5
++   |
++LL |     1u32 as isize;
++   |     ^^^^^^^^^^^^^
++
++error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
++  --> $DIR/cast_size_32bit.rs:33:5
++   |
++LL |     999_999_999 as f32;
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: casting integer literal to `f64` is unnecessary
++  --> $DIR/cast_size_32bit.rs:34:5
++   |
++LL |     3_999_999_999usize as f64;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3999999999_f64`
++   |
++   = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
++
++error: aborting due to 20 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4e583a25b94c2a517555585caf76f5631e263847
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++// run-rustfix
++#![feature(stmt_expr_attributes)]
++
++#![allow(unused, clippy::no_effect)]
++#![warn(clippy::deprecated_cfg_attr)]
++
++// This doesn't get linted, see known problems
++#![cfg_attr(rustfmt, rustfmt_skip)]
++
++#[rustfmt::skip]
++trait Foo
++{
++fn foo(
++);
++}
++
++fn skip_on_statements() {
++    #[rustfmt::skip]
++    5+3;
++}
++
++#[rustfmt::skip]
++fn main() {
++    foo::f();
++}
++
++mod foo {
++    #![cfg_attr(rustfmt, rustfmt_skip)]
++
++    pub fn f() {}
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9c0fcf6fb454c4ac00a89b8bcb1b5b34aa58e2cf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++// run-rustfix
++#![feature(stmt_expr_attributes)]
++
++#![allow(unused, clippy::no_effect)]
++#![warn(clippy::deprecated_cfg_attr)]
++
++// This doesn't get linted, see known problems
++#![cfg_attr(rustfmt, rustfmt_skip)]
++
++#[rustfmt::skip]
++trait Foo
++{
++fn foo(
++);
++}
++
++fn skip_on_statements() {
++    #[cfg_attr(rustfmt, rustfmt::skip)]
++    5+3;
++}
++
++#[cfg_attr(rustfmt, rustfmt_skip)]
++fn main() {
++    foo::f();
++}
++
++mod foo {
++    #![cfg_attr(rustfmt, rustfmt_skip)]
++
++    pub fn f() {}
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c1efd47db90b0f313132e0a0465c5cf2826672e2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes
++  --> $DIR/cfg_attr_rustfmt.rs:18:5
++   |
++LL |     #[cfg_attr(rustfmt, rustfmt::skip)]
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]`
++   |
++   = note: `-D clippy::deprecated-cfg-attr` implied by `-D warnings`
++
++error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes
++  --> $DIR/cfg_attr_rustfmt.rs:22:1
++   |
++LL | #[cfg_attr(rustfmt, rustfmt_skip)]
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0a53a3d6490a3d8ade83eb4de080af0da6fc31ba
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++#![warn(clippy::char_lit_as_u8)]
++
++fn main() {
++    let _ = '❤' as u8; // no suggestion, since a byte literal won't work.
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b9836d2f25532d5806663ac9c865fd6430fe867a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++error: casting a character literal to `u8` truncates
++  --> $DIR/char_lit_as_u8.rs:4:13
++   |
++LL |     let _ = '❤' as u8; // no suggestion, since a byte literal won't work.
++   |             ^^^^^^^^^
++   |
++   = note: `-D clippy::char-lit-as-u8` implied by `-D warnings`
++   = note: `char` is four bytes wide, but `u8` is a single byte
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3dc3cb4e7573dbb3147ac20f4a876c655da51a5d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++// run-rustfix
++
++#![warn(clippy::char_lit_as_u8)]
++
++fn main() {
++    let _ = b'a';
++    let _ = b'\n';
++    let _ = b'\0';
++    let _ = b'\x01';
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d379a0234942ab46864a666ac5c4237a498db9ca
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++// run-rustfix
++
++#![warn(clippy::char_lit_as_u8)]
++
++fn main() {
++    let _ = 'a' as u8;
++    let _ = '\n' as u8;
++    let _ = '\0' as u8;
++    let _ = '\x01' as u8;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bf7cb1607b4e208cbf7d633d2a29e068b2269f3d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++error: casting a character literal to `u8` truncates
++  --> $DIR/char_lit_as_u8_suggestions.rs:6:13
++   |
++LL |     let _ = 'a' as u8;
++   |             ^^^^^^^^^ help: use a byte literal instead: `b'a'`
++   |
++   = note: `-D clippy::char-lit-as-u8` implied by `-D warnings`
++   = note: `char` is four bytes wide, but `u8` is a single byte
++
++error: casting a character literal to `u8` truncates
++  --> $DIR/char_lit_as_u8_suggestions.rs:7:13
++   |
++LL |     let _ = '/n' as u8;
++   |             ^^^^^^^^^^ help: use a byte literal instead: `b'/n'`
++   |
++   = note: `char` is four bytes wide, but `u8` is a single byte
++
++error: casting a character literal to `u8` truncates
++  --> $DIR/char_lit_as_u8_suggestions.rs:8:13
++   |
++LL |     let _ = '/0' as u8;
++   |             ^^^^^^^^^^ help: use a byte literal instead: `b'/0'`
++   |
++   = note: `char` is four bytes wide, but `u8` is a single byte
++
++error: casting a character literal to `u8` truncates
++  --> $DIR/char_lit_as_u8_suggestions.rs:9:13
++   |
++LL |     let _ = '/x01' as u8;
++   |             ^^^^^^^^^^^^ help: use a byte literal instead: `b'/x01'`
++   |
++   = note: `char` is four bytes wide, but `u8` is a single byte
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7febd6f376135884d31ee80143de3d8eec941ef1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,106 @@@
++// run-rustfix
++
++#![warn(clippy::checked_conversions)]
++#![allow(clippy::cast_lossless)]
++#![allow(dead_code)]
++use std::convert::TryFrom;
++
++// Positive tests
++
++// Signed to unsigned
++
++fn i64_to_u32(value: i64) -> Option<u32> {
++    if u32::try_from(value).is_ok() {
++        Some(value as u32)
++    } else {
++        None
++    }
++}
++
++fn i64_to_u16(value: i64) -> Option<u16> {
++    if u16::try_from(value).is_ok() {
++        Some(value as u16)
++    } else {
++        None
++    }
++}
++
++fn isize_to_u8(value: isize) -> Option<u8> {
++    if u8::try_from(value).is_ok() {
++        Some(value as u8)
++    } else {
++        None
++    }
++}
++
++// Signed to signed
++
++fn i64_to_i32(value: i64) -> Option<i32> {
++    if i32::try_from(value).is_ok() {
++        Some(value as i32)
++    } else {
++        None
++    }
++}
++
++fn i64_to_i16(value: i64) -> Option<i16> {
++    if i16::try_from(value).is_ok() {
++        Some(value as i16)
++    } else {
++        None
++    }
++}
++
++// Unsigned to X
++
++fn u32_to_i32(value: u32) -> Option<i32> {
++    if i32::try_from(value).is_ok() {
++        Some(value as i32)
++    } else {
++        None
++    }
++}
++
++fn usize_to_isize(value: usize) -> isize {
++    if isize::try_from(value).is_ok() && value as i32 == 5 {
++        5
++    } else {
++        1
++    }
++}
++
++fn u32_to_u16(value: u32) -> isize {
++    if u16::try_from(value).is_ok() && value as i32 == 5 {
++        5
++    } else {
++        1
++    }
++}
++
++// Negative tests
++
++fn no_i64_to_i32(value: i64) -> Option<i32> {
++    if value <= (i32::max_value() as i64) && value >= 0 {
++        Some(value as i32)
++    } else {
++        None
++    }
++}
++
++fn no_isize_to_u8(value: isize) -> Option<u8> {
++    if value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize) {
++        Some(value as u8)
++    } else {
++        None
++    }
++}
++
++fn i8_to_u8(value: i8) -> Option<u8> {
++    if value >= 0 {
++        Some(value as u8)
++    } else {
++        None
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a643354e2438f0ad71ffb2cf968c08da8e21dd10
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,106 @@@
++// run-rustfix
++
++#![warn(clippy::checked_conversions)]
++#![allow(clippy::cast_lossless)]
++#![allow(dead_code)]
++use std::convert::TryFrom;
++
++// Positive tests
++
++// Signed to unsigned
++
++fn i64_to_u32(value: i64) -> Option<u32> {
++    if value <= (u32::max_value() as i64) && value >= 0 {
++        Some(value as u32)
++    } else {
++        None
++    }
++}
++
++fn i64_to_u16(value: i64) -> Option<u16> {
++    if value <= i64::from(u16::max_value()) && value >= 0 {
++        Some(value as u16)
++    } else {
++        None
++    }
++}
++
++fn isize_to_u8(value: isize) -> Option<u8> {
++    if value <= (u8::max_value() as isize) && value >= 0 {
++        Some(value as u8)
++    } else {
++        None
++    }
++}
++
++// Signed to signed
++
++fn i64_to_i32(value: i64) -> Option<i32> {
++    if value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64) {
++        Some(value as i32)
++    } else {
++        None
++    }
++}
++
++fn i64_to_i16(value: i64) -> Option<i16> {
++    if value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()) {
++        Some(value as i16)
++    } else {
++        None
++    }
++}
++
++// Unsigned to X
++
++fn u32_to_i32(value: u32) -> Option<i32> {
++    if value <= i32::max_value() as u32 {
++        Some(value as i32)
++    } else {
++        None
++    }
++}
++
++fn usize_to_isize(value: usize) -> isize {
++    if value <= isize::max_value() as usize && value as i32 == 5 {
++        5
++    } else {
++        1
++    }
++}
++
++fn u32_to_u16(value: u32) -> isize {
++    if value <= u16::max_value() as u32 && value as i32 == 5 {
++        5
++    } else {
++        1
++    }
++}
++
++// Negative tests
++
++fn no_i64_to_i32(value: i64) -> Option<i32> {
++    if value <= (i32::max_value() as i64) && value >= 0 {
++        Some(value as i32)
++    } else {
++        None
++    }
++}
++
++fn no_isize_to_u8(value: isize) -> Option<u8> {
++    if value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize) {
++        Some(value as u8)
++    } else {
++        None
++    }
++}
++
++fn i8_to_u8(value: i8) -> Option<u8> {
++    if value >= 0 {
++        Some(value as u8)
++    } else {
++        None
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f678f009621f8f3dd5a9036ceb376190fd828042
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++error: Checked cast can be simplified.
++  --> $DIR/checked_conversions.rs:13:8
++   |
++LL |     if value <= (u32::max_value() as i64) && value >= 0 {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
++   |
++   = note: `-D clippy::checked-conversions` implied by `-D warnings`
++
++error: Checked cast can be simplified.
++  --> $DIR/checked_conversions.rs:21:8
++   |
++LL |     if value <= i64::from(u16::max_value()) && value >= 0 {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
++
++error: Checked cast can be simplified.
++  --> $DIR/checked_conversions.rs:29:8
++   |
++LL |     if value <= (u8::max_value() as isize) && value >= 0 {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
++
++error: Checked cast can be simplified.
++  --> $DIR/checked_conversions.rs:39:8
++   |
++LL |     if value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64) {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
++
++error: Checked cast can be simplified.
++  --> $DIR/checked_conversions.rs:47:8
++   |
++LL |     if value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()) {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
++
++error: Checked cast can be simplified.
++  --> $DIR/checked_conversions.rs:57:8
++   |
++LL |     if value <= i32::max_value() as u32 {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
++
++error: Checked cast can be simplified.
++  --> $DIR/checked_conversions.rs:65:8
++   |
++LL |     if value <= isize::max_value() as usize && value as i32 == 5 {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
++
++error: Checked cast can be simplified.
++  --> $DIR/checked_conversions.rs:73:8
++   |
++LL |     if value <= u16::max_value() as u32 && value as i32 == 5 {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c986c992a07a626f53ed7bc398f15d26824f4081
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,54 @@@
++#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++#![allow(clippy::if_same_then_else)]
++
++fn test_complex_conditions() {
++    let x: Result<(), ()> = Ok(());
++    let y: Result<(), ()> = Ok(());
++    if x.is_ok() && y.is_err() {
++        x.unwrap(); // unnecessary
++        x.unwrap_err(); // will panic
++        y.unwrap(); // will panic
++        y.unwrap_err(); // unnecessary
++    } else {
++        // not statically determinable whether any of the following will always succeed or always fail:
++        x.unwrap();
++        x.unwrap_err();
++        y.unwrap();
++        y.unwrap_err();
++    }
++
++    if x.is_ok() || y.is_ok() {
++        // not statically determinable whether any of the following will always succeed or always fail:
++        x.unwrap();
++        y.unwrap();
++    } else {
++        x.unwrap(); // will panic
++        x.unwrap_err(); // unnecessary
++        y.unwrap(); // will panic
++        y.unwrap_err(); // unnecessary
++    }
++    let z: Result<(), ()> = Ok(());
++    if x.is_ok() && !(y.is_ok() || z.is_err()) {
++        x.unwrap(); // unnecessary
++        x.unwrap_err(); // will panic
++        y.unwrap(); // will panic
++        y.unwrap_err(); // unnecessary
++        z.unwrap(); // unnecessary
++        z.unwrap_err(); // will panic
++    }
++    if x.is_ok() || !(y.is_ok() && z.is_err()) {
++        // not statically determinable whether any of the following will always succeed or always fail:
++        x.unwrap();
++        y.unwrap();
++        z.unwrap();
++    } else {
++        x.unwrap(); // will panic
++        x.unwrap_err(); // unnecessary
++        y.unwrap(); // unnecessary
++        y.unwrap_err(); // will panic
++        z.unwrap(); // will panic
++        z.unwrap_err(); // unnecessary
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dc666bab4603960fdb4928c1d05bf8495bb5e96e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,192 @@@
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/complex_conditionals.rs:8:9
++   |
++LL |     if x.is_ok() && y.is_err() {
++   |        --------- the check is happening here
++LL |         x.unwrap(); // unnecessary
++   |         ^^^^^^^^^^
++   |
++note: the lint level is defined here
++  --> $DIR/complex_conditionals.rs:1:35
++   |
++LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: This call to `unwrap_err()` will always panic.
++  --> $DIR/complex_conditionals.rs:9:9
++   |
++LL |     if x.is_ok() && y.is_err() {
++   |        --------- because of this check
++LL |         x.unwrap(); // unnecessary
++LL |         x.unwrap_err(); // will panic
++   |         ^^^^^^^^^^^^^^
++   |
++note: the lint level is defined here
++  --> $DIR/complex_conditionals.rs:1:9
++   |
++LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++  --> $DIR/complex_conditionals.rs:10:9
++   |
++LL |     if x.is_ok() && y.is_err() {
++   |                     ---------- because of this check
++...
++LL |         y.unwrap(); // will panic
++   |         ^^^^^^^^^^
++
++error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/complex_conditionals.rs:11:9
++   |
++LL |     if x.is_ok() && y.is_err() {
++   |                     ---------- the check is happening here
++...
++LL |         y.unwrap_err(); // unnecessary
++   |         ^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++  --> $DIR/complex_conditionals.rs:25:9
++   |
++LL |     if x.is_ok() || y.is_ok() {
++   |        --------- because of this check
++...
++LL |         x.unwrap(); // will panic
++   |         ^^^^^^^^^^
++
++error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/complex_conditionals.rs:26:9
++   |
++LL |     if x.is_ok() || y.is_ok() {
++   |        --------- the check is happening here
++...
++LL |         x.unwrap_err(); // unnecessary
++   |         ^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++  --> $DIR/complex_conditionals.rs:27:9
++   |
++LL |     if x.is_ok() || y.is_ok() {
++   |                     --------- because of this check
++...
++LL |         y.unwrap(); // will panic
++   |         ^^^^^^^^^^
++
++error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/complex_conditionals.rs:28:9
++   |
++LL |     if x.is_ok() || y.is_ok() {
++   |                     --------- the check is happening here
++...
++LL |         y.unwrap_err(); // unnecessary
++   |         ^^^^^^^^^^^^^^
++
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/complex_conditionals.rs:32:9
++   |
++LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
++   |        --------- the check is happening here
++LL |         x.unwrap(); // unnecessary
++   |         ^^^^^^^^^^
++
++error: This call to `unwrap_err()` will always panic.
++  --> $DIR/complex_conditionals.rs:33:9
++   |
++LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
++   |        --------- because of this check
++LL |         x.unwrap(); // unnecessary
++LL |         x.unwrap_err(); // will panic
++   |         ^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++  --> $DIR/complex_conditionals.rs:34:9
++   |
++LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
++   |                       --------- because of this check
++...
++LL |         y.unwrap(); // will panic
++   |         ^^^^^^^^^^
++
++error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/complex_conditionals.rs:35:9
++   |
++LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
++   |                       --------- the check is happening here
++...
++LL |         y.unwrap_err(); // unnecessary
++   |         ^^^^^^^^^^^^^^
++
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/complex_conditionals.rs:36:9
++   |
++LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
++   |                                    ---------- the check is happening here
++...
++LL |         z.unwrap(); // unnecessary
++   |         ^^^^^^^^^^
++
++error: This call to `unwrap_err()` will always panic.
++  --> $DIR/complex_conditionals.rs:37:9
++   |
++LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
++   |                                    ---------- because of this check
++...
++LL |         z.unwrap_err(); // will panic
++   |         ^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++  --> $DIR/complex_conditionals.rs:45:9
++   |
++LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
++   |        --------- because of this check
++...
++LL |         x.unwrap(); // will panic
++   |         ^^^^^^^^^^
++
++error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/complex_conditionals.rs:46:9
++   |
++LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
++   |        --------- the check is happening here
++...
++LL |         x.unwrap_err(); // unnecessary
++   |         ^^^^^^^^^^^^^^
++
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/complex_conditionals.rs:47:9
++   |
++LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
++   |                       --------- the check is happening here
++...
++LL |         y.unwrap(); // unnecessary
++   |         ^^^^^^^^^^
++
++error: This call to `unwrap_err()` will always panic.
++  --> $DIR/complex_conditionals.rs:48:9
++   |
++LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
++   |                       --------- because of this check
++...
++LL |         y.unwrap_err(); // will panic
++   |         ^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++  --> $DIR/complex_conditionals.rs:49:9
++   |
++LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
++   |                                    ---------- because of this check
++...
++LL |         z.unwrap(); // will panic
++   |         ^^^^^^^^^^
++
++error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/complex_conditionals.rs:50:9
++   |
++LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
++   |                                    ---------- the check is happening here
++...
++LL |         z.unwrap_err(); // unnecessary
++   |         ^^^^^^^^^^^^^^
++
++error: aborting due to 20 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2307996a48ffc62deef0f6d5e472d2cab5de9286
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++#![allow(clippy::if_same_then_else)]
++
++fn test_nested() {
++    fn nested() {
++        let x = Some(());
++        if x.is_some() {
++            x.unwrap(); // unnecessary
++        } else {
++            x.unwrap(); // will panic
++        }
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e4d085470c3b4200af404d3260974caca34ca731
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/complex_conditionals_nested.rs:8:13
++   |
++LL |         if x.is_some() {
++   |            ----------- the check is happening here
++LL |             x.unwrap(); // unnecessary
++   |             ^^^^^^^^^^
++   |
++note: the lint level is defined here
++  --> $DIR/complex_conditionals_nested.rs:1:35
++   |
++LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++  --> $DIR/complex_conditionals_nested.rs:10:13
++   |
++LL |         if x.is_some() {
++   |            ----------- because of this check
++...
++LL |             x.unwrap(); // will panic
++   |             ^^^^^^^^^^
++   |
++note: the lint level is defined here
++  --> $DIR/complex_conditionals_nested.rs:1:9
++   |
++LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b0fc26ff76de559b36b13ee2170f1607d93f1bf8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++#![allow(clippy::if_same_then_else)]
++
++macro_rules! m {
++    ($a:expr) => {
++        if $a.is_some() {
++            $a.unwrap(); // unnecessary
++        }
++    };
++}
++
++fn main() {
++    let x = Some(());
++    if x.is_some() {
++        x.unwrap(); // unnecessary
++    } else {
++        x.unwrap(); // will panic
++    }
++    if x.is_none() {
++        x.unwrap(); // will panic
++    } else {
++        x.unwrap(); // unnecessary
++    }
++    m!(x);
++    let mut x: Result<(), ()> = Ok(());
++    if x.is_ok() {
++        x.unwrap(); // unnecessary
++        x.unwrap_err(); // will panic
++    } else {
++        x.unwrap(); // will panic
++        x.unwrap_err(); // unnecessary
++    }
++    if x.is_err() {
++        x.unwrap(); // will panic
++        x.unwrap_err(); // unnecessary
++    } else {
++        x.unwrap(); // unnecessary
++        x.unwrap_err(); // will panic
++    }
++    if x.is_ok() {
++        x = Err(());
++        x.unwrap(); // not unnecessary because of mutation of x
++                    // it will always panic but the lint is not smart enough to see this (it only
++                    // checks if conditions).
++    } else {
++        x = Ok(());
++        x.unwrap_err(); // not unnecessary because of mutation of x
++                        // it will always panic but the lint is not smart enough to see this (it
++                        // only checks if conditions).
++    }
++
++    assert!(x.is_ok(), "{:?}", x.unwrap_err()); // ok, it's a common test pattern
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e40542e2e4f90ba47fd1bc788a97801941f86d1f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,131 @@@
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/simple_conditionals.rs:15:9
++   |
++LL |     if x.is_some() {
++   |        ----------- the check is happening here
++LL |         x.unwrap(); // unnecessary
++   |         ^^^^^^^^^^
++   |
++note: the lint level is defined here
++  --> $DIR/simple_conditionals.rs:1:35
++   |
++LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++  --> $DIR/simple_conditionals.rs:17:9
++   |
++LL |     if x.is_some() {
++   |        ----------- because of this check
++...
++LL |         x.unwrap(); // will panic
++   |         ^^^^^^^^^^
++   |
++note: the lint level is defined here
++  --> $DIR/simple_conditionals.rs:1:9
++   |
++LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++  --> $DIR/simple_conditionals.rs:20:9
++   |
++LL |     if x.is_none() {
++   |        ----------- because of this check
++LL |         x.unwrap(); // will panic
++   |         ^^^^^^^^^^
++
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/simple_conditionals.rs:22:9
++   |
++LL |     if x.is_none() {
++   |        ----------- the check is happening here
++...
++LL |         x.unwrap(); // unnecessary
++   |         ^^^^^^^^^^
++
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/simple_conditionals.rs:7:13
++   |
++LL |         if $a.is_some() {
++   |            ------------ the check is happening here
++LL |             $a.unwrap(); // unnecessary
++   |             ^^^^^^^^^^^
++...
++LL |     m!(x);
++   |     ------ in this macro invocation
++   |
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/simple_conditionals.rs:27:9
++   |
++LL |     if x.is_ok() {
++   |        --------- the check is happening here
++LL |         x.unwrap(); // unnecessary
++   |         ^^^^^^^^^^
++
++error: This call to `unwrap_err()` will always panic.
++  --> $DIR/simple_conditionals.rs:28:9
++   |
++LL |     if x.is_ok() {
++   |        --------- because of this check
++LL |         x.unwrap(); // unnecessary
++LL |         x.unwrap_err(); // will panic
++   |         ^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++  --> $DIR/simple_conditionals.rs:30:9
++   |
++LL |     if x.is_ok() {
++   |        --------- because of this check
++...
++LL |         x.unwrap(); // will panic
++   |         ^^^^^^^^^^
++
++error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/simple_conditionals.rs:31:9
++   |
++LL |     if x.is_ok() {
++   |        --------- the check is happening here
++...
++LL |         x.unwrap_err(); // unnecessary
++   |         ^^^^^^^^^^^^^^
++
++error: This call to `unwrap()` will always panic.
++  --> $DIR/simple_conditionals.rs:34:9
++   |
++LL |     if x.is_err() {
++   |        ---------- because of this check
++LL |         x.unwrap(); // will panic
++   |         ^^^^^^^^^^
++
++error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/simple_conditionals.rs:35:9
++   |
++LL |     if x.is_err() {
++   |        ---------- the check is happening here
++LL |         x.unwrap(); // will panic
++LL |         x.unwrap_err(); // unnecessary
++   |         ^^^^^^^^^^^^^^
++
++error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
++  --> $DIR/simple_conditionals.rs:37:9
++   |
++LL |     if x.is_err() {
++   |        ---------- the check is happening here
++...
++LL |         x.unwrap(); // unnecessary
++   |         ^^^^^^^^^^
++
++error: This call to `unwrap_err()` will always panic.
++  --> $DIR/simple_conditionals.rs:38:9
++   |
++LL |     if x.is_err() {
++   |        ---------- because of this check
++...
++LL |         x.unwrap_err(); // will panic
++   |         ^^^^^^^^^^^^^^
++
++error: aborting due to 13 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8f9f2a0db8c437df5e8eaa9dc41315fdfcf2510d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++use std::fmt;
++use std::marker::PhantomData;
++
++pub struct Key<T> {
++    #[doc(hidden)]
++    pub __name: &'static str,
++    #[doc(hidden)]
++    pub __phantom: PhantomData<T>,
++}
++
++impl<T> Copy for Key<T> {}
++
++impl<T> Clone for Key<T> {
++    fn clone(&self) -> Self {
++        Key {
++            __name: self.__name,
++            __phantom: self.__phantom,
++        }
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5bfa256623b6bbcd5b56bb93421b655e417f0d68
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++pub fn dec_read_dec(i: &mut i32) -> i32 {
++    *i -= 1;
++    let ret = *i;
++    *i -= 1;
++    ret
++}
++
++pub fn minus_1(i: &i32) -> i32 {
++    dec_read_dec(&mut i.clone())
++}
++
++fn main() {
++    let mut i = 10;
++    assert_eq!(minus_1(&i), 9);
++    assert_eq!(i, 10);
++    assert_eq!(dec_read_dec(&mut i), 9);
++    assert_eq!(i, 8);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..64ca52b010a7eb5c5a38db9a3d285d706b2b3656
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++const NAN_F32: f32 = f32::NAN;
++const NAN_F64: f64 = f64::NAN;
++
++#[warn(clippy::cmp_nan)]
++#[allow(clippy::float_cmp, clippy::no_effect, clippy::unnecessary_operation)]
++fn main() {
++    let x = 5f32;
++    x == f32::NAN;
++    x != f32::NAN;
++    x < f32::NAN;
++    x > f32::NAN;
++    x <= f32::NAN;
++    x >= f32::NAN;
++    x == NAN_F32;
++    x != NAN_F32;
++    x < NAN_F32;
++    x > NAN_F32;
++    x <= NAN_F32;
++    x >= NAN_F32;
++
++    let y = 0f64;
++    y == f64::NAN;
++    y != f64::NAN;
++    y < f64::NAN;
++    y > f64::NAN;
++    y <= f64::NAN;
++    y >= f64::NAN;
++    y == NAN_F64;
++    y != NAN_F64;
++    y < NAN_F64;
++    y > NAN_F64;
++    y <= NAN_F64;
++    y >= NAN_F64;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..867516661a539150125313a274e4817b13955e42
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,148 @@@
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:8:5
++   |
++LL |     x == f32::NAN;
++   |     ^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::cmp-nan` implied by `-D warnings`
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:9:5
++   |
++LL |     x != f32::NAN;
++   |     ^^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:10:5
++   |
++LL |     x < f32::NAN;
++   |     ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:11:5
++   |
++LL |     x > f32::NAN;
++   |     ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:12:5
++   |
++LL |     x <= f32::NAN;
++   |     ^^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:13:5
++   |
++LL |     x >= f32::NAN;
++   |     ^^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:14:5
++   |
++LL |     x == NAN_F32;
++   |     ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:15:5
++   |
++LL |     x != NAN_F32;
++   |     ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:16:5
++   |
++LL |     x < NAN_F32;
++   |     ^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:17:5
++   |
++LL |     x > NAN_F32;
++   |     ^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:18:5
++   |
++LL |     x <= NAN_F32;
++   |     ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:19:5
++   |
++LL |     x >= NAN_F32;
++   |     ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:22:5
++   |
++LL |     y == f64::NAN;
++   |     ^^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:23:5
++   |
++LL |     y != f64::NAN;
++   |     ^^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:24:5
++   |
++LL |     y < f64::NAN;
++   |     ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:25:5
++   |
++LL |     y > f64::NAN;
++   |     ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:26:5
++   |
++LL |     y <= f64::NAN;
++   |     ^^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:27:5
++   |
++LL |     y >= f64::NAN;
++   |     ^^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:28:5
++   |
++LL |     y == NAN_F64;
++   |     ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:29:5
++   |
++LL |     y != NAN_F64;
++   |     ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:30:5
++   |
++LL |     y < NAN_F64;
++   |     ^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:31:5
++   |
++LL |     y > NAN_F64;
++   |     ^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:32:5
++   |
++LL |     y <= NAN_F64;
++   |     ^^^^^^^^^^^^
++
++error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
++  --> $DIR/cmp_nan.rs:33:5
++   |
++LL |     y >= NAN_F64;
++   |     ^^^^^^^^^^^^
++
++error: aborting due to 24 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2d2d04178c35d21bc04ed1d1526c9a4378563fde
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++#![warn(clippy::cmp_null)]
++#![allow(unused_mut)]
++
++use std::ptr;
++
++fn main() {
++    let x = 0;
++    let p: *const usize = &x;
++    if p == ptr::null() {
++        println!("This is surprising!");
++    }
++    let mut y = 0;
++    let mut m: *mut usize = &mut y;
++    if m == ptr::null_mut() {
++        println!("This is surprising, too!");
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b563a2ebec2d2fd1b55f7f436799f6efe6920864
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: Comparing with null is better expressed by the `.is_null()` method
++  --> $DIR/cmp_null.rs:9:8
++   |
++LL |     if p == ptr::null() {
++   |        ^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::cmp-null` implied by `-D warnings`
++
++error: Comparing with null is better expressed by the `.is_null()` method
++  --> $DIR/cmp_null.rs:14:8
++   |
++LL |     if m == ptr::null_mut() {
++   |        ^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..05fb96339e33e9ff561c1e4e5c067170e1e48c58
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,72 @@@
++// run-rustfix
++
++#[warn(clippy::cmp_owned)]
++#[allow(clippy::unnecessary_operation, clippy::no_effect, unused_must_use, clippy::eq_op)]
++fn main() {
++    fn with_to_string(x: &str) {
++        x != "foo";
++
++        "foo" != x;
++    }
++
++    let x = "oh";
++
++    with_to_string(x);
++
++    x != "foo";
++
++    x != "foo";
++
++    42.to_string() == "42";
++
++    Foo == Foo;
++
++    "abc".chars().filter(|c| *c != 'X');
++
++    "abc".chars().filter(|c| *c != 'X');
++}
++
++struct Foo;
++
++impl PartialEq for Foo {
++    // Allow this here, because it emits the lint
++    // without a suggestion. This is tested in
++    // `tests/ui/cmp_owned/without_suggestion.rs`
++    #[allow(clippy::cmp_owned)]
++    fn eq(&self, other: &Self) -> bool {
++        self.to_owned() == *other
++    }
++}
++
++impl ToOwned for Foo {
++    type Owned = Bar;
++    fn to_owned(&self) -> Bar {
++        Bar
++    }
++}
++
++#[derive(PartialEq)]
++struct Bar;
++
++impl PartialEq<Foo> for Bar {
++    fn eq(&self, _: &Foo) -> bool {
++        true
++    }
++}
++
++impl std::borrow::Borrow<Foo> for Bar {
++    fn borrow(&self) -> &Foo {
++        static FOO: Foo = Foo;
++        &FOO
++    }
++}
++
++#[derive(PartialEq)]
++struct Baz;
++
++impl ToOwned for Baz {
++    type Owned = Baz;
++    fn to_owned(&self) -> Baz {
++        Baz
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0a02825ed82f1b858eb1c52a6ceb3981c4effcee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,72 @@@
++// run-rustfix
++
++#[warn(clippy::cmp_owned)]
++#[allow(clippy::unnecessary_operation, clippy::no_effect, unused_must_use, clippy::eq_op)]
++fn main() {
++    fn with_to_string(x: &str) {
++        x != "foo".to_string();
++
++        "foo".to_string() != x;
++    }
++
++    let x = "oh";
++
++    with_to_string(x);
++
++    x != "foo".to_owned();
++
++    x != String::from("foo");
++
++    42.to_string() == "42";
++
++    Foo.to_owned() == Foo;
++
++    "abc".chars().filter(|c| c.to_owned() != 'X');
++
++    "abc".chars().filter(|c| *c != 'X');
++}
++
++struct Foo;
++
++impl PartialEq for Foo {
++    // Allow this here, because it emits the lint
++    // without a suggestion. This is tested in
++    // `tests/ui/cmp_owned/without_suggestion.rs`
++    #[allow(clippy::cmp_owned)]
++    fn eq(&self, other: &Self) -> bool {
++        self.to_owned() == *other
++    }
++}
++
++impl ToOwned for Foo {
++    type Owned = Bar;
++    fn to_owned(&self) -> Bar {
++        Bar
++    }
++}
++
++#[derive(PartialEq)]
++struct Bar;
++
++impl PartialEq<Foo> for Bar {
++    fn eq(&self, _: &Foo) -> bool {
++        true
++    }
++}
++
++impl std::borrow::Borrow<Foo> for Bar {
++    fn borrow(&self) -> &Foo {
++        static FOO: Foo = Foo;
++        &FOO
++    }
++}
++
++#[derive(PartialEq)]
++struct Baz;
++
++impl ToOwned for Baz {
++    type Owned = Baz;
++    fn to_owned(&self) -> Baz {
++        Baz
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2f333e6ea8ecbda9d0338ff7cbfa131b518a690c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: this creates an owned instance just for comparison
++  --> $DIR/with_suggestion.rs:7:14
++   |
++LL |         x != "foo".to_string();
++   |              ^^^^^^^^^^^^^^^^^ help: try: `"foo"`
++   |
++   = note: `-D clippy::cmp-owned` implied by `-D warnings`
++
++error: this creates an owned instance just for comparison
++  --> $DIR/with_suggestion.rs:9:9
++   |
++LL |         "foo".to_string() != x;
++   |         ^^^^^^^^^^^^^^^^^ help: try: `"foo"`
++
++error: this creates an owned instance just for comparison
++  --> $DIR/with_suggestion.rs:16:10
++   |
++LL |     x != "foo".to_owned();
++   |          ^^^^^^^^^^^^^^^^ help: try: `"foo"`
++
++error: this creates an owned instance just for comparison
++  --> $DIR/with_suggestion.rs:18:10
++   |
++LL |     x != String::from("foo");
++   |          ^^^^^^^^^^^^^^^^^^^ help: try: `"foo"`
++
++error: this creates an owned instance just for comparison
++  --> $DIR/with_suggestion.rs:22:5
++   |
++LL |     Foo.to_owned() == Foo;
++   |     ^^^^^^^^^^^^^^ help: try: `Foo`
++
++error: this creates an owned instance just for comparison
++  --> $DIR/with_suggestion.rs:24:30
++   |
++LL |     "abc".chars().filter(|c| c.to_owned() != 'X');
++   |                              ^^^^^^^^^^^^ help: try: `*c`
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9ab8795474c674912f3f42c4fe1f5a266b814e9e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++#[allow(clippy::unnecessary_operation)]
++
++fn main() {
++    let x = &Baz;
++    let y = &Baz;
++    y.to_owned() == *x;
++
++    let x = &&Baz;
++    let y = &Baz;
++    y.to_owned() == **x;
++}
++
++struct Foo;
++
++impl PartialEq for Foo {
++    fn eq(&self, other: &Self) -> bool {
++        self.to_owned() == *other
++    }
++}
++
++impl ToOwned for Foo {
++    type Owned = Bar;
++    fn to_owned(&self) -> Bar {
++        Bar
++    }
++}
++
++#[derive(PartialEq)]
++struct Baz;
++
++impl ToOwned for Baz {
++    type Owned = Baz;
++    fn to_owned(&self) -> Baz {
++        Baz
++    }
++}
++
++#[derive(PartialEq)]
++struct Bar;
++
++impl PartialEq<Foo> for Bar {
++    fn eq(&self, _: &Foo) -> bool {
++        true
++    }
++}
++
++impl std::borrow::Borrow<Foo> for Bar {
++    fn borrow(&self) -> &Foo {
++        static FOO: Foo = Foo;
++        &FOO
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6e8a5ad2a17b5a6c02ae1dce0815da9c22b4dfa6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: this creates an owned instance just for comparison
++  --> $DIR/without_suggestion.rs:6:5
++   |
++LL |     y.to_owned() == *x;
++   |     ^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating
++   |
++   = note: `-D clippy::cmp-owned` implied by `-D warnings`
++
++error: this creates an owned instance just for comparison
++  --> $DIR/without_suggestion.rs:10:5
++   |
++LL |     y.to_owned() == **x;
++   |     ^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating
++
++error: this creates an owned instance just for comparison
++  --> $DIR/without_suggestion.rs:17:9
++   |
++LL |         self.to_owned() == *other
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1d3fe405521cdbc936c2f82836611571a85acb7f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,395 @@@
++#![allow(clippy::all)]
++#![warn(clippy::cognitive_complexity)]
++#![allow(unused)]
++
++#[rustfmt::skip]
++fn main() {
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++    if true {
++        println!("a");
++    }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn kaboom() {
++    let n = 0;
++    'a: for i in 0..20 {
++        'b: for j in i..20 {
++            for k in j..20 {
++                if k == 5 {
++                    break 'b;
++                }
++                if j == 3 && k == 6 {
++                    continue 'a;
++                }
++                if k == j {
++                    continue;
++                }
++                println!("bake");
++            }
++        }
++        println!("cake");
++    }
++}
++
++fn bloo() {
++    match 42 {
++        0 => println!("hi"),
++        1 => println!("hai"),
++        2 => println!("hey"),
++        3 => println!("hallo"),
++        4 => println!("hello"),
++        5 => println!("salut"),
++        6 => println!("good morning"),
++        7 => println!("good evening"),
++        8 => println!("good afternoon"),
++        9 => println!("good night"),
++        10 => println!("bonjour"),
++        11 => println!("hej"),
++        12 => println!("hej hej"),
++        13 => println!("greetings earthling"),
++        14 => println!("take us to you leader"),
++        15 | 17 | 19 | 21 | 23 | 25 | 27 | 29 | 31 | 33 => println!("take us to you leader"),
++        35 | 37 | 39 | 41 | 43 | 45 | 47 | 49 | 51 | 53 => println!("there is no undefined behavior"),
++        55 | 57 | 59 | 61 | 63 | 65 | 67 | 69 | 71 | 73 => println!("I know borrow-fu"),
++        _ => println!("bye"),
++    }
++}
++
++// Short circuiting operations don't increase the complexity of a function.
++// Note that the minimum complexity of a function is 1.
++#[clippy::cognitive_complexity = "1"]
++fn lots_of_short_circuits() -> bool {
++    true && false && true && false && true && false && true
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn lots_of_short_circuits2() -> bool {
++    true || false || true || false || true || false || true
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn baa() {
++    let x = || match 99 {
++        0 => 0,
++        1 => 1,
++        2 => 2,
++        4 => 4,
++        6 => 6,
++        9 => 9,
++        _ => 42,
++    };
++    if x() == 42 {
++        println!("x");
++    } else {
++        println!("not x");
++    }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn bar() {
++    match 99 {
++        0 => println!("hi"),
++        _ => println!("bye"),
++    }
++}
++
++#[test]
++#[clippy::cognitive_complexity = "1"]
++/// Tests are usually complex but simple at the same time. `clippy::cognitive_complexity` used to
++/// give lots of false-positives in tests.
++fn dont_warn_on_tests() {
++    match 99 {
++        0 => println!("hi"),
++        _ => println!("bye"),
++    }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn barr() {
++    match 99 {
++        0 => println!("hi"),
++        1 => println!("bla"),
++        2 | 3 => println!("blub"),
++        _ => println!("bye"),
++    }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn barr2() {
++    match 99 {
++        0 => println!("hi"),
++        1 => println!("bla"),
++        2 | 3 => println!("blub"),
++        _ => println!("bye"),
++    }
++    match 99 {
++        0 => println!("hi"),
++        1 => println!("bla"),
++        2 | 3 => println!("blub"),
++        _ => println!("bye"),
++    }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn barrr() {
++    match 99 {
++        0 => println!("hi"),
++        1 => panic!("bla"),
++        2 | 3 => println!("blub"),
++        _ => println!("bye"),
++    }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn barrr2() {
++    match 99 {
++        0 => println!("hi"),
++        1 => panic!("bla"),
++        2 | 3 => println!("blub"),
++        _ => println!("bye"),
++    }
++    match 99 {
++        0 => println!("hi"),
++        1 => panic!("bla"),
++        2 | 3 => println!("blub"),
++        _ => println!("bye"),
++    }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn barrrr() {
++    match 99 {
++        0 => println!("hi"),
++        1 => println!("bla"),
++        2 | 3 => panic!("blub"),
++        _ => println!("bye"),
++    }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn barrrr2() {
++    match 99 {
++        0 => println!("hi"),
++        1 => println!("bla"),
++        2 | 3 => panic!("blub"),
++        _ => println!("bye"),
++    }
++    match 99 {
++        0 => println!("hi"),
++        1 => println!("bla"),
++        2 | 3 => panic!("blub"),
++        _ => println!("bye"),
++    }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn cake() {
++    if 4 == 5 {
++        println!("yea");
++    } else {
++        panic!("meh");
++    }
++    println!("whee");
++}
++
++#[clippy::cognitive_complexity = "1"]
++pub fn read_file(input_path: &str) -> String {
++    use std::fs::File;
++    use std::io::{Read, Write};
++    use std::path::Path;
++    let mut file = match File::open(&Path::new(input_path)) {
++        Ok(f) => f,
++        Err(err) => {
++            panic!("Can't open {}: {}", input_path, err);
++        },
++    };
++
++    let mut bytes = Vec::new();
++
++    match file.read_to_end(&mut bytes) {
++        Ok(..) => {},
++        Err(_) => {
++            panic!("Can't read {}", input_path);
++        },
++    };
++
++    match String::from_utf8(bytes) {
++        Ok(contents) => contents,
++        Err(_) => {
++            panic!("{} is not UTF-8 encoded", input_path);
++        },
++    }
++}
++
++enum Void {}
++
++#[clippy::cognitive_complexity = "1"]
++fn void(void: Void) {
++    if true {
++        match void {}
++    }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn mcarton_sees_all() {
++    panic!("meh");
++    panic!("möh");
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn try_() -> Result<i32, &'static str> {
++    match 5 {
++        5 => Ok(5),
++        _ => return Err("bla"),
++    }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn try_again() -> Result<i32, &'static str> {
++    let _ = Ok(42)?;
++    let _ = Ok(43)?;
++    let _ = Ok(44)?;
++    let _ = Ok(45)?;
++    let _ = Ok(46)?;
++    let _ = Ok(47)?;
++    let _ = Ok(48)?;
++    let _ = Ok(49)?;
++    match 5 {
++        5 => Ok(5),
++        _ => return Err("bla"),
++    }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn early() -> Result<i32, &'static str> {
++    return Ok(5);
++    return Ok(5);
++    return Ok(5);
++    return Ok(5);
++    return Ok(5);
++    return Ok(5);
++    return Ok(5);
++    return Ok(5);
++    return Ok(5);
++}
++
++#[rustfmt::skip]
++#[clippy::cognitive_complexity = "1"]
++fn early_ret() -> i32 {
++    let a = if true { 42 } else { return 0; };
++    let a = if a < 99 { 42 } else { return 0; };
++    let a = if a < 99 { 42 } else { return 0; };
++    let a = if a < 99 { 42 } else { return 0; };
++    let a = if a < 99 { 42 } else { return 0; };
++    let a = if a < 99 { 42 } else { return 0; };
++    let a = if a < 99 { 42 } else { return 0; };
++    let a = if a < 99 { 42 } else { return 0; };
++    let a = if a < 99 { 42 } else { return 0; };
++    let a = if a < 99 { 42 } else { return 0; };
++    let a = if a < 99 { 42 } else { return 0; };
++    let a = if a < 99 { 42 } else { return 0; };
++    match 5 {
++        5 => 5,
++        _ => return 6,
++    }
++}
++
++#[clippy::cognitive_complexity = "1"]
++fn closures() {
++    let x = |a: i32, b: i32| -> i32 {
++        if true {
++            println!("moo");
++        }
++
++        a + b
++    };
++}
++
++struct Moo;
++
++#[clippy::cognitive_complexity = "1"]
++impl Moo {
++    fn moo(&self) {
++        if true {
++            println!("moo");
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a0ddc673abcc103f8a94456947834d407a0c7257
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,139 @@@
++error: the function has a cognitive complexity of (28/25)
++  --> $DIR/cognitive_complexity.rs:6:4
++   |
++LL | fn main() {
++   |    ^^^^
++   |
++   = note: `-D clippy::cognitive-complexity` implied by `-D warnings`
++   = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (7/1)
++  --> $DIR/cognitive_complexity.rs:91:4
++   |
++LL | fn kaboom() {
++   |    ^^^^^^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++  --> $DIR/cognitive_complexity.rs:149:4
++   |
++LL | fn baa() {
++   |    ^^^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++  --> $DIR/cognitive_complexity.rs:150:13
++   |
++LL |     let x = || match 99 {
++   |             ^^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++  --> $DIR/cognitive_complexity.rs:167:4
++   |
++LL | fn bar() {
++   |    ^^^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++  --> $DIR/cognitive_complexity.rs:186:4
++   |
++LL | fn barr() {
++   |    ^^^^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (3/1)
++  --> $DIR/cognitive_complexity.rs:196:4
++   |
++LL | fn barr2() {
++   |    ^^^^^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++  --> $DIR/cognitive_complexity.rs:212:4
++   |
++LL | fn barrr() {
++   |    ^^^^^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (3/1)
++  --> $DIR/cognitive_complexity.rs:222:4
++   |
++LL | fn barrr2() {
++   |    ^^^^^^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++  --> $DIR/cognitive_complexity.rs:238:4
++   |
++LL | fn barrrr() {
++   |    ^^^^^^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (3/1)
++  --> $DIR/cognitive_complexity.rs:248:4
++   |
++LL | fn barrrr2() {
++   |    ^^^^^^^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++  --> $DIR/cognitive_complexity.rs:264:4
++   |
++LL | fn cake() {
++   |    ^^^^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (4/1)
++  --> $DIR/cognitive_complexity.rs:274:8
++   |
++LL | pub fn read_file(input_path: &str) -> String {
++   |        ^^^^^^^^^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++  --> $DIR/cognitive_complexity.rs:305:4
++   |
++LL | fn void(void: Void) {
++   |    ^^^^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (8/1)
++  --> $DIR/cognitive_complexity.rs:356:4
++   |
++LL | fn early_ret() -> i32 {
++   |    ^^^^^^^^^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++  --> $DIR/cognitive_complexity.rs:377:13
++   |
++LL |     let x = |a: i32, b: i32| -> i32 {
++   |             ^^^^^^^^^^^^^^^^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++  --> $DIR/cognitive_complexity.rs:390:8
++   |
++LL |     fn moo(&self) {
++   |        ^^^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: aborting due to 17 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..403eff566ed6d34d2a2ac92e58d9d94735782f62
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++#![warn(clippy::cognitive_complexity)]
++#![warn(unused)]
++
++fn main() {
++    kaboom();
++}
++
++#[clippy::cognitive_complexity = "0"]
++fn kaboom() {
++    if 42 == 43 {
++        panic!();
++    } else if "cake" == "lie" {
++        println!("what?");
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f5ff53dda603b388329940d2ad6d70a2003e9e0f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++error: the function has a cognitive complexity of (3/0)
++  --> $DIR/cognitive_complexity_attr_used.rs:9:4
++   |
++LL | fn kaboom() {
++   |    ^^^^^^
++   |
++   = note: `-D clippy::cognitive-complexity` implied by `-D warnings`
++   = help: you could split it up into multiple smaller functions
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ce2a1c28c8a80f84c3ab0454d2efc517efa4f7c5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,66 @@@
++// run-rustfix
++#![allow(clippy::assertions_on_constants)]
++
++#[rustfmt::skip]
++#[warn(clippy::collapsible_if)]
++fn main() {
++    let x = "hello";
++    let y = "world";
++    // Collapse `else { if .. }` to `else if ..`
++    if x == "hello" {
++        print!("Hello ");
++    } else if y == "world" {
++        println!("world!")
++    }
++
++    if x == "hello" {
++        print!("Hello ");
++    } else if let Some(42) = Some(42) {
++        println!("world!")
++    }
++
++    if x == "hello" {
++        print!("Hello ");
++    } else if y == "world" {
++        println!("world")
++    }
++    else {
++        println!("!")
++    }
++
++    if x == "hello" {
++        print!("Hello ");
++    } else if let Some(42) = Some(42) {
++        println!("world")
++    }
++    else {
++        println!("!")
++    }
++
++    if let Some(42) = Some(42) {
++        print!("Hello ");
++    } else if let Some(42) = Some(42) {
++        println!("world")
++    }
++    else {
++        println!("!")
++    }
++
++    if let Some(42) = Some(42) {
++        print!("Hello ");
++    } else if x == "hello" {
++        println!("world")
++    }
++    else {
++        println!("!")
++    }
++
++    if let Some(42) = Some(42) {
++        print!("Hello ");
++    } else if let Some(42) = Some(42) {
++        println!("world")
++    }
++    else {
++        println!("!")
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..99c40b8d38eb9a79247d25b18e92b48145ef3d31
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,80 @@@
++// run-rustfix
++#![allow(clippy::assertions_on_constants)]
++
++#[rustfmt::skip]
++#[warn(clippy::collapsible_if)]
++fn main() {
++    let x = "hello";
++    let y = "world";
++    // Collapse `else { if .. }` to `else if ..`
++    if x == "hello" {
++        print!("Hello ");
++    } else {
++        if y == "world" {
++            println!("world!")
++        }
++    }
++
++    if x == "hello" {
++        print!("Hello ");
++    } else {
++        if let Some(42) = Some(42) {
++            println!("world!")
++        }
++    }
++
++    if x == "hello" {
++        print!("Hello ");
++    } else {
++        if y == "world" {
++            println!("world")
++        }
++        else {
++            println!("!")
++        }
++    }
++
++    if x == "hello" {
++        print!("Hello ");
++    } else {
++        if let Some(42) = Some(42) {
++            println!("world")
++        }
++        else {
++            println!("!")
++        }
++    }
++
++    if let Some(42) = Some(42) {
++        print!("Hello ");
++    } else {
++        if let Some(42) = Some(42) {
++            println!("world")
++        }
++        else {
++            println!("!")
++        }
++    }
++
++    if let Some(42) = Some(42) {
++        print!("Hello ");
++    } else {
++        if x == "hello" {
++            println!("world")
++        }
++        else {
++            println!("!")
++        }
++    }
++
++    if let Some(42) = Some(42) {
++        print!("Hello ");
++    } else {
++        if let Some(42) = Some(42) {
++            println!("world")
++        }
++        else {
++            println!("!")
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..28048999e8ec9568a8071135f2e3612bccddae43
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,154 @@@
++error: this `else { if .. }` block can be collapsed
++  --> $DIR/collapsible_else_if.rs:12:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         if y == "world" {
++LL | |             println!("world!")
++LL | |         }
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::collapsible-if` implied by `-D warnings`
++help: try
++   |
++LL |     } else if y == "world" {
++LL |         println!("world!")
++LL |     }
++   |
++
++error: this `else { if .. }` block can be collapsed
++  --> $DIR/collapsible_else_if.rs:20:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         if let Some(42) = Some(42) {
++LL | |             println!("world!")
++LL | |         }
++LL | |     }
++   | |_____^
++   |
++help: try
++   |
++LL |     } else if let Some(42) = Some(42) {
++LL |         println!("world!")
++LL |     }
++   |
++
++error: this `else { if .. }` block can be collapsed
++  --> $DIR/collapsible_else_if.rs:28:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         if y == "world" {
++LL | |             println!("world")
++LL | |         }
++...  |
++LL | |         }
++LL | |     }
++   | |_____^
++   |
++help: try
++   |
++LL |     } else if y == "world" {
++LL |         println!("world")
++LL |     }
++LL |     else {
++LL |         println!("!")
++LL |     }
++   |
++
++error: this `else { if .. }` block can be collapsed
++  --> $DIR/collapsible_else_if.rs:39:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         if let Some(42) = Some(42) {
++LL | |             println!("world")
++LL | |         }
++...  |
++LL | |         }
++LL | |     }
++   | |_____^
++   |
++help: try
++   |
++LL |     } else if let Some(42) = Some(42) {
++LL |         println!("world")
++LL |     }
++LL |     else {
++LL |         println!("!")
++LL |     }
++   |
++
++error: this `else { if .. }` block can be collapsed
++  --> $DIR/collapsible_else_if.rs:50:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         if let Some(42) = Some(42) {
++LL | |             println!("world")
++LL | |         }
++...  |
++LL | |         }
++LL | |     }
++   | |_____^
++   |
++help: try
++   |
++LL |     } else if let Some(42) = Some(42) {
++LL |         println!("world")
++LL |     }
++LL |     else {
++LL |         println!("!")
++LL |     }
++   |
++
++error: this `else { if .. }` block can be collapsed
++  --> $DIR/collapsible_else_if.rs:61:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         if x == "hello" {
++LL | |             println!("world")
++LL | |         }
++...  |
++LL | |         }
++LL | |     }
++   | |_____^
++   |
++help: try
++   |
++LL |     } else if x == "hello" {
++LL |         println!("world")
++LL |     }
++LL |     else {
++LL |         println!("!")
++LL |     }
++   |
++
++error: this `else { if .. }` block can be collapsed
++  --> $DIR/collapsible_else_if.rs:72:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         if let Some(42) = Some(42) {
++LL | |             println!("world")
++LL | |         }
++...  |
++LL | |         }
++LL | |     }
++   | |_____^
++   |
++help: try
++   |
++LL |     } else if let Some(42) = Some(42) {
++LL |         println!("world")
++LL |     }
++LL |     else {
++LL |         println!("!")
++LL |     }
++   |
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..561283fc8e73de2b839d10dbf3d3fd22068d091c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,138 @@@
++// run-rustfix
++#![allow(clippy::assertions_on_constants)]
++
++#[rustfmt::skip]
++#[warn(clippy::collapsible_if)]
++fn main() {
++    let x = "hello";
++    let y = "world";
++    if x == "hello" && y == "world" {
++        println!("Hello world!");
++    }
++
++    if (x == "hello" || x == "world") && (y == "world" || y == "hello") {
++        println!("Hello world!");
++    }
++
++    if x == "hello" && x == "world" && (y == "world" || y == "hello") {
++        println!("Hello world!");
++    }
++
++    if (x == "hello" || x == "world") && y == "world" && y == "hello" {
++        println!("Hello world!");
++    }
++
++    if x == "hello" && x == "world" && y == "world" && y == "hello" {
++        println!("Hello world!");
++    }
++
++    if 42 == 1337 && 'a' != 'A' {
++        println!("world!")
++    }
++
++    // Works because any if with an else statement cannot be collapsed.
++    if x == "hello" {
++        if y == "world" {
++            println!("Hello world!");
++        }
++    } else {
++        println!("Not Hello world");
++    }
++
++    if x == "hello" {
++        if y == "world" {
++            println!("Hello world!");
++        } else {
++            println!("Hello something else");
++        }
++    }
++
++    if x == "hello" {
++        print!("Hello ");
++        if y == "world" {
++            println!("world!")
++        }
++    }
++
++    if true {
++    } else {
++        assert!(true); // assert! is just an `if`
++    }
++
++
++    // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798
++    if x == "hello" {// Not collapsible
++        if y == "world" {
++            println!("Hello world!");
++        }
++    }
++
++    if x == "hello" { // Not collapsible
++        if y == "world" {
++            println!("Hello world!");
++        }
++    }
++
++    if x == "hello" {
++        // Not collapsible
++        if y == "world" {
++            println!("Hello world!");
++        }
++    }
++
++    if x == "hello" && y == "world" { // Collapsible
++        println!("Hello world!");
++    }
++
++    if x == "hello" {
++        print!("Hello ");
++    } else {
++        // Not collapsible
++        if y == "world" {
++            println!("world!")
++        }
++    }
++
++    if x == "hello" {
++        print!("Hello ");
++    } else {
++        // Not collapsible
++        if let Some(42) = Some(42) {
++            println!("world!")
++        }
++    }
++
++    if x == "hello" {
++        /* Not collapsible */
++        if y == "world" {
++            println!("Hello world!");
++        }
++    }
++
++    if x == "hello" { /* Not collapsible */
++        if y == "world" {
++            println!("Hello world!");
++        }
++    }
++
++    // Test behavior wrt. `let_chains`.
++    // None of the cases below should be collapsed.
++    fn truth() -> bool { true }
++
++    // Prefix:
++    if let 0 = 1 {
++        if truth() {}
++    }
++
++    // Suffix:
++    if truth() {
++        if let 0 = 1 {}
++    }
++
++    // Midfix:
++    if truth() {
++        if let 0 = 1 {
++            if truth() {}
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dc9d9b451c0f9620d9181936ebbc18bd58fa6a4f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,152 @@@
++// run-rustfix
++#![allow(clippy::assertions_on_constants)]
++
++#[rustfmt::skip]
++#[warn(clippy::collapsible_if)]
++fn main() {
++    let x = "hello";
++    let y = "world";
++    if x == "hello" {
++        if y == "world" {
++            println!("Hello world!");
++        }
++    }
++
++    if x == "hello" || x == "world" {
++        if y == "world" || y == "hello" {
++            println!("Hello world!");
++        }
++    }
++
++    if x == "hello" && x == "world" {
++        if y == "world" || y == "hello" {
++            println!("Hello world!");
++        }
++    }
++
++    if x == "hello" || x == "world" {
++        if y == "world" && y == "hello" {
++            println!("Hello world!");
++        }
++    }
++
++    if x == "hello" && x == "world" {
++        if y == "world" && y == "hello" {
++            println!("Hello world!");
++        }
++    }
++
++    if 42 == 1337 {
++        if 'a' != 'A' {
++            println!("world!")
++        }
++    }
++
++    // Works because any if with an else statement cannot be collapsed.
++    if x == "hello" {
++        if y == "world" {
++            println!("Hello world!");
++        }
++    } else {
++        println!("Not Hello world");
++    }
++
++    if x == "hello" {
++        if y == "world" {
++            println!("Hello world!");
++        } else {
++            println!("Hello something else");
++        }
++    }
++
++    if x == "hello" {
++        print!("Hello ");
++        if y == "world" {
++            println!("world!")
++        }
++    }
++
++    if true {
++    } else {
++        assert!(true); // assert! is just an `if`
++    }
++
++
++    // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798
++    if x == "hello" {// Not collapsible
++        if y == "world" {
++            println!("Hello world!");
++        }
++    }
++
++    if x == "hello" { // Not collapsible
++        if y == "world" {
++            println!("Hello world!");
++        }
++    }
++
++    if x == "hello" {
++        // Not collapsible
++        if y == "world" {
++            println!("Hello world!");
++        }
++    }
++
++    if x == "hello" {
++        if y == "world" { // Collapsible
++            println!("Hello world!");
++        }
++    }
++
++    if x == "hello" {
++        print!("Hello ");
++    } else {
++        // Not collapsible
++        if y == "world" {
++            println!("world!")
++        }
++    }
++
++    if x == "hello" {
++        print!("Hello ");
++    } else {
++        // Not collapsible
++        if let Some(42) = Some(42) {
++            println!("world!")
++        }
++    }
++
++    if x == "hello" {
++        /* Not collapsible */
++        if y == "world" {
++            println!("Hello world!");
++        }
++    }
++
++    if x == "hello" { /* Not collapsible */
++        if y == "world" {
++            println!("Hello world!");
++        }
++    }
++
++    // Test behavior wrt. `let_chains`.
++    // None of the cases below should be collapsed.
++    fn truth() -> bool { true }
++
++    // Prefix:
++    if let 0 = 1 {
++        if truth() {}
++    }
++
++    // Suffix:
++    if truth() {
++        if let 0 = 1 {}
++    }
++
++    // Midfix:
++    if truth() {
++        if let 0 = 1 {
++            if truth() {}
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6440ff41be81e243da433c4ac3ac7ec67b6bf55a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,122 @@@
++error: this `if` statement can be collapsed
++  --> $DIR/collapsible_if.rs:9:5
++   |
++LL | /     if x == "hello" {
++LL | |         if y == "world" {
++LL | |             println!("Hello world!");
++LL | |         }
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::collapsible-if` implied by `-D warnings`
++help: try
++   |
++LL |     if x == "hello" && y == "world" {
++LL |         println!("Hello world!");
++LL |     }
++   |
++
++error: this `if` statement can be collapsed
++  --> $DIR/collapsible_if.rs:15:5
++   |
++LL | /     if x == "hello" || x == "world" {
++LL | |         if y == "world" || y == "hello" {
++LL | |             println!("Hello world!");
++LL | |         }
++LL | |     }
++   | |_____^
++   |
++help: try
++   |
++LL |     if (x == "hello" || x == "world") && (y == "world" || y == "hello") {
++LL |         println!("Hello world!");
++LL |     }
++   |
++
++error: this `if` statement can be collapsed
++  --> $DIR/collapsible_if.rs:21:5
++   |
++LL | /     if x == "hello" && x == "world" {
++LL | |         if y == "world" || y == "hello" {
++LL | |             println!("Hello world!");
++LL | |         }
++LL | |     }
++   | |_____^
++   |
++help: try
++   |
++LL |     if x == "hello" && x == "world" && (y == "world" || y == "hello") {
++LL |         println!("Hello world!");
++LL |     }
++   |
++
++error: this `if` statement can be collapsed
++  --> $DIR/collapsible_if.rs:27:5
++   |
++LL | /     if x == "hello" || x == "world" {
++LL | |         if y == "world" && y == "hello" {
++LL | |             println!("Hello world!");
++LL | |         }
++LL | |     }
++   | |_____^
++   |
++help: try
++   |
++LL |     if (x == "hello" || x == "world") && y == "world" && y == "hello" {
++LL |         println!("Hello world!");
++LL |     }
++   |
++
++error: this `if` statement can be collapsed
++  --> $DIR/collapsible_if.rs:33:5
++   |
++LL | /     if x == "hello" && x == "world" {
++LL | |         if y == "world" && y == "hello" {
++LL | |             println!("Hello world!");
++LL | |         }
++LL | |     }
++   | |_____^
++   |
++help: try
++   |
++LL |     if x == "hello" && x == "world" && y == "world" && y == "hello" {
++LL |         println!("Hello world!");
++LL |     }
++   |
++
++error: this `if` statement can be collapsed
++  --> $DIR/collapsible_if.rs:39:5
++   |
++LL | /     if 42 == 1337 {
++LL | |         if 'a' != 'A' {
++LL | |             println!("world!")
++LL | |         }
++LL | |     }
++   | |_____^
++   |
++help: try
++   |
++LL |     if 42 == 1337 && 'a' != 'A' {
++LL |         println!("world!")
++LL |     }
++   |
++
++error: this `if` statement can be collapsed
++  --> $DIR/collapsible_if.rs:95:5
++   |
++LL | /     if x == "hello" {
++LL | |         if y == "world" { // Collapsible
++LL | |             println!("Hello world!");
++LL | |         }
++LL | |     }
++   | |_____^
++   |
++help: try
++   |
++LL |     if x == "hello" && y == "world" { // Collapsible
++LL |         println!("Hello world!");
++LL |     }
++   |
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e588c23345e2fdd8ad010e05a0db259589f7bade
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,91 @@@
++// run-rustfix
++#![deny(clippy::internal)]
++#![feature(rustc_private)]
++
++extern crate rustc_ast;
++extern crate rustc_errors;
++extern crate rustc_lint;
++extern crate rustc_session;
++extern crate rustc_span;
++
++use rustc_ast::ast::Expr;
++use rustc_errors::{Applicability, DiagnosticBuilder};
++use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++#[allow(unused_variables)]
++pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
++where
++    F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
++{
++}
++
++#[allow(unused_variables)]
++fn span_lint_and_help<'a, T: LintContext>(
++    cx: &'a T,
++    lint: &'static Lint,
++    span: Span,
++    msg: &str,
++    option_span: Option<Span>,
++    help: &str,
++) {
++}
++
++#[allow(unused_variables)]
++fn span_lint_and_note<'a, T: LintContext>(
++    cx: &'a T,
++    lint: &'static Lint,
++    span: Span,
++    msg: &str,
++    note_span: Option<Span>,
++    note: &str,
++) {
++}
++
++#[allow(unused_variables)]
++fn span_lint_and_sugg<'a, T: LintContext>(
++    cx: &'a T,
++    lint: &'static Lint,
++    sp: Span,
++    msg: &str,
++    help: &str,
++    sugg: String,
++    applicability: Applicability,
++) {
++}
++
++declare_tool_lint! {
++    pub clippy::TEST_LINT,
++    Warn,
++    "",
++    report_in_external_macro: true
++}
++
++declare_lint_pass!(Pass => [TEST_LINT]);
++
++impl EarlyLintPass for Pass {
++    fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
++        let lint_msg = "lint message";
++        let help_msg = "help message";
++        let note_msg = "note message";
++        let sugg = "new_call()";
++        let predicate = true;
++
++        span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable);
++        span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg);
++        span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg);
++        span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg);
++        span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg);
++
++        // This expr shouldn't trigger this lint.
++        span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++            db.note(note_msg);
++            if predicate {
++                db.note(note_msg);
++            }
++        })
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d5dd3bb562b429d9265f3ba1d5c80a4c977a4dc2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,101 @@@
++// run-rustfix
++#![deny(clippy::internal)]
++#![feature(rustc_private)]
++
++extern crate rustc_ast;
++extern crate rustc_errors;
++extern crate rustc_lint;
++extern crate rustc_session;
++extern crate rustc_span;
++
++use rustc_ast::ast::Expr;
++use rustc_errors::{Applicability, DiagnosticBuilder};
++use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Span;
++
++#[allow(unused_variables)]
++pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
++where
++    F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
++{
++}
++
++#[allow(unused_variables)]
++fn span_lint_and_help<'a, T: LintContext>(
++    cx: &'a T,
++    lint: &'static Lint,
++    span: Span,
++    msg: &str,
++    option_span: Option<Span>,
++    help: &str,
++) {
++}
++
++#[allow(unused_variables)]
++fn span_lint_and_note<'a, T: LintContext>(
++    cx: &'a T,
++    lint: &'static Lint,
++    span: Span,
++    msg: &str,
++    note_span: Option<Span>,
++    note: &str,
++) {
++}
++
++#[allow(unused_variables)]
++fn span_lint_and_sugg<'a, T: LintContext>(
++    cx: &'a T,
++    lint: &'static Lint,
++    sp: Span,
++    msg: &str,
++    help: &str,
++    sugg: String,
++    applicability: Applicability,
++) {
++}
++
++declare_tool_lint! {
++    pub clippy::TEST_LINT,
++    Warn,
++    "",
++    report_in_external_macro: true
++}
++
++declare_lint_pass!(Pass => [TEST_LINT]);
++
++impl EarlyLintPass for Pass {
++    fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
++        let lint_msg = "lint message";
++        let help_msg = "help message";
++        let note_msg = "note message";
++        let sugg = "new_call()";
++        let predicate = true;
++
++        span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++            db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable);
++        });
++        span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++            db.span_help(expr.span, help_msg);
++        });
++        span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++            db.help(help_msg);
++        });
++        span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++            db.span_note(expr.span, note_msg);
++        });
++        span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++            db.note(note_msg);
++        });
++
++        // This expr shouldn't trigger this lint.
++        span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++            db.note(note_msg);
++            if predicate {
++                db.note(note_msg);
++            }
++        })
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..874d4a9f255c27dcaa542325be407d65d25cfabd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,49 @@@
++error: this call is collapsible
++  --> $DIR/collapsible_span_lint_calls.rs:75:9
++   |
++LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++LL | |             db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable);
++LL | |         });
++   | |__________^ help: collapse into: `span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable)`
++   |
++note: the lint level is defined here
++  --> $DIR/collapsible_span_lint_calls.rs:2:9
++   |
++LL | #![deny(clippy::internal)]
++   |         ^^^^^^^^^^^^^^^^
++   = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]`
++
++error: this call is collapsible
++  --> $DIR/collapsible_span_lint_calls.rs:78:9
++   |
++LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++LL | |             db.span_help(expr.span, help_msg);
++LL | |         });
++   | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)`
++
++error: this call is collapsible
++  --> $DIR/collapsible_span_lint_calls.rs:81:9
++   |
++LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++LL | |             db.help(help_msg);
++LL | |         });
++   | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)`
++
++error: this call is collspible
++  --> $DIR/collapsible_span_lint_calls.rs:84:9
++   |
++LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++LL | |             db.span_note(expr.span, note_msg);
++LL | |         });
++   | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)`
++
++error: this call is collspible
++  --> $DIR/collapsible_span_lint_calls.rs:87:9
++   |
++LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
++LL | |             db.note(note_msg);
++LL | |         });
++   | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg)`
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9c2128469de9ded7afeaf9571882f4e3be171383
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,140 @@@
++#![allow(dead_code)]
++#![warn(clippy::comparison_chain)]
++
++fn a() {}
++fn b() {}
++fn c() {}
++
++fn f(x: u8, y: u8, z: u8) {
++    // Ignored: Only one branch
++    if x > y {
++        a()
++    }
++
++    if x > y {
++        a()
++    } else if x < y {
++        b()
++    }
++
++    // Ignored: Only one explicit conditional
++    if x > y {
++        a()
++    } else {
++        b()
++    }
++
++    if x > y {
++        a()
++    } else if x < y {
++        b()
++    } else {
++        c()
++    }
++
++    if x > y {
++        a()
++    } else if y > x {
++        b()
++    } else {
++        c()
++    }
++
++    if x > 1 {
++        a()
++    } else if x < 1 {
++        b()
++    } else if x == 1 {
++        c()
++    }
++
++    // Ignored: Binop args are not equivalent
++    if x > 1 {
++        a()
++    } else if y > 1 {
++        b()
++    } else {
++        c()
++    }
++
++    // Ignored: Binop args are not equivalent
++    if x > y {
++        a()
++    } else if x > z {
++        b()
++    } else if y > z {
++        c()
++    }
++
++    // Ignored: Not binary comparisons
++    if true {
++        a()
++    } else if false {
++        b()
++    } else {
++        c()
++    }
++}
++
++#[allow(clippy::float_cmp)]
++fn g(x: f64, y: f64, z: f64) {
++    // Ignored: f64 doesn't implement Ord
++    if x > y {
++        a()
++    } else if x < y {
++        b()
++    }
++
++    // Ignored: f64 doesn't implement Ord
++    if x > y {
++        a()
++    } else if x < y {
++        b()
++    } else {
++        c()
++    }
++
++    // Ignored: f64 doesn't implement Ord
++    if x > y {
++        a()
++    } else if y > x {
++        b()
++    } else {
++        c()
++    }
++
++    // Ignored: f64 doesn't implement Ord
++    if x > 1.0 {
++        a()
++    } else if x < 1.0 {
++        b()
++    } else if x == 1.0 {
++        c()
++    }
++}
++
++fn h<T: Ord>(x: T, y: T, z: T) {
++    if x > y {
++        a()
++    } else if x < y {
++        b()
++    }
++
++    if x > y {
++        a()
++    } else if x < y {
++        b()
++    } else {
++        c()
++    }
++
++    if x > y {
++        a()
++    } else if y > x {
++        b()
++    } else {
++        c()
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..69db88b03b5b55d30251dad120febb2fecf7be65
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,97 @@@
++error: `if` chain can be rewritten with `match`
++  --> $DIR/comparison_chain.rs:14:5
++   |
++LL | /     if x > y {
++LL | |         a()
++LL | |     } else if x < y {
++LL | |         b()
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::comparison-chain` implied by `-D warnings`
++   = help: Consider rewriting the `if` chain to use `cmp` and `match`.
++
++error: `if` chain can be rewritten with `match`
++  --> $DIR/comparison_chain.rs:27:5
++   |
++LL | /     if x > y {
++LL | |         a()
++LL | |     } else if x < y {
++LL | |         b()
++LL | |     } else {
++LL | |         c()
++LL | |     }
++   | |_____^
++   |
++   = help: Consider rewriting the `if` chain to use `cmp` and `match`.
++
++error: `if` chain can be rewritten with `match`
++  --> $DIR/comparison_chain.rs:35:5
++   |
++LL | /     if x > y {
++LL | |         a()
++LL | |     } else if y > x {
++LL | |         b()
++LL | |     } else {
++LL | |         c()
++LL | |     }
++   | |_____^
++   |
++   = help: Consider rewriting the `if` chain to use `cmp` and `match`.
++
++error: `if` chain can be rewritten with `match`
++  --> $DIR/comparison_chain.rs:43:5
++   |
++LL | /     if x > 1 {
++LL | |         a()
++LL | |     } else if x < 1 {
++LL | |         b()
++LL | |     } else if x == 1 {
++LL | |         c()
++LL | |     }
++   | |_____^
++   |
++   = help: Consider rewriting the `if` chain to use `cmp` and `match`.
++
++error: `if` chain can be rewritten with `match`
++  --> $DIR/comparison_chain.rs:117:5
++   |
++LL | /     if x > y {
++LL | |         a()
++LL | |     } else if x < y {
++LL | |         b()
++LL | |     }
++   | |_____^
++   |
++   = help: Consider rewriting the `if` chain to use `cmp` and `match`.
++
++error: `if` chain can be rewritten with `match`
++  --> $DIR/comparison_chain.rs:123:5
++   |
++LL | /     if x > y {
++LL | |         a()
++LL | |     } else if x < y {
++LL | |         b()
++LL | |     } else {
++LL | |         c()
++LL | |     }
++   | |_____^
++   |
++   = help: Consider rewriting the `if` chain to use `cmp` and `match`.
++
++error: `if` chain can be rewritten with `match`
++  --> $DIR/comparison_chain.rs:131:5
++   |
++LL | /     if x > y {
++LL | |         a()
++LL | |     } else if y > x {
++LL | |         b()
++LL | |     } else {
++LL | |         c()
++LL | |     }
++   | |_____^
++   |
++   = help: Consider rewriting the `if` chain to use `cmp` and `match`.
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..be61fb6b9be613cca8bd66f622644b253af6cabd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,60 @@@
++#![warn(clippy::all)]
++#![allow(unused, clippy::needless_pass_by_value, clippy::vec_box)]
++#![feature(associated_type_defaults)]
++
++type Alias = Vec<Vec<Box<(u32, u32, u32, u32)>>>; // no warning here
++
++const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
++static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
++
++struct S {
++    f: Vec<Vec<Box<(u32, u32, u32, u32)>>>,
++}
++
++struct TS(Vec<Vec<Box<(u32, u32, u32, u32)>>>);
++
++enum E {
++    Tuple(Vec<Vec<Box<(u32, u32, u32, u32)>>>),
++    Struct { f: Vec<Vec<Box<(u32, u32, u32, u32)>>> },
++}
++
++impl S {
++    const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
++    fn impl_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
++}
++
++trait T {
++    const A: Vec<Vec<Box<(u32, u32, u32, u32)>>>;
++    type B = Vec<Vec<Box<(u32, u32, u32, u32)>>>;
++    fn method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>);
++    fn def_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
++}
++
++fn test1() -> Vec<Vec<Box<(u32, u32, u32, u32)>>> {
++    vec![]
++}
++
++fn test2(_x: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
++
++fn test3() {
++    let _y: Vec<Vec<Box<(u32, u32, u32, u32)>>> = vec![];
++}
++
++#[repr(C)]
++struct D {
++    // should not warn, since we don't have control over the signature (#3222)
++    test4: extern "C" fn(
++        itself: &D,
++        a: usize,
++        b: usize,
++        c: usize,
++        d: usize,
++        e: usize,
++        f: usize,
++        g: usize,
++        h: usize,
++        i: usize,
++    ),
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8f5dbd27956c5326e60aae886b8bc9ddd4cc2fb0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,94 @@@
++error: very complex type used. Consider factoring parts into `type` definitions
++  --> $DIR/complex_types.rs:7:12
++   |
++LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
++   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::type-complexity` implied by `-D warnings`
++
++error: very complex type used. Consider factoring parts into `type` definitions
++  --> $DIR/complex_types.rs:8:12
++   |
++LL | static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
++   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++  --> $DIR/complex_types.rs:11:8
++   |
++LL |     f: Vec<Vec<Box<(u32, u32, u32, u32)>>>,
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++  --> $DIR/complex_types.rs:14:11
++   |
++LL | struct TS(Vec<Vec<Box<(u32, u32, u32, u32)>>>);
++   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++  --> $DIR/complex_types.rs:17:11
++   |
++LL |     Tuple(Vec<Vec<Box<(u32, u32, u32, u32)>>>),
++   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++  --> $DIR/complex_types.rs:18:17
++   |
++LL |     Struct { f: Vec<Vec<Box<(u32, u32, u32, u32)>>> },
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++  --> $DIR/complex_types.rs:22:14
++   |
++LL |     const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++  --> $DIR/complex_types.rs:23:30
++   |
++LL |     fn impl_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++  --> $DIR/complex_types.rs:27:14
++   |
++LL |     const A: Vec<Vec<Box<(u32, u32, u32, u32)>>>;
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++  --> $DIR/complex_types.rs:28:14
++   |
++LL |     type B = Vec<Vec<Box<(u32, u32, u32, u32)>>>;
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++  --> $DIR/complex_types.rs:29:25
++   |
++LL |     fn method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>);
++   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++  --> $DIR/complex_types.rs:30:29
++   |
++LL |     fn def_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
++   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++  --> $DIR/complex_types.rs:33:15
++   |
++LL | fn test1() -> Vec<Vec<Box<(u32, u32, u32, u32)>>> {
++   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++  --> $DIR/complex_types.rs:37:14
++   |
++LL | fn test2(_x: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: very complex type used. Consider factoring parts into `type` definitions
++  --> $DIR/complex_types.rs:40:13
++   |
++LL |     let _y: Vec<Vec<Box<(u32, u32, u32, u32)>>> = vec![];
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 15 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e3d5928be231b3d18ef3e0509c779b8ee948db74
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++#![warn(clippy::copy_iterator)]
++
++#[derive(Copy, Clone)]
++struct Countdown(u8);
++
++impl Iterator for Countdown {
++    type Item = u8;
++
++    fn next(&mut self) -> Option<u8> {
++        self.0.checked_sub(1).map(|c| {
++            self.0 = c;
++            c
++        })
++    }
++}
++
++fn main() {
++    let my_iterator = Countdown(5);
++    let a: Vec<_> = my_iterator.take(1).collect();
++    assert_eq!(a.len(), 1);
++    let b: Vec<_> = my_iterator.collect();
++    assert_eq!(b.len(), 5);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f8ce6af7961a6148bb94eaae5b9815cd8f1617dd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++error: you are implementing `Iterator` on a `Copy` type
++  --> $DIR/copy_iterator.rs:6:1
++   |
++LL | / impl Iterator for Countdown {
++LL | |     type Item = u8;
++LL | |
++LL | |     fn next(&mut self) -> Option<u8> {
++...  |
++LL | |     }
++LL | | }
++   | |_^
++   |
++   = note: `-D clippy::copy-iterator` implied by `-D warnings`
++   = note: consider implementing `IntoIterator` instead
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4bb833795bb167bbdd70c71f9944966e2b28a6d3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++// run-pass
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/1698
++
++pub trait Trait {
++    const CONSTANT: u8;
++}
++
++impl Trait for u8 {
++    const CONSTANT: u8 = 2;
++}
++
++fn main() {
++    println!("{}", u8::CONSTANT * 10);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..58a20caf6c67634694e7c85714579f2a60711c54
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++pub trait Trait {
++    fn fun(par: &str) -> &str;
++}
++
++impl Trait for str {
++    fn fun(par: &str) -> &str {
++        &par[0..1]
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..086548e58ed677b2ca1f85e53c925c956f39f0fc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++// no-prefer-dynamic
++// ^ compiletest by default builds all aux files as dylibs, but we don't want that for proc-macro
++// crates. If we don't set this, compiletest will override the `crate_type` attribute below and
++// compile this as dylib. Removing this then causes the test to fail because a `dylib` crate can't
++// contain a proc-macro.
++
++#![feature(repr128)]
++#![crate_type = "proc-macro"]
++
++extern crate proc_macro;
++
++use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
++use std::iter::FromIterator;
++
++#[proc_macro]
++pub fn macro_test(input_stream: TokenStream) -> TokenStream {
++    let first_token = input_stream.into_iter().next().unwrap();
++    let span = first_token.span();
++
++    TokenStream::from_iter(vec![
++        TokenTree::Ident(Ident::new("fn", Span::call_site())),
++        TokenTree::Ident(Ident::new("code", Span::call_site())),
++        TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
++        TokenTree::Group(Group::new(Delimiter::Brace, {
++            let mut clause = Group::new(Delimiter::Brace, TokenStream::new());
++            clause.set_span(span);
++
++            TokenStream::from_iter(vec![
++                TokenTree::Ident(Ident::new("if", Span::call_site())),
++                TokenTree::Ident(Ident::new("true", Span::call_site())),
++                TokenTree::Group(clause.clone()),
++                TokenTree::Ident(Ident::new("else", Span::call_site())),
++                TokenTree::Group(clause),
++            ])
++        })),
++    ])
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a8a85b4baefb728a4cd663ee47c98bceb02aea53
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++macro_rules! use_self {
++    (
++        impl $ty:ident {
++            fn func(&$this:ident) {
++                [fields($($field:ident)*)]
++            }
++        }
++    ) => (
++        impl  $ty {
++            fn func(&$this) {
++                let $ty { $($field),* } = $this;
++            }
++        }
++    )
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c48c7e9e6c6ba075568f5622a3522bf9c92688c3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++// run-pass
++
++#[allow(dead_code)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/478
++
++enum Baz {
++    One,
++    Two,
++}
++
++struct Test {
++    t: Option<usize>,
++    b: Baz,
++}
++
++fn main() {}
++
++pub fn foo() {
++    use Baz::*;
++    let x = Test { t: Some(0), b: One };
++
++    match x {
++        Test { t: Some(_), b: One } => unreachable!(),
++        Test { t: Some(42), b: Two } => unreachable!(),
++        Test { t: None, .. } => unreachable!(),
++        Test { .. } => unreachable!(),
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..db1fa871afe0050c4d10bb3dbb45746a613f7d12
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++// run-pass
++
++#![deny(clippy::all)]
++#![allow(unused_imports)]
++
++use std::*;
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..15d0f705b367f49b696065c971a7724151b231a8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++// run-pass
++
++#![allow(clippy::all)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/1588
++
++fn main() {
++    match 1 {
++        1 => {},
++        2 => {
++            [0; 1];
++        },
++        _ => {},
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1ca6b6976b38f6a6485b089bd86304feb58d2843
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++// run-pass
++
++#![allow(dead_code, unused_variables)]
++
++/// Should not trigger an ICE in `SpanlessEq` / `consts::constant`
++///
++/// Issue: https://github.com/rust-lang/rust-clippy/issues/1782
++use std::{mem, ptr};
++
++fn spanless_eq_ice() {
++    let txt = "something";
++    match txt {
++        "something" => unsafe {
++            ptr::write(
++                ptr::null_mut() as *mut u32,
++                mem::transmute::<[u8; 4], _>([0, 0, 0, 255]),
++            )
++        },
++        _ => unsafe {
++            ptr::write(
++                ptr::null_mut() as *mut u32,
++                mem::transmute::<[u8; 4], _>([13, 246, 24, 255]),
++            )
++        },
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..837ec9df31ab107e8da528b818b55c2480d94ee1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++// run-pass
++
++#![allow(clippy::all)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/1969
++
++fn main() {}
++
++pub trait Convert {
++    type Action: From<*const f64>;
++
++    fn convert(val: *const f64) -> Self::Action {
++        val.into()
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ffef1631775eeb8c3e415fc900ca2e7a1e60ea77
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++// run-pass
++
++#![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)]
++
++/// Should not trigger an ICE in `SpanlessHash` / `consts::constant`
++///
++/// Issue: https://github.com/rust-lang/rust-clippy/issues/2499
++
++fn f(s: &[u8]) -> bool {
++    let t = s[0] as char;
++
++    match t {
++        'E' | 'W' => {},
++        'T' => {
++            if s[0..4] != ['0' as u8; 4] {
++                return false;
++            } else {
++                return true;
++            }
++        },
++        _ => {
++            return false;
++        },
++    }
++    true
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ac19f1976e91207ad37a1b0c996aee69955c9c65
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++// run-pass
++
++#![allow(dead_code, unused_variables)]
++
++/// Should not trigger an ICE in `SpanlessHash` / `consts::constant`
++///
++/// Issue: https://github.com/rust-lang/rust-clippy/issues/2594
++
++fn spanless_hash_ice() {
++    let txt = "something";
++    let empty_header: [u8; 1] = [1; 1];
++
++    match txt {
++        "something" => {
++            let mut headers = [empty_header; 1];
++        },
++        "" => (),
++        _ => (),
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e0b58157590ab26ec150f156273d5df4e8c77947
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++#![allow(dead_code)]
++
++enum Foo {
++    A,
++    B,
++    C,
++}
++
++macro_rules! test_hash {
++    ($foo:expr, $($t:ident => $ord:expr),+ ) => {
++        use self::Foo::*;
++        match $foo {
++            $ ( & $t => $ord,
++            )*
++        };
++    };
++}
++
++fn main() {
++    let a = Foo::A;
++    test_hash!(&a, A => 0, B => 1, C => 2);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..53799b4fbf1d971c26eab4b29a21619b567254e9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++error: you don't need to add `&` to both the expression and the patterns
++  --> $DIR/ice-2636.rs:12:9
++   |
++LL | /         match $foo {
++LL | |             $ ( & $t => $ord,
++LL | |             )*
++LL | |         };
++   | |_________^
++...
++LL |       test_hash!(&a, A => 0, B => 1, C => 2);
++   |       --------------------------------------- in this macro invocation
++   |
++   = note: `-D clippy::match-ref-pats` implied by `-D warnings`
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d832c2860332d0b1b25e062de26437e5ef699210
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++// run-pass
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/2727
++
++pub fn f(new: fn()) {
++    new();
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9e5e299c336a518ca68a137efd5a6738925413f6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++// run-pass
++
++#![allow(
++    unused_variables,
++    clippy::blacklisted_name,
++    clippy::needless_pass_by_value,
++    dead_code
++)]
++
++/// This should not compile-fail with:
++///
++///      error[E0277]: the trait bound `T: Foo` is not satisfied
++// See rust-lang/rust-clippy#2760.
++
++trait Foo {
++    type Bar;
++}
++
++struct Baz<T: Foo> {
++    bar: T::Bar,
++}
++
++fn take<T: Foo>(baz: Baz<T>) {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..47f8e3b18eeaa217b16d62787769dd16db8ec9c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++// run-pass
++
++use std::collections::HashSet;
++
++// See rust-lang/rust-clippy#2774.
++
++#[derive(Eq, PartialEq, Debug, Hash)]
++pub struct Bar {
++    foo: Foo,
++}
++
++#[derive(Eq, PartialEq, Debug, Hash)]
++pub struct Foo {}
++
++#[allow(clippy::implicit_hasher)]
++// This should not cause a "cannot relate bound region" ICE.
++pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) {
++    let mut foos = HashSet::new();
++    foos.extend(bars.iter().map(|b| &b.foo));
++}
++
++#[allow(clippy::implicit_hasher)]
++// Also, this should not cause a "cannot relate bound region" ICE.
++pub fn add_barfoos_to_foos2(bars: &HashSet<&Bar>) {
++    let mut foos = HashSet::new();
++    foos.extend(bars.iter().map(|b| &b.foo));
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..47324ce18316953dcc4dd383617b2b2edc85b1fe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++// run-pass
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/2862
++
++pub trait FooMap {
++    fn map<B, F: Fn() -> B>(&self, f: F) -> B;
++}
++
++impl FooMap for bool {
++    fn map<B, F: Fn() -> B>(&self, f: F) -> B {
++        f()
++    }
++}
++
++fn main() {
++    let a = true;
++    a.map(|| false);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c4f6c0fed6820ef715939ac07984badb522b52cf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++// run-pass
++
++#[allow(dead_code)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/2865
++
++struct Ice {
++    size: String,
++}
++
++impl<'a> From<String> for Ice {
++    fn from(_: String) -> Self {
++        let text = || "iceberg".to_string();
++        Self { size: text() }
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ffad2d06b56e249e21e9b585ddd931d6ef31729e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++// run-pass
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/2865
++
++#[derive(Clone)]
++pub struct HashMap<V, S> {
++    hash_builder: S,
++    table: RawTable<V>,
++}
++
++#[derive(Clone)]
++pub struct RawTable<V> {
++    size: usize,
++    val: V,
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..95c7dff9be36b5522da3e2faa65d36b117d73a03
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++// run-pass
++
++#![warn(clippy::all)]
++#![allow(clippy::blacklisted_name)]
++#![allow(unused)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/3462
++
++enum Foo {
++    Bar,
++    Baz,
++}
++
++fn bar(foo: Foo) {
++    macro_rules! baz {
++        () => {
++            if let Foo::Bar = foo {}
++        };
++    }
++
++    baz!();
++    baz!();
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6555c19ca6a26b5c79711eb925cd1d6b6a4cf9bb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++fn main() {}
++
++fn no_panic<T>(slice: &[T]) {
++    let mut iter = slice.iter();
++    loop {
++        let _ = match iter.next() {
++            Some(ele) => ele,
++            None => break,
++        };
++        loop {}
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..84e31eaf2e9f8d420fef2ea67486ce7dfc1ce32a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++error: this loop could be written as a `while let` loop
++  --> $DIR/ice-360.rs:5:5
++   |
++LL | /     loop {
++LL | |         let _ = match iter.next() {
++LL | |             Some(ele) => ele,
++LL | |             None => break,
++LL | |         };
++LL | |         loop {}
++LL | |     }
++   | |_____^ help: try: `while let Some(ele) = iter.next() { .. }`
++   |
++   = note: `-D clippy::while-let-loop` implied by `-D warnings`
++
++error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
++  --> $DIR/ice-360.rs:10:9
++   |
++LL |         loop {}
++   |         ^^^^^^^
++   |
++   = note: `-D clippy::empty-loop` implied by `-D warnings`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f50714643fd2524905a7166c96d71e0c25687c34
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++#![deny(clippy::implicit_hasher)]
++
++use std::collections::HashSet;
++
++fn main() {}
++
++pub fn ice_3717(_: &HashSet<usize>) {
++    let _ = [0u8; 0];
++    let _: HashSet<usize> = HashSet::new();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..296c95abb96d343f73629910d0f5fab3dd6fd228
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: parameter of type `HashSet` should be generalized over different hashers
++  --> $DIR/ice-3717.rs:7:21
++   |
++LL | pub fn ice_3717(_: &HashSet<usize>) {
++   |                     ^^^^^^^^^^^^^^
++   |
++note: the lint level is defined here
++  --> $DIR/ice-3717.rs:1:9
++   |
++LL | #![deny(clippy::implicit_hasher)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^
++help: consider adding a type parameter
++   |
++LL | pub fn ice_3717<S: ::std::hash::BuildHasher + Default>(_: &HashSet<usize, S>) {
++   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^     ^^^^^^^^^^^^^^^^^
++help: ...and use generic constructor
++   |
++LL |     let _: HashSet<usize> = HashSet::default();
++   |                             ^^^^^^^^^^^^^^^^^^
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..74b9c9c86c81cc5372520db1952b9e4449af2015
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++// aux-build:proc_macro_crash.rs
++// run-pass
++
++#![feature(proc_macro_hygiene)]
++#![warn(clippy::suspicious_else_formatting)]
++
++extern crate proc_macro_crash;
++use proc_macro_crash::macro_test;
++
++fn main() {
++    macro_test!(2);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d0b44ebafeeb22e7d85738bce954dd12e5e6278d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++// run-pass
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/3747
++
++macro_rules! a {
++    ( $pub:tt $($attr:tt)* ) => {
++        $($attr)* $pub fn say_hello() {}
++    };
++}
++
++macro_rules! b {
++    () => {
++        a! { pub }
++    };
++}
++
++b! {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..05c5134c8457b4f2564b99eff0aa4353f773cbcd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++fn main() {
++    1x;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5a285b0e714920fcc8a88e24b81d3f804ed2c0c4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: invalid suffix `x` for integer literal
++  --> $DIR/ice-3891.rs:2:5
++   |
++LL |     1x;
++   |     ^^ invalid suffix `x`
++   |
++   = help: the suffix must be one of the integral types (`u32`, `isize`, etc)
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e1a142fdcb67f00053d5700ec5193bf89fc7c6a6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++use std::mem;
++
++pub struct Foo<A, B>(A, B);
++
++impl<A, B> Foo<A, B> {
++    const HOST_SIZE: usize = mem::size_of::<B>();
++
++    pub fn crash() -> bool {
++        Self::HOST_SIZE == 0
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d9c9c2096d97c364f62fb2f686ce7e9b280b3a0c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++fn repro() {
++    trait Foo {
++        type Bar;
++    }
++
++    #[allow(dead_code)]
++    struct Baz<T: Foo> {
++        field: T::Bar,
++    }
++}
++
++fn main() {
++    repro();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2e7e279f847dd28f11cead520f004d2f09998018
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++#![allow(clippy::single_match)]
++
++use std::ptr;
++
++fn main() {
++    match Some(0_usize) {
++        Some(_) => {
++            let s = "012345";
++            unsafe { ptr::read(s.as_ptr().offset(1) as *const [u8; 5]) };
++        },
++        _ => (),
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..64e8e7769412d6b09c9c4eca759250bd744b6b6d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++#![warn(clippy::use_self)]
++
++#[macro_use]
++#[path = "auxiliary/use_self_macro.rs"]
++mod use_self_macro;
++
++struct Foo {
++    a: u32,
++}
++
++use_self! {
++    impl Foo {
++        fn func(&self) {
++            [fields(
++                a
++            )]
++        }
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cdb59caec67ed4b3dd46541acd28c3d1c65b14ab
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++// run-pass
++
++#![warn(clippy::use_self)]
++
++#[path = "auxiliary/ice-4727-aux.rs"]
++mod aux;
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ead67d5ed1b1eaee50f973aab9eddc6c9a6e1771
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++// run-pass
++const COUNT: usize = 2;
++struct Thing;
++trait Dummy {}
++
++const _: () = {
++    impl Dummy for Thing where [i32; COUNT]: Sized {}
++};
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..31e53e846d54da5bdd0f1c29f92d4b1b6d34460e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++#![feature(const_generics)]
++#![allow(incomplete_features)]
++
++pub struct ArrayWrapper<const N: usize>([usize; N]);
++
++impl<const N: usize> ArrayWrapper<{ N }> {
++    pub fn ice(&self) {
++        for i in self.0.iter() {
++            println!("{}", i);
++        }
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3822f174598540ea2980e6469795872029d5a9e7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++// check-pass
++
++// Test for https://github.com/rust-lang/rust-clippy/issues/4968
++
++#![warn(clippy::unsound_collection_transmute)]
++
++trait Trait {
++    type Assoc;
++}
++
++use std::mem::{self, ManuallyDrop};
++
++#[allow(unused)]
++fn func<T: Trait>(slice: Vec<T::Assoc>) {
++    unsafe {
++        let _: Vec<ManuallyDrop<T::Assoc>> = mem::transmute(slice);
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1b20c9defac823185ec31b6a3229167d0abb5e58
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++// edition:2018
++
++// Regression test for https://github.com/rust-lang/rust-clippy/issues/5207
++
++pub async fn bar<'a, T: 'a>(_: T) {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9bb2e227fc12e6ccbad119ac349f1f5af721fbcd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++// Regression test for #5233
++
++#![feature(const_generics)]
++#![allow(incomplete_features)]
++#![warn(clippy::indexing_slicing, clippy::iter_cloned_collect)]
++
++pub struct KotomineArray<T, const N: usize> {
++    arr: [T; N],
++}
++
++impl<T: std::clone::Clone, const N: usize> KotomineArray<T, N> {
++    pub fn ice(self) {
++        let _ = self.arr[..];
++        let _ = self.arr.iter().cloned().collect::<Vec<_>>();
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..989eb6d4485575443ead9c3b9f0f782cdcc25b4e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++// Regression test for #5238 / https://github.com/rust-lang/rust/pull/69562
++
++#![feature(generators, generator_trait)]
++
++fn main() {
++    let _ = || {
++        yield;
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0769bce5fc80993db3f269e8cdab5e0e2e4c6819
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++// reduced from rustc issue-69020-assoc-const-arith-overflow.rs
++pub fn main() {}
++
++pub trait Foo {
++    const OOB: i32;
++}
++
++impl<T: Foo> Foo for Vec<T> {
++    const OOB: i32 = [1][1] + T::OOB;
++    //~^ ERROR operation will panic
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b06df83d51a5bf932b908fbfe448356219243a66
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++// run-pass
++
++#![deny(clippy::all)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/700
++
++fn core() {}
++
++fn main() {
++    core();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e02eb28ab8650c54b4842dfe0d5a40a099827fd2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++// run-pass
++
++#![deny(clippy::all)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/1336
++
++#[allow(dead_code)]
++struct Foo;
++
++impl Iterator for Foo {
++    type Item = ();
++
++    fn next(&mut self) -> Option<()> {
++        let _ = self.len() == 0;
++        unimplemented!()
++    }
++}
++
++impl ExactSizeIterator for Foo {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4ef992b05e7618f2b40221690618354f528b7d9d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++// run-pass
++
++#![allow(clippy::comparison_chain)]
++#![deny(clippy::if_same_then_else)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/2426
++
++fn main() {}
++
++pub fn foo(a: i32, b: i32) -> Option<&'static str> {
++    if a == b {
++        None
++    } else if a > b {
++        Some("a pfeil b")
++    } else {
++        None
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aeb27b5ba8c2fbfe36da4e533e0a0b14ec9b5e0e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++#![deny(clippy::multiple_inherent_impl)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/4578
++
++macro_rules! impl_foo {
++    ($struct:ident) => {
++        impl $struct {
++            fn foo() {}
++        }
++    };
++}
++
++macro_rules! impl_bar {
++    ($struct:ident) => {
++        impl $struct {
++            fn bar() {}
++        }
++    };
++}
++
++struct MyStruct;
++
++impl_foo!(MyStruct);
++impl_bar!(MyStruct);
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3d4a88ab3c4e5dbfc59f3d39a053ff9cf36a2726
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++// run-pass
++
++#![allow(warnings)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/825
++
++// this should compile in a reasonable amount of time
++fn rust_type_id(name: &str) {
++    if "bool" == &name[..]
++        || "uint" == &name[..]
++        || "u8" == &name[..]
++        || "u16" == &name[..]
++        || "u32" == &name[..]
++        || "f32" == &name[..]
++        || "f64" == &name[..]
++        || "i8" == &name[..]
++        || "i16" == &name[..]
++        || "i32" == &name[..]
++        || "i64" == &name[..]
++        || "Self" == &name[..]
++        || "str" == &name[..]
++    {
++        unreachable!();
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c4acd5cda1b0a5f9dc133e032944113d8e1fd567
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++// run-pass
++
++#![allow(dead_code)]
++
++/// Issue: https://github.com/rust-lang/rust-clippy/issues/2596
++pub fn loop_on_block_condition(u: &mut isize) {
++    while { *u < 0 } {
++        *u += 1;
++    }
++}
++
++/// https://github.com/rust-lang/rust-clippy/issues/2584
++fn loop_with_unsafe_condition(ptr: *const u8) {
++    let mut len = 0;
++    while unsafe { *ptr.offset(len) } != 0 {
++        len += 1;
++    }
++}
++
++/// https://github.com/rust-lang/rust-clippy/issues/2710
++static mut RUNNING: bool = true;
++fn loop_on_static_condition() {
++    unsafe {
++        while RUNNING {
++            RUNNING = false;
++        }
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..848f0ea52ca5e5d77765bf815ae9ddf6090cb701
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++// run-pass
++
++#![deny(clippy::match_same_arms)]
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/2427
++
++const PRICE_OF_SWEETS: u32 = 5;
++const PRICE_OF_KINDNESS: u32 = 0;
++const PRICE_OF_DRINKS: u32 = 5;
++
++pub fn price(thing: &str) -> u32 {
++    match thing {
++        "rolo" => PRICE_OF_SWEETS,
++        "advice" => PRICE_OF_KINDNESS,
++        "juice" => PRICE_OF_DRINKS,
++        _ => panic!(),
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d8fbaa5414664ac7be093ed7efab22a7c939321d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++// run-pass
++
++#![deny(clippy::mut_mut, clippy::zero_ptr, clippy::cmp_nan)]
++#![allow(dead_code)]
++
++// FIXME: compiletest + extern crates doesn't work together. To make this test work, it would need
++// the following three lines and the lazy_static crate.
++//
++//     #[macro_use]
++//     extern crate lazy_static;
++//     use std::collections::HashMap;
++
++/// ensure that we don't suggest `is_nan` and `is_null` inside constants
++/// FIXME: once const fn is stable, suggest these functions again in constants
++
++const BAA: *const i32 = 0 as *const i32;
++static mut BAR: *const i32 = BAA;
++static mut FOO: *const i32 = 0 as *const i32;
++static mut BUH: bool = 42.0 < f32::NAN;
++
++#[allow(unused_variables, unused_mut)]
++fn main() {
++    /*
++    lazy_static! {
++        static ref MUT_MAP : HashMap<usize, &'static str> = {
++            let mut m = HashMap::new();
++            m.insert(0, "zero");
++            m
++        };
++        static ref MUT_COUNT : usize = MUT_MAP.len();
++    }
++    assert_eq!(*MUT_COUNT, 1);
++    */
++    // FIXME: don't lint in array length, requires `check_body`
++    //let _ = [""; (42.0 < f32::NAN) as usize];
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..48507efe1e98be8a282e0ab2f56e1b03836d5736
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++// run-pass
++
++#[deny(clippy::all)]
++#[derive(Debug)]
++pub enum Error {
++    Type(&'static str),
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bd1fa4a0b1ef297d503753a99513436a4e1ec7f5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++// run-pass
++
++#![deny(clippy::needless_lifetimes)]
++#![allow(dead_code)]
++
++trait Foo {}
++
++struct Bar {}
++
++struct Baz<'a> {
++    bar: &'a Bar,
++}
++
++impl<'a> Foo for Baz<'a> {}
++
++impl Bar {
++    fn baz<'a>(&'a self) -> impl Foo + 'a {
++        Baz { bar: self }
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f79d9ab6460b893bf7911c2f79cabc038c58cdc5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++// run-pass
++
++#[macro_use]
++extern crate clippy_mini_macro_test;
++
++#[deny(warnings)]
++fn main() {
++    let x = Foo;
++    println!("{:?}", x);
++}
++
++#[derive(ClippyMiniMacroTest, Debug)]
++struct Foo;
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..623ae51f9f08cf43390d23067c9a5343a3edc3d3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++// run-pass
++
++#![allow(clippy::blacklisted_name)]
++
++pub fn foo(bar: *const u8) {
++    println!("{:#p}", bar);
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f2153efc3880060a62b693008c2f8d9d854cf761
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++// run-pass
++
++/// Test for https://github.com/rust-lang/rust-clippy/issues/1346
++
++#[deny(warnings)]
++fn cfg_return() -> i32 {
++    #[cfg(unix)]
++    return 1;
++    #[cfg(not(unix))]
++    return 2;
++}
++
++#[deny(warnings)]
++fn cfg_let_and_return() -> i32 {
++    #[cfg(unix)]
++    let x = 1;
++    #[cfg(not(unix))]
++    let x = 2;
++    x
++}
++
++fn main() {
++    cfg_return();
++    cfg_let_and_return();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..843e8ef64dcd97b2fa43f605b1f5b269b1f97f0f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++fn main() {
++    let x: [i32; {
++        let u = 2;
++        4
++    }] = [2; { 4 }];
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3a4bbe310ccaa8c2913f0eb595c70cacc57bcc4a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++// run-pass
++
++#![warn(clippy::single_match_else)]
++
++//! Test for https://github.com/rust-lang/rust-clippy/issues/1588
++
++fn main() {
++    let n = match (42, 43) {
++        (42, n) => n,
++        _ => panic!("typeck error"),
++    };
++    assert_eq!(n, 43);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2bb95c18a39129fd7028c5d505c17e74ca06227e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++// run-pass
++
++#![feature(trivial_bounds)]
++#![allow(unused, trivial_bounds)]
++
++fn test_trivial_bounds()
++where
++    i32: Iterator,
++{
++    for _ in 2i32 {}
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..265017c51d92452be0236c06f1d6895a90bea1d5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++// run-pass
++
++#![allow(clippy::useless_attribute)] //issue #2910
++
++#[macro_use]
++extern crate serde_derive;
++
++/// Tests that we do not lint for unused underscores in a `MacroAttribute`
++/// expansion
++#[deny(clippy::used_underscore_binding)]
++#[derive(Deserialize)]
++struct MacroAttributesTest {
++    _foo: u32,
++}
++
++#[test]
++fn macro_attributes_test() {
++    let _ = MacroAttributesTest { _foo: 0 };
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9f87de20baff36866965bccd7c4ef87774789645
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++# this is ignored by Clippy, but allowed for other tools like clippy-service
++[third-party]
++clippy-feature = "nightly"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f328e4d9d04c31d0d70d16d21a07d1613be9d577
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..995787c5336688d9283fc8e6ca5ff806faf54133
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++// ignore-macos
++// ignore-windows
++
++#![feature(main)]
++
++#[warn(clippy::main_recursion)]
++#[allow(unconditional_recursion)]
++#[main]
++fn a() {
++    println!("Hello, World!");
++    a();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f52fc949f6c3e7a4d7e933518969079efac7711d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++error: recursing into entrypoint `a`
++  --> $DIR/entrypoint_recursion.rs:11:5
++   |
++LL |     a();
++   |     ^
++   |
++   = note: `-D clippy::main-recursion` implied by `-D warnings`
++   = help: consider using another function for this recursion
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..25b1417be9766a312878f5f9c91a7fd365ea2811
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++// ignore-macos
++// ignore-windows
++
++#![feature(lang_items, link_args, start, libc)]
++#![link_args = "-nostartfiles"]
++#![no_std]
++
++use core::panic::PanicInfo;
++use core::sync::atomic::{AtomicUsize, Ordering};
++
++static N: AtomicUsize = AtomicUsize::new(0);
++
++#[warn(clippy::main_recursion)]
++#[start]
++fn main(argc: isize, argv: *const *const u8) -> isize {
++    let x = N.load(Ordering::Relaxed);
++    N.store(x + 1, Ordering::Relaxed);
++
++    if x < 3 {
++        main(argc, argv);
++    }
++
++    0
++}
++
++#[allow(clippy::empty_loop)]
++#[panic_handler]
++fn panic(_info: &PanicInfo) -> ! {
++    loop {}
++}
++
++#[lang = "eh_personality"]
++extern "C" fn eh_personality() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..89ff6609934d2cac98f83f5a4d4ddc6e91b1e6c0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++#[warn(clippy::main_recursion)]
++#[allow(unconditional_recursion)]
++fn main() {
++    println!("Hello, World!");
++    main();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0a260f9d2309ea9a5cf39e9003a8f580d235f8bb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++error: recursing into entrypoint `main`
++  --> $DIR/std_main_recursion.rs:5:5
++   |
++LL |     main();
++   |     ^^^^
++   |
++   = note: `-D clippy::main-recursion` implied by `-D warnings`
++   = help: consider using another function for this recursion
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6cdd6b4ff6e77671afb525e01d9f5398ad536a0e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++#![deny(clippy::temporary_cstring_as_ptr)]
++
++fn main() {}
++
++fn temporary_cstring() {
++    use std::ffi::CString;
++
++    CString::new("foo").unwrap().as_ptr();
++    CString::new("foo").expect("dummy").as_ptr();
++}
++
++mod issue4375 {
++    use std::ffi::CString;
++    use std::os::raw::c_char;
++
++    extern "C" {
++        fn foo(data: *const c_char);
++    }
++
++    pub fn bar(v: &[u8]) {
++        let cstr = CString::new(v);
++        unsafe { foo(cstr.unwrap().as_ptr()) }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..87cb29be57758a3f9679a2d95cebd4fe2caf321a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++error: you are getting the inner pointer of a temporary `CString`
++  --> $DIR/cstring.rs:8:5
++   |
++LL |     CString::new("foo").unwrap().as_ptr();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++note: the lint level is defined here
++  --> $DIR/cstring.rs:1:9
++   |
++LL | #![deny(clippy::temporary_cstring_as_ptr)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   = note: that pointer will be invalid outside this expression
++help: assign the `CString` to a variable to extend its lifetime
++  --> $DIR/cstring.rs:8:5
++   |
++LL |     CString::new("foo").unwrap().as_ptr();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you are getting the inner pointer of a temporary `CString`
++  --> $DIR/cstring.rs:9:5
++   |
++LL |     CString::new("foo").expect("dummy").as_ptr();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: that pointer will be invalid outside this expression
++help: assign the `CString` to a variable to extend its lifetime
++  --> $DIR/cstring.rs:9:5
++   |
++LL |     CString::new("foo").expect("dummy").as_ptr();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you are getting the inner pointer of a temporary `CString`
++  --> $DIR/cstring.rs:22:22
++   |
++LL |         unsafe { foo(cstr.unwrap().as_ptr()) }
++   |                      ^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: that pointer will be invalid outside this expression
++help: assign the `CString` to a variable to extend its lifetime
++  --> $DIR/cstring.rs:22:22
++   |
++LL |         unsafe { foo(cstr.unwrap().as_ptr()) }
++   |                      ^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5b30c9d5721ca5a0dcf8c7fe0ef200c2339d50f2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++// rustc-env:RUST_BACKTRACE=0
++// normalize-stderr-test: "Clippy version: .*" -> "Clippy version: foo"
++// normalize-stderr-test: "internal_lints.rs:\d*:\d*" -> "internal_lints.rs"
++// normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints"
++
++#![deny(clippy::internal)]
++
++fn it_looks_like_you_are_trying_to_kill_clippy() {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a9a65a38c109d4b956f99d62d991a7a5dc0d1758
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints.rs
++note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
++
++error: internal compiler error: unexpected panic
++
++note: the compiler unexpectedly panicked. this is a bug.
++
++note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new
++
++note: Clippy version: foo
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d2df7fbd3e84c727d263648fff82d7edb684abab
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++#![warn(clippy::dbg_macro)]
++
++fn foo(n: u32) -> u32 {
++    if let Some(n) = dbg!(n.checked_sub(4)) {
++        n
++    } else {
++        n
++    }
++}
++
++fn factorial(n: u32) -> u32 {
++    if dbg!(n <= 1) {
++        dbg!(1)
++    } else {
++        dbg!(n * factorial(n - 1))
++    }
++}
++
++fn main() {
++    dbg!(42);
++    dbg!(dbg!(dbg!(42)));
++    foo(3) + dbg!(factorial(4));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b8aafe966784663ae87609c6883b4d9935b02395
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,80 @@@
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:4:22
++   |
++LL |     if let Some(n) = dbg!(n.checked_sub(4)) {
++   |                      ^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::dbg-macro` implied by `-D warnings`
++help: ensure to avoid having uses of it in version control
++   |
++LL |     if let Some(n) = n.checked_sub(4) {
++   |                      ^^^^^^^^^^^^^^^^
++
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:12:8
++   |
++LL |     if dbg!(n <= 1) {
++   |        ^^^^^^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |     if n <= 1 {
++   |        ^^^^^^
++
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:13:9
++   |
++LL |         dbg!(1)
++   |         ^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |         1
++   |
++
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:15:9
++   |
++LL |         dbg!(n * factorial(n - 1))
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |         n * factorial(n - 1)
++   |
++
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:20:5
++   |
++LL |     dbg!(42);
++   |     ^^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |     42;
++   |     ^^
++
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:21:5
++   |
++LL |     dbg!(dbg!(dbg!(42)));
++   |     ^^^^^^^^^^^^^^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |     dbg!(dbg!(42));
++   |     ^^^^^^^^^^^^^^
++
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:22:14
++   |
++LL |     foo(3) + dbg!(factorial(4));
++   |              ^^^^^^^^^^^^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |     foo(3) + factorial(4);
++   |              ^^^^^^^^^^^^
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..477a47118d4116587fc6526ca01f3f72edf639e2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,133 @@@
++// compile-flags: --edition=2018
++#![feature(custom_inner_attributes)]
++#![rustfmt::skip]
++#![warn(clippy::debug_assert_with_mut_call)]
++#![allow(clippy::redundant_closure_call)]
++
++struct S;
++
++impl S {
++    fn bool_self_ref(&self) -> bool { false }
++    fn bool_self_mut(&mut self) -> bool { false }
++    fn bool_self_ref_arg_ref(&self, _: &u32) -> bool { false }
++    fn bool_self_ref_arg_mut(&self, _: &mut u32) -> bool { false }
++    fn bool_self_mut_arg_ref(&mut self, _: &u32) -> bool { false }
++    fn bool_self_mut_arg_mut(&mut self, _: &mut u32) -> bool { false }
++
++    fn u32_self_ref(&self) -> u32 { 0 }
++    fn u32_self_mut(&mut self) -> u32 { 0 }
++    fn u32_self_ref_arg_ref(&self, _: &u32) -> u32 { 0 }
++    fn u32_self_ref_arg_mut(&self, _: &mut u32) -> u32 { 0 }
++    fn u32_self_mut_arg_ref(&mut self, _: &u32) -> u32 { 0 }
++    fn u32_self_mut_arg_mut(&mut self, _: &mut u32) -> u32 { 0 }
++}
++
++fn bool_ref(_: &u32) -> bool { false }
++fn bool_mut(_: &mut u32) -> bool { false }
++fn u32_ref(_: &u32) -> u32 { 0 }
++fn u32_mut(_: &mut u32) -> u32 { 0 }
++
++fn func_non_mutable() {
++    debug_assert!(bool_ref(&3));
++    debug_assert!(!bool_ref(&3));
++
++    debug_assert_eq!(0, u32_ref(&3));
++    debug_assert_eq!(u32_ref(&3), 0);
++
++    debug_assert_ne!(1, u32_ref(&3));
++    debug_assert_ne!(u32_ref(&3), 1);
++}
++
++fn func_mutable() {
++    debug_assert!(bool_mut(&mut 3));
++    debug_assert!(!bool_mut(&mut 3));
++
++    debug_assert_eq!(0, u32_mut(&mut 3));
++    debug_assert_eq!(u32_mut(&mut 3), 0);
++
++    debug_assert_ne!(1, u32_mut(&mut 3));
++    debug_assert_ne!(u32_mut(&mut 3), 1);
++}
++
++fn method_non_mutable() {
++    debug_assert!(S.bool_self_ref());
++    debug_assert!(S.bool_self_ref_arg_ref(&3));
++
++    debug_assert_eq!(S.u32_self_ref(), 0);
++    debug_assert_eq!(S.u32_self_ref_arg_ref(&3), 0);
++
++    debug_assert_ne!(S.u32_self_ref(), 1);
++    debug_assert_ne!(S.u32_self_ref_arg_ref(&3), 1);
++}
++
++fn method_mutable() {
++    debug_assert!(S.bool_self_mut());
++    debug_assert!(!S.bool_self_mut());
++    debug_assert!(S.bool_self_ref_arg_mut(&mut 3));
++    debug_assert!(S.bool_self_mut_arg_ref(&3));
++    debug_assert!(S.bool_self_mut_arg_mut(&mut 3));
++
++    debug_assert_eq!(S.u32_self_mut(), 0);
++    debug_assert_eq!(S.u32_self_mut_arg_ref(&3), 0);
++    debug_assert_eq!(S.u32_self_ref_arg_mut(&mut 3), 0);
++    debug_assert_eq!(S.u32_self_mut_arg_mut(&mut 3), 0);
++
++    debug_assert_ne!(S.u32_self_mut(), 1);
++    debug_assert_ne!(S.u32_self_mut_arg_ref(&3), 1);
++    debug_assert_ne!(S.u32_self_ref_arg_mut(&mut 3), 1);
++    debug_assert_ne!(S.u32_self_mut_arg_mut(&mut 3), 1);
++}
++
++fn misc() {
++    // with variable
++    let mut v: Vec<u32> = vec![1, 2, 3, 4];
++    debug_assert_eq!(v.get(0), Some(&1));
++    debug_assert_ne!(v[0], 2);
++    debug_assert_eq!(v.pop(), Some(1));
++    debug_assert_ne!(Some(3), v.pop());
++
++    let a = &mut 3;
++    debug_assert!(bool_mut(a));
++
++    // nested
++    debug_assert!(!(bool_ref(&u32_mut(&mut 3))));
++
++    // chained
++    debug_assert_eq!(v.pop().unwrap(), 3);
++
++    // format args
++    debug_assert!(bool_ref(&3), "w/o format");
++    debug_assert!(bool_mut(&mut 3), "w/o format");
++    debug_assert!(bool_ref(&3), "{} format", "w/");
++    debug_assert!(bool_mut(&mut 3), "{} format", "w/");
++
++    // sub block
++    let mut x = 42_u32;
++    debug_assert!({
++        bool_mut(&mut x);
++        x > 10
++    });
++
++    // closures
++    debug_assert!((|| {
++        let mut x = 42;
++        bool_mut(&mut x);
++        x > 10
++    })());
++}
++
++async fn debug_await() {
++    debug_assert!(async {
++        true
++    }.await);
++}
++
++fn main() {
++    func_non_mutable();
++    func_mutable();
++    method_non_mutable();
++    method_mutable();
++
++    misc();
++    debug_await();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a2ca71b57a6fdf029db0662acdca29fcbcabb9bb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,172 @@@
++error: do not call a function with mutable arguments inside of `debug_assert!`
++  --> $DIR/debug_assert_with_mut_call.rs:42:19
++   |
++LL |     debug_assert!(bool_mut(&mut 3));
++   |                   ^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::debug-assert-with-mut-call` implied by `-D warnings`
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++  --> $DIR/debug_assert_with_mut_call.rs:43:20
++   |
++LL |     debug_assert!(!bool_mut(&mut 3));
++   |                    ^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_eq!`
++  --> $DIR/debug_assert_with_mut_call.rs:45:25
++   |
++LL |     debug_assert_eq!(0, u32_mut(&mut 3));
++   |                         ^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_eq!`
++  --> $DIR/debug_assert_with_mut_call.rs:46:22
++   |
++LL |     debug_assert_eq!(u32_mut(&mut 3), 0);
++   |                      ^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_ne!`
++  --> $DIR/debug_assert_with_mut_call.rs:48:25
++   |
++LL |     debug_assert_ne!(1, u32_mut(&mut 3));
++   |                         ^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_ne!`
++  --> $DIR/debug_assert_with_mut_call.rs:49:22
++   |
++LL |     debug_assert_ne!(u32_mut(&mut 3), 1);
++   |                      ^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++  --> $DIR/debug_assert_with_mut_call.rs:64:19
++   |
++LL |     debug_assert!(S.bool_self_mut());
++   |                   ^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++  --> $DIR/debug_assert_with_mut_call.rs:65:20
++   |
++LL |     debug_assert!(!S.bool_self_mut());
++   |                    ^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++  --> $DIR/debug_assert_with_mut_call.rs:66:19
++   |
++LL |     debug_assert!(S.bool_self_ref_arg_mut(&mut 3));
++   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++  --> $DIR/debug_assert_with_mut_call.rs:67:19
++   |
++LL |     debug_assert!(S.bool_self_mut_arg_ref(&3));
++   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++  --> $DIR/debug_assert_with_mut_call.rs:68:19
++   |
++LL |     debug_assert!(S.bool_self_mut_arg_mut(&mut 3));
++   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_eq!`
++  --> $DIR/debug_assert_with_mut_call.rs:70:22
++   |
++LL |     debug_assert_eq!(S.u32_self_mut(), 0);
++   |                      ^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_eq!`
++  --> $DIR/debug_assert_with_mut_call.rs:71:22
++   |
++LL |     debug_assert_eq!(S.u32_self_mut_arg_ref(&3), 0);
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_eq!`
++  --> $DIR/debug_assert_with_mut_call.rs:72:22
++   |
++LL |     debug_assert_eq!(S.u32_self_ref_arg_mut(&mut 3), 0);
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_eq!`
++  --> $DIR/debug_assert_with_mut_call.rs:73:22
++   |
++LL |     debug_assert_eq!(S.u32_self_mut_arg_mut(&mut 3), 0);
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_ne!`
++  --> $DIR/debug_assert_with_mut_call.rs:75:22
++   |
++LL |     debug_assert_ne!(S.u32_self_mut(), 1);
++   |                      ^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_ne!`
++  --> $DIR/debug_assert_with_mut_call.rs:76:22
++   |
++LL |     debug_assert_ne!(S.u32_self_mut_arg_ref(&3), 1);
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_ne!`
++  --> $DIR/debug_assert_with_mut_call.rs:77:22
++   |
++LL |     debug_assert_ne!(S.u32_self_ref_arg_mut(&mut 3), 1);
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_ne!`
++  --> $DIR/debug_assert_with_mut_call.rs:78:22
++   |
++LL |     debug_assert_ne!(S.u32_self_mut_arg_mut(&mut 3), 1);
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_eq!`
++  --> $DIR/debug_assert_with_mut_call.rs:86:22
++   |
++LL |     debug_assert_eq!(v.pop(), Some(1));
++   |                      ^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_ne!`
++  --> $DIR/debug_assert_with_mut_call.rs:87:31
++   |
++LL |     debug_assert_ne!(Some(3), v.pop());
++   |                               ^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++  --> $DIR/debug_assert_with_mut_call.rs:90:19
++   |
++LL |     debug_assert!(bool_mut(a));
++   |                   ^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++  --> $DIR/debug_assert_with_mut_call.rs:93:31
++   |
++LL |     debug_assert!(!(bool_ref(&u32_mut(&mut 3))));
++   |                               ^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert_eq!`
++  --> $DIR/debug_assert_with_mut_call.rs:96:22
++   |
++LL |     debug_assert_eq!(v.pop().unwrap(), 3);
++   |                      ^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++  --> $DIR/debug_assert_with_mut_call.rs:100:19
++   |
++LL |     debug_assert!(bool_mut(&mut 3), "w/o format");
++   |                   ^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++  --> $DIR/debug_assert_with_mut_call.rs:102:19
++   |
++LL |     debug_assert!(bool_mut(&mut 3), "{} format", "w/");
++   |                   ^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++  --> $DIR/debug_assert_with_mut_call.rs:107:9
++   |
++LL |         bool_mut(&mut x);
++   |         ^^^^^^^^^^^^^^^^
++
++error: do not call a function with mutable arguments inside of `debug_assert!`
++  --> $DIR/debug_assert_with_mut_call.rs:114:9
++   |
++LL |         bool_mut(&mut x);
++   |         ^^^^^^^^^^^^^^^^
++
++error: aborting due to 28 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..de391465125cec9fa3f702983a6f00b9169945db
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++// run-rustfix
++
++#[warn(clippy::decimal_literal_representation)]
++#[allow(unused_variables)]
++#[rustfmt::skip]
++fn main() {
++    let good = (       // Hex:
++        127,           // 0x7F
++        256,           // 0x100
++        511,           // 0x1FF
++        2048,          // 0x800
++        4090,          // 0xFFA
++        16_371,        // 0x3FF3
++        61_683,        // 0xF0F3
++        2_131_750_925, // 0x7F0F_F00D
++    );
++    let bad = (        // Hex:
++        0x8005,        // 0x8005
++        0xFF00,        // 0xFF00
++        0x7F0F_F00F, // 0x7F0F_F00F
++        0x7FFF_FFFF, // 0x7FFF_FFFF
++        #[allow(overflowing_literals)]
++        0xF0F0_F0F0, // 0xF0F0_F0F0
++        0x8005_usize,   // 0x8005_usize
++        0x7F0F_F00F_isize, // 0x7F0F_F00F_isize
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..55d07698e7e5bba7b3950697b7683ecfc5c9f80f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++// run-rustfix
++
++#[warn(clippy::decimal_literal_representation)]
++#[allow(unused_variables)]
++#[rustfmt::skip]
++fn main() {
++    let good = (       // Hex:
++        127,           // 0x7F
++        256,           // 0x100
++        511,           // 0x1FF
++        2048,          // 0x800
++        4090,          // 0xFFA
++        16_371,        // 0x3FF3
++        61_683,        // 0xF0F3
++        2_131_750_925, // 0x7F0F_F00D
++    );
++    let bad = (        // Hex:
++        32_773,        // 0x8005
++        65_280,        // 0xFF00
++        2_131_750_927, // 0x7F0F_F00F
++        2_147_483_647, // 0x7FFF_FFFF
++        #[allow(overflowing_literals)]
++        4_042_322_160, // 0xF0F0_F0F0
++        32_773usize,   // 0x8005_usize
++        2_131_750_927isize, // 0x7F0F_F00F_isize
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8d50c8f83db44e51f640a0166999ab00a6a1f925
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++error: integer literal has a better hexadecimal representation
++  --> $DIR/decimal_literal_representation.rs:18:9
++   |
++LL |         32_773,        // 0x8005
++   |         ^^^^^^ help: consider: `0x8005`
++   |
++   = note: `-D clippy::decimal-literal-representation` implied by `-D warnings`
++
++error: integer literal has a better hexadecimal representation
++  --> $DIR/decimal_literal_representation.rs:19:9
++   |
++LL |         65_280,        // 0xFF00
++   |         ^^^^^^ help: consider: `0xFF00`
++
++error: integer literal has a better hexadecimal representation
++  --> $DIR/decimal_literal_representation.rs:20:9
++   |
++LL |         2_131_750_927, // 0x7F0F_F00F
++   |         ^^^^^^^^^^^^^ help: consider: `0x7F0F_F00F`
++
++error: integer literal has a better hexadecimal representation
++  --> $DIR/decimal_literal_representation.rs:21:9
++   |
++LL |         2_147_483_647, // 0x7FFF_FFFF
++   |         ^^^^^^^^^^^^^ help: consider: `0x7FFF_FFFF`
++
++error: integer literal has a better hexadecimal representation
++  --> $DIR/decimal_literal_representation.rs:23:9
++   |
++LL |         4_042_322_160, // 0xF0F0_F0F0
++   |         ^^^^^^^^^^^^^ help: consider: `0xF0F0_F0F0`
++
++error: integer literal has a better hexadecimal representation
++  --> $DIR/decimal_literal_representation.rs:24:9
++   |
++LL |         32_773usize,   // 0x8005_usize
++   |         ^^^^^^^^^^^ help: consider: `0x8005_usize`
++
++error: integer literal has a better hexadecimal representation
++  --> $DIR/decimal_literal_representation.rs:25:9
++   |
++LL |         2_131_750_927isize, // 0x7F0F_F00F_isize
++   |         ^^^^^^^^^^^^^^^^^^ help: consider: `0x7F0F_F00F_isize`
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b4003ed8932d37bd214e7160db8757fc7cfbedd4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,93 @@@
++#![warn(clippy::declare_interior_mutable_const)]
++
++use std::borrow::Cow;
++use std::cell::Cell;
++use std::fmt::Display;
++use std::sync::atomic::AtomicUsize;
++use std::sync::Once;
++
++const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable
++const CELL: Cell<usize> = Cell::new(6); //~ ERROR interior mutable
++const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
++//~^ ERROR interior mutable
++
++macro_rules! declare_const {
++    ($name:ident: $ty:ty = $e:expr) => {
++        const $name: $ty = $e;
++    };
++}
++declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable
++
++// const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492.
++
++const INTEGER: u8 = 8;
++const STRING: String = String::new();
++const STR: &str = "012345";
++const COW: Cow<str> = Cow::Borrowed("abcdef");
++//^ note: a const item of Cow is used in the `postgres` package.
++
++const NO_ANN: &dyn Display = &70;
++
++static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
++//^ there should be no lints on this line
++
++#[allow(clippy::declare_interior_mutable_const)]
++const ONCE_INIT: Once = Once::new();
++
++trait Trait<T>: Copy {
++    type NonCopyType;
++
++    const ATOMIC: AtomicUsize; //~ ERROR interior mutable
++    const INTEGER: u64;
++    const STRING: String;
++    const SELF: Self; // (no error)
++    const INPUT: T;
++    //~^ ERROR interior mutable
++    //~| HELP consider requiring `T` to be `Copy`
++    const ASSOC: Self::NonCopyType;
++    //~^ ERROR interior mutable
++    //~| HELP consider requiring `<Self as Trait<T>>::NonCopyType` to be `Copy`
++
++    const AN_INPUT: T = Self::INPUT;
++    //~^ ERROR interior mutable
++    //~| ERROR consider requiring `T` to be `Copy`
++    declare_const!(ANOTHER_INPUT: T = Self::INPUT); //~ ERROR interior mutable
++}
++
++trait Trait2 {
++    type CopyType: Copy;
++
++    const SELF_2: Self;
++    //~^ ERROR interior mutable
++    //~| HELP consider requiring `Self` to be `Copy`
++    const ASSOC_2: Self::CopyType; // (no error)
++}
++
++// we don't lint impl of traits, because an impl has no power to change the interface.
++impl Trait<u32> for u64 {
++    type NonCopyType = u16;
++
++    const ATOMIC: AtomicUsize = AtomicUsize::new(9);
++    const INTEGER: u64 = 10;
++    const STRING: String = String::new();
++    const SELF: Self = 11;
++    const INPUT: u32 = 12;
++    const ASSOC: Self::NonCopyType = 13;
++}
++
++struct Local<T, U>(T, U);
++
++impl<T: Trait2 + Trait<u32>, U: Trait2> Local<T, U> {
++    const ASSOC_3: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable
++    const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy");
++    const T_SELF: T = T::SELF_2;
++    const U_SELF: U = U::SELF_2;
++    //~^ ERROR interior mutable
++    //~| HELP consider requiring `U` to be `Copy`
++    const T_ASSOC: T::NonCopyType = T::ASSOC;
++    //~^ ERROR interior mutable
++    //~| HELP consider requiring `<T as Trait<u32>>::NonCopyType` to be `Copy`
++    const U_ASSOC: U::CopyType = U::ASSOC_2;
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6a9a57361f9f39fe1c762988bfdc4e6b632898a5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,110 @@@
++error: a `const` item should never be interior mutable
++  --> $DIR/declare_interior_mutable_const.rs:9:1
++   |
++LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable
++   | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   | |
++   | make this a static item (maybe with lazy_static)
++   |
++   = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
++
++error: a `const` item should never be interior mutable
++  --> $DIR/declare_interior_mutable_const.rs:10:1
++   |
++LL | const CELL: Cell<usize> = Cell::new(6); //~ ERROR interior mutable
++   | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   | |
++   | make this a static item (maybe with lazy_static)
++
++error: a `const` item should never be interior mutable
++  --> $DIR/declare_interior_mutable_const.rs:11:1
++   |
++LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
++   | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   | |
++   | make this a static item (maybe with lazy_static)
++
++error: a `const` item should never be interior mutable
++  --> $DIR/declare_interior_mutable_const.rs:16:9
++   |
++LL |         const $name: $ty = $e;
++   |         ^^^^^^^^^^^^^^^^^^^^^^
++...
++LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable
++   | ------------------------------------------ in this macro invocation
++   |
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: a `const` item should never be interior mutable
++  --> $DIR/declare_interior_mutable_const.rs:40:5
++   |
++LL |     const ATOMIC: AtomicUsize; //~ ERROR interior mutable
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: a `const` item should never be interior mutable
++  --> $DIR/declare_interior_mutable_const.rs:44:5
++   |
++LL |     const INPUT: T;
++   |     ^^^^^^^^^^^^^-^
++   |                  |
++   |                  consider requiring `T` to be `Copy`
++
++error: a `const` item should never be interior mutable
++  --> $DIR/declare_interior_mutable_const.rs:47:5
++   |
++LL |     const ASSOC: Self::NonCopyType;
++   |     ^^^^^^^^^^^^^-----------------^
++   |                  |
++   |                  consider requiring `<Self as Trait<T>>::NonCopyType` to be `Copy`
++
++error: a `const` item should never be interior mutable
++  --> $DIR/declare_interior_mutable_const.rs:51:5
++   |
++LL |     const AN_INPUT: T = Self::INPUT;
++   |     ^^^^^^^^^^^^^^^^-^^^^^^^^^^^^^^^
++   |                     |
++   |                     consider requiring `T` to be `Copy`
++
++error: a `const` item should never be interior mutable
++  --> $DIR/declare_interior_mutable_const.rs:16:9
++   |
++LL |         const $name: $ty = $e;
++   |         ^^^^^^^^^^^^^^^^^^^^^^
++...
++LL |     declare_const!(ANOTHER_INPUT: T = Self::INPUT); //~ ERROR interior mutable
++   |     ----------------------------------------------- in this macro invocation
++   |
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: a `const` item should never be interior mutable
++  --> $DIR/declare_interior_mutable_const.rs:60:5
++   |
++LL |     const SELF_2: Self;
++   |     ^^^^^^^^^^^^^^----^
++   |                   |
++   |                   consider requiring `Self` to be `Copy`
++
++error: a `const` item should never be interior mutable
++  --> $DIR/declare_interior_mutable_const.rs:81:5
++   |
++LL |     const ASSOC_3: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: a `const` item should never be interior mutable
++  --> $DIR/declare_interior_mutable_const.rs:84:5
++   |
++LL |     const U_SELF: U = U::SELF_2;
++   |     ^^^^^^^^^^^^^^-^^^^^^^^^^^^^
++   |                   |
++   |                   consider requiring `U` to be `Copy`
++
++error: a `const` item should never be interior mutable
++  --> $DIR/declare_interior_mutable_const.rs:87:5
++   |
++LL |     const T_ASSOC: T::NonCopyType = T::ASSOC;
++   |     ^^^^^^^^^^^^^^^--------------^^^^^^^^^^^^
++   |                    |
++   |                    consider requiring `<T as Trait<u32>>::NonCopyType` to be `Copy`
++
++error: aborting due to 13 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2a948d60b108954b6a6e31980f960449ac91c835
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++// ignore-windows
++// ignore-macos
++
++#![feature(no_core, lang_items, start)]
++#![no_core]
++
++#[link(name = "c")]
++extern "C" {}
++
++#[lang = "sized"]
++pub trait Sized {}
++#[lang = "copy"]
++pub trait Copy {}
++#[lang = "freeze"]
++pub unsafe trait Freeze {}
++
++#[lang = "start"]
++#[start]
++fn start(_argc: isize, _argv: *const *const u8) -> isize {
++    0
++}
++
++pub struct A;
++
++impl A {
++    pub fn as_ref(self) -> &'static str {
++        "A"
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ed87a50547d17398a13790c1ab9ad7e4114aabe0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
++  --> $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`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..053faae02ce3e0d3aa457083224bb9d32b20ba30
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++#![deny(clippy::internal)]
++#![feature(rustc_private)]
++
++#[macro_use]
++extern crate rustc_middle;
++#[macro_use]
++extern crate rustc_session;
++extern crate rustc_lint;
++
++declare_tool_lint! {
++    pub clippy::TEST_LINT,
++    Warn,
++    "",
++    report_in_external_macro: true
++}
++
++declare_tool_lint! {
++    pub clippy::TEST_LINT_DEFAULT,
++    Warn,
++    "default lint description",
++    report_in_external_macro: true
++}
++
++declare_lint_pass!(Pass => [TEST_LINT]);
++declare_lint_pass!(Pass2 => [TEST_LINT_DEFAULT]);
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5c5836a7d297e82a0ae0c18b819d73ef39b3caf9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++error: the lint `TEST_LINT_DEFAULT` has the default lint description
++  --> $DIR/default_lint.rs:17:1
++   |
++LL | / declare_tool_lint! {
++LL | |     pub clippy::TEST_LINT_DEFAULT,
++LL | |     Warn,
++LL | |     "default lint description",
++LL | |     report_in_external_macro: true
++LL | | }
++   | |_^
++   |
++note: the lint level is defined here
++  --> $DIR/default_lint.rs:1:9
++   |
++LL | #![deny(clippy::internal)]
++   |         ^^^^^^^^^^^^^^^^
++   = note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]`
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2f1490a70369e267a340f726fd9227cd3d9fe6b6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,103 @@@
++#![warn(clippy::default_trait_access)]
++
++use std::default;
++use std::default::Default as D2;
++use std::string;
++
++fn main() {
++    let s1: String = Default::default();
++
++    let s2 = String::default();
++
++    let s3: String = D2::default();
++
++    let s4: String = std::default::Default::default();
++
++    let s5 = string::String::default();
++
++    let s6: String = default::Default::default();
++
++    let s7 = std::string::String::default();
++
++    let s8: String = DefaultFactory::make_t_badly();
++
++    let s9: String = DefaultFactory::make_t_nicely();
++
++    let s10 = DerivedDefault::default();
++
++    let s11: GenericDerivedDefault<String> = Default::default();
++
++    let s12 = GenericDerivedDefault::<String>::default();
++
++    let s13 = TupleDerivedDefault::default();
++
++    let s14: TupleDerivedDefault = Default::default();
++
++    let s15: ArrayDerivedDefault = Default::default();
++
++    let s16 = ArrayDerivedDefault::default();
++
++    let s17: TupleStructDerivedDefault = Default::default();
++
++    let s18 = TupleStructDerivedDefault::default();
++
++    let s19 = <DerivedDefault as Default>::default();
++
++    println!(
++        "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]",
++        s1,
++        s2,
++        s3,
++        s4,
++        s5,
++        s6,
++        s7,
++        s8,
++        s9,
++        s10,
++        s11,
++        s12,
++        s13,
++        s14,
++        s15,
++        s16,
++        s17,
++        s18,
++        s19,
++    );
++}
++
++struct DefaultFactory;
++
++impl DefaultFactory {
++    pub fn make_t_badly<T: Default>() -> T {
++        Default::default()
++    }
++
++    pub fn make_t_nicely<T: Default>() -> T {
++        T::default()
++    }
++}
++
++#[derive(Debug, Default)]
++struct DerivedDefault {
++    pub s: String,
++}
++
++#[derive(Debug, Default)]
++struct GenericDerivedDefault<T: Default + std::fmt::Debug> {
++    pub s: T,
++}
++
++#[derive(Debug, Default)]
++struct TupleDerivedDefault {
++    pub s: (String, String),
++}
++
++#[derive(Debug, Default)]
++struct ArrayDerivedDefault {
++    pub s: [String; 10],
++}
++
++#[derive(Debug, Default)]
++struct TupleStructDerivedDefault(String);
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..453760c6b9211c338130d9fadad206af116440e2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++error: Calling `std::string::String::default()` is more clear than this expression
++  --> $DIR/default_trait_access.rs:8:22
++   |
++LL |     let s1: String = Default::default();
++   |                      ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
++   |
++   = note: `-D clippy::default-trait-access` implied by `-D warnings`
++
++error: Calling `std::string::String::default()` is more clear than this expression
++  --> $DIR/default_trait_access.rs:12:22
++   |
++LL |     let s3: String = D2::default();
++   |                      ^^^^^^^^^^^^^ help: try: `std::string::String::default()`
++
++error: Calling `std::string::String::default()` is more clear than this expression
++  --> $DIR/default_trait_access.rs:14:22
++   |
++LL |     let s4: String = std::default::Default::default();
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
++
++error: Calling `std::string::String::default()` is more clear than this expression
++  --> $DIR/default_trait_access.rs:18:22
++   |
++LL |     let s6: String = default::Default::default();
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
++
++error: Calling `GenericDerivedDefault<std::string::String>::default()` is more clear than this expression
++  --> $DIR/default_trait_access.rs:28:46
++   |
++LL |     let s11: GenericDerivedDefault<String> = Default::default();
++   |                                              ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault<std::string::String>::default()`
++
++error: Calling `TupleDerivedDefault::default()` is more clear than this expression
++  --> $DIR/default_trait_access.rs:34:36
++   |
++LL |     let s14: TupleDerivedDefault = Default::default();
++   |                                    ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()`
++
++error: Calling `ArrayDerivedDefault::default()` is more clear than this expression
++  --> $DIR/default_trait_access.rs:36:36
++   |
++LL |     let s15: ArrayDerivedDefault = Default::default();
++   |                                    ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()`
++
++error: Calling `TupleStructDerivedDefault::default()` is more clear than this expression
++  --> $DIR/default_trait_access.rs:40:42
++   |
++LL |     let s17: TupleStructDerivedDefault = Default::default();
++   |                                          ^^^^^^^^^^^^^^^^^^ help: try: `TupleStructDerivedDefault::default()`
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..188a641aa1af25db2288d2162f5b6b7edea46f5e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++#[warn(clippy::str_to_string)]
++#[warn(clippy::string_to_string)]
++#[warn(clippy::unstable_as_slice)]
++#[warn(clippy::unstable_as_mut_slice)]
++#[warn(clippy::misaligned_transmute)]
++#[warn(clippy::unused_collect)]
++#[warn(clippy::invalid_ref)]
++#[warn(clippy::into_iter_on_array)]
++#[warn(clippy::unused_label)]
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a4efe3d15a952e100cbfb4e59c4e94cb303ac1e9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,64 @@@
++error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon`
++  --> $DIR/deprecated.rs:1:8
++   |
++LL | #[warn(clippy::str_to_string)]
++   |        ^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D renamed-and-removed-lints` implied by `-D warnings`
++
++error: lint `clippy::string_to_string` has been removed: `using `string::to_string` is common even today and specialization will likely happen soon`
++  --> $DIR/deprecated.rs:2:8
++   |
++LL | #[warn(clippy::string_to_string)]
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: lint `clippy::unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7`
++  --> $DIR/deprecated.rs:3:8
++   |
++LL | #[warn(clippy::unstable_as_slice)]
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: lint `clippy::unstable_as_mut_slice` has been removed: ``Vec::as_mut_slice` has been stabilized in 1.7`
++  --> $DIR/deprecated.rs:4:8
++   |
++LL | #[warn(clippy::unstable_as_mut_slice)]
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: lint `clippy::misaligned_transmute` has been removed: `this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr`
++  --> $DIR/deprecated.rs:5:8
++   |
++LL | #[warn(clippy::misaligned_transmute)]
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: lint `clippy::unused_collect` has been removed: ``collect` has been marked as #[must_use] in rustc and that covers all cases of this lint`
++  --> $DIR/deprecated.rs:6:8
++   |
++LL | #[warn(clippy::unused_collect)]
++   |        ^^^^^^^^^^^^^^^^^^^^^^
++
++error: lint `clippy::invalid_ref` has been removed: `superseded by rustc lint `invalid_value``
++  --> $DIR/deprecated.rs:7:8
++   |
++LL | #[warn(clippy::invalid_ref)]
++   |        ^^^^^^^^^^^^^^^^^^^
++
++error: lint `clippy::into_iter_on_array` has been removed: `this lint has been uplifted to rustc and is now called `array_into_iter``
++  --> $DIR/deprecated.rs:8:8
++   |
++LL | #[warn(clippy::into_iter_on_array)]
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: lint `clippy::unused_label` has been removed: `this lint has been uplifted to rustc and is now called `unused_labels``
++  --> $DIR/deprecated.rs:9:8
++   |
++LL | #[warn(clippy::unused_label)]
++   |        ^^^^^^^^^^^^^^^^^^^^
++
++error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon`
++  --> $DIR/deprecated.rs:1:8
++   |
++LL | #[warn(clippy::str_to_string)]
++   |        ^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 10 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2e5c5b7ead12c1e2fd81b2816473939feb5b508f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++#[warn(str_to_string)]
++#[warn(string_to_string)]
++#[warn(unstable_as_slice)]
++#[warn(unstable_as_mut_slice)]
++#[warn(misaligned_transmute)]
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ff3e9e8fcf36763e57e282ad0f8149ce67eadb3c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: lint `str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon`
++  --> $DIR/deprecated_old.rs:1:8
++   |
++LL | #[warn(str_to_string)]
++   |        ^^^^^^^^^^^^^
++   |
++   = note: `-D renamed-and-removed-lints` implied by `-D warnings`
++
++error: lint `string_to_string` has been removed: `using `string::to_string` is common even today and specialization will likely happen soon`
++  --> $DIR/deprecated_old.rs:2:8
++   |
++LL | #[warn(string_to_string)]
++   |        ^^^^^^^^^^^^^^^^
++
++error: lint `unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7`
++  --> $DIR/deprecated_old.rs:3:8
++   |
++LL | #[warn(unstable_as_slice)]
++   |        ^^^^^^^^^^^^^^^^^
++
++error: lint `unstable_as_mut_slice` has been removed: ``Vec::as_mut_slice` has been stabilized in 1.7`
++  --> $DIR/deprecated_old.rs:4:8
++   |
++LL | #[warn(unstable_as_mut_slice)]
++   |        ^^^^^^^^^^^^^^^^^^^^^
++
++error: lint `misaligned_transmute` has been removed: `this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr`
++  --> $DIR/deprecated_old.rs:5:8
++   |
++LL | #[warn(misaligned_transmute)]
++   |        ^^^^^^^^^^^^^^^^^^^^
++
++error: lint `str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon`
++  --> $DIR/deprecated_old.rs:1:8
++   |
++LL | #[warn(str_to_string)]
++   |        ^^^^^^^^^^^^^
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9e5b51d6d5e6d5da7344e0bdc33bacb163882010
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++// run-rustfix
++
++fn get_number() -> usize {
++    10
++}
++
++fn get_reference(n: &usize) -> &usize {
++    n
++}
++
++#[allow(clippy::many_single_char_names, clippy::double_parens)]
++#[allow(unused_variables, unused_parens)]
++#[warn(clippy::deref_addrof)]
++fn main() {
++    let a = 10;
++    let aref = &a;
++
++    let b = a;
++
++    let b = get_number();
++
++    let b = *get_reference(&a);
++
++    let bytes: Vec<usize> = vec![1, 2, 3, 4];
++    let b = bytes[1..2][0];
++
++    //This produces a suggestion of 'let b = (a);' which
++    //will trigger the 'unused_parens' lint
++    let b = (a);
++
++    let b = a;
++
++    #[rustfmt::skip]
++    let b = a;
++
++    let b = &a;
++
++    let b = *aref;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5641a73cbc1cb1b73e766dbbfb8de1d515a12e6d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++// run-rustfix
++
++fn get_number() -> usize {
++    10
++}
++
++fn get_reference(n: &usize) -> &usize {
++    n
++}
++
++#[allow(clippy::many_single_char_names, clippy::double_parens)]
++#[allow(unused_variables, unused_parens)]
++#[warn(clippy::deref_addrof)]
++fn main() {
++    let a = 10;
++    let aref = &a;
++
++    let b = *&a;
++
++    let b = *&get_number();
++
++    let b = *get_reference(&a);
++
++    let bytes: Vec<usize> = vec![1, 2, 3, 4];
++    let b = *&bytes[1..2][0];
++
++    //This produces a suggestion of 'let b = (a);' which
++    //will trigger the 'unused_parens' lint
++    let b = *&(a);
++
++    let b = *(&a);
++
++    #[rustfmt::skip]
++    let b = *((&a));
++
++    let b = *&&a;
++
++    let b = **&aref;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bc51719e8a7a0e6bb9dfd4a4c4137208b59e8891
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++error: immediately dereferencing a reference
++  --> $DIR/deref_addrof.rs:18:13
++   |
++LL |     let b = *&a;
++   |             ^^^ help: try this: `a`
++   |
++   = note: `-D clippy::deref-addrof` implied by `-D warnings`
++
++error: immediately dereferencing a reference
++  --> $DIR/deref_addrof.rs:20:13
++   |
++LL |     let b = *&get_number();
++   |             ^^^^^^^^^^^^^^ help: try this: `get_number()`
++
++error: immediately dereferencing a reference
++  --> $DIR/deref_addrof.rs:25:13
++   |
++LL |     let b = *&bytes[1..2][0];
++   |             ^^^^^^^^^^^^^^^^ help: try this: `bytes[1..2][0]`
++
++error: immediately dereferencing a reference
++  --> $DIR/deref_addrof.rs:29:13
++   |
++LL |     let b = *&(a);
++   |             ^^^^^ help: try this: `(a)`
++
++error: immediately dereferencing a reference
++  --> $DIR/deref_addrof.rs:31:13
++   |
++LL |     let b = *(&a);
++   |             ^^^^^ help: try this: `a`
++
++error: immediately dereferencing a reference
++  --> $DIR/deref_addrof.rs:34:13
++   |
++LL |     let b = *((&a));
++   |             ^^^^^^^ help: try this: `a`
++
++error: immediately dereferencing a reference
++  --> $DIR/deref_addrof.rs:36:13
++   |
++LL |     let b = *&&a;
++   |             ^^^^ help: try this: `&a`
++
++error: immediately dereferencing a reference
++  --> $DIR/deref_addrof.rs:38:14
++   |
++LL |     let b = **&aref;
++   |              ^^^^^^ help: try this: `aref`
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4531943299cd1206aa0388229522e40334d92ad9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++// This test can't work with run-rustfix because it needs two passes of test+fix
++
++#[warn(clippy::deref_addrof)]
++#[allow(unused_variables, unused_mut)]
++fn main() {
++    let a = 10;
++
++    //This produces a suggestion of 'let b = *&a;' which
++    //will trigger the 'clippy::deref_addrof' lint again
++    let b = **&&a;
++
++    {
++        let mut x = 10;
++        let y = *&mut x;
++    }
++
++    {
++        //This produces a suggestion of 'let y = *&mut x' which
++        //will trigger the 'clippy::deref_addrof' lint again
++        let mut x = 10;
++        let y = **&mut &mut x;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2c55a4ed6acdb269663a7d92b482a65d3c3e9370
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: immediately dereferencing a reference
++  --> $DIR/deref_addrof_double_trigger.rs:10:14
++   |
++LL |     let b = **&&a;
++   |              ^^^^ help: try this: `&a`
++   |
++   = note: `-D clippy::deref-addrof` implied by `-D warnings`
++
++error: immediately dereferencing a reference
++  --> $DIR/deref_addrof_double_trigger.rs:14:17
++   |
++LL |         let y = *&mut x;
++   |                 ^^^^^^^ help: try this: `x`
++
++error: immediately dereferencing a reference
++  --> $DIR/deref_addrof_double_trigger.rs:21:18
++   |
++LL |         let y = **&mut &mut x;
++   |                  ^^^^^^^^^^^^ help: try this: `&mut x`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dcebd6c6e29c8474d4537d8e3d1a659051922b09
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++macro_rules! m {
++    ($($x:tt),*) => { &[$(($x, stringify!(x)),)*] };
++}
++
++#[warn(clippy::deref_addrof)]
++fn f() -> [(i32, &'static str); 3] {
++    *m![1, 2, 3] // should be fine
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..459ca91b93b9e5b7dc4f67d7481e8a32ad8a7884
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,93 @@@
++// 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();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8dc5272e67fa529fb5afc0bc8a6d1db62a51bdc1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,93 @@@
++// 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();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d26b462a4336236b79472bc5dd8b001f99c08473
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,70 @@@
++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
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8fcb0e8b28d0905bbdbca6cb45317f7a699842b8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,74 @@@
++#![feature(untagged_unions)]
++#![allow(dead_code)]
++#![warn(clippy::expl_impl_clone_on_copy)]
++
++#[derive(Copy)]
++struct Qux;
++
++impl Clone for Qux {
++    fn clone(&self) -> Self {
++        Qux
++    }
++}
++
++// looks like unions don't support deriving Clone for now
++#[derive(Copy)]
++union Union {
++    a: u8,
++}
++
++impl Clone for Union {
++    fn clone(&self) -> Self {
++        Union { a: 42 }
++    }
++}
++
++// See #666
++#[derive(Copy)]
++struct Lt<'a> {
++    a: &'a u8,
++}
++
++impl<'a> Clone for Lt<'a> {
++    fn clone(&self) -> Self {
++        unimplemented!()
++    }
++}
++
++// Ok, `Clone` cannot be derived because of the big array
++#[derive(Copy)]
++struct BigArray {
++    a: [u8; 65],
++}
++
++impl Clone for BigArray {
++    fn clone(&self) -> Self {
++        unimplemented!()
++    }
++}
++
++// Ok, function pointers are not always Clone
++#[derive(Copy)]
++struct FnPtr {
++    a: fn() -> !,
++}
++
++impl Clone for FnPtr {
++    fn clone(&self) -> Self {
++        unimplemented!()
++    }
++}
++
++// Ok, generics
++#[derive(Copy)]
++struct Generic<T> {
++    a: T,
++}
++
++impl<T> Clone for Generic<T> {
++    fn clone(&self) -> Self {
++        unimplemented!()
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1328a9b31077e0182722c3b1c4479cfd138728e6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,83 @@@
++error: you are implementing `Clone` explicitly on a `Copy` type
++  --> $DIR/derive.rs:8:1
++   |
++LL | / impl Clone for Qux {
++LL | |     fn clone(&self) -> Self {
++LL | |         Qux
++LL | |     }
++LL | | }
++   | |_^
++   |
++   = note: `-D clippy::expl-impl-clone-on-copy` implied by `-D warnings`
++note: consider deriving `Clone` or removing `Copy`
++  --> $DIR/derive.rs:8:1
++   |
++LL | / impl Clone for Qux {
++LL | |     fn clone(&self) -> Self {
++LL | |         Qux
++LL | |     }
++LL | | }
++   | |_^
++
++error: you are implementing `Clone` explicitly on a `Copy` type
++  --> $DIR/derive.rs:32:1
++   |
++LL | / impl<'a> Clone for Lt<'a> {
++LL | |     fn clone(&self) -> Self {
++LL | |         unimplemented!()
++LL | |     }
++LL | | }
++   | |_^
++   |
++note: consider deriving `Clone` or removing `Copy`
++  --> $DIR/derive.rs:32:1
++   |
++LL | / impl<'a> Clone for Lt<'a> {
++LL | |     fn clone(&self) -> Self {
++LL | |         unimplemented!()
++LL | |     }
++LL | | }
++   | |_^
++
++error: you are implementing `Clone` explicitly on a `Copy` type
++  --> $DIR/derive.rs:44:1
++   |
++LL | / impl Clone for BigArray {
++LL | |     fn clone(&self) -> Self {
++LL | |         unimplemented!()
++LL | |     }
++LL | | }
++   | |_^
++   |
++note: consider deriving `Clone` or removing `Copy`
++  --> $DIR/derive.rs:44:1
++   |
++LL | / impl Clone for BigArray {
++LL | |     fn clone(&self) -> Self {
++LL | |         unimplemented!()
++LL | |     }
++LL | | }
++   | |_^
++
++error: you are implementing `Clone` explicitly on a `Copy` type
++  --> $DIR/derive.rs:56:1
++   |
++LL | / impl Clone for FnPtr {
++LL | |     fn clone(&self) -> Self {
++LL | |         unimplemented!()
++LL | |     }
++LL | | }
++   | |_^
++   |
++note: consider deriving `Clone` or removing `Copy`
++  --> $DIR/derive.rs:56:1
++   |
++LL | / impl Clone for FnPtr {
++LL | |     fn clone(&self) -> Self {
++LL | |         unimplemented!()
++LL | |     }
++LL | | }
++   | |_^
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..10abe22d53e5c1338c8f136eacc301e8c77e4128
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,54 @@@
++#[derive(PartialEq, Hash)]
++struct Foo;
++
++impl PartialEq<u64> for Foo {
++    fn eq(&self, _: &u64) -> bool {
++        true
++    }
++}
++
++#[derive(Hash)]
++struct Bar;
++
++impl PartialEq for Bar {
++    fn eq(&self, _: &Bar) -> bool {
++        true
++    }
++}
++
++#[derive(Hash)]
++struct Baz;
++
++impl PartialEq<Baz> for Baz {
++    fn eq(&self, _: &Baz) -> bool {
++        true
++    }
++}
++
++#[derive(PartialEq)]
++struct Bah;
++
++impl std::hash::Hash for Bah {
++    fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
++}
++
++#[derive(PartialEq)]
++struct Foo2;
++
++trait Hash {}
++
++// We don't want to lint on user-defined traits called `Hash`
++impl Hash for Foo2 {}
++
++mod use_hash {
++    use std::hash::{Hash, Hasher};
++
++    #[derive(PartialEq)]
++    struct Foo3;
++
++    impl Hash for Foo3 {
++        fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2287a548fe46cc67a8b3de8de09ea609a97424e7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,67 @@@
++error: you are deriving `Hash` but have implemented `PartialEq` explicitly
++  --> $DIR/derive_hash_xor_eq.rs:10:10
++   |
++LL | #[derive(Hash)]
++   |          ^^^^
++   |
++   = note: `#[deny(clippy::derive_hash_xor_eq)]` on by default
++note: `PartialEq` implemented here
++  --> $DIR/derive_hash_xor_eq.rs:13:1
++   |
++LL | / impl PartialEq for Bar {
++LL | |     fn eq(&self, _: &Bar) -> bool {
++LL | |         true
++LL | |     }
++LL | | }
++   | |_^
++   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: you are deriving `Hash` but have implemented `PartialEq` explicitly
++  --> $DIR/derive_hash_xor_eq.rs:19:10
++   |
++LL | #[derive(Hash)]
++   |          ^^^^
++   |
++note: `PartialEq` implemented here
++  --> $DIR/derive_hash_xor_eq.rs:22:1
++   |
++LL | / impl PartialEq<Baz> for Baz {
++LL | |     fn eq(&self, _: &Baz) -> bool {
++LL | |         true
++LL | |     }
++LL | | }
++   | |_^
++   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: you are implementing `Hash` explicitly but have derived `PartialEq`
++  --> $DIR/derive_hash_xor_eq.rs:31:1
++   |
++LL | / impl std::hash::Hash for Bah {
++LL | |     fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
++LL | | }
++   | |_^
++   |
++note: `PartialEq` implemented here
++  --> $DIR/derive_hash_xor_eq.rs:28:10
++   |
++LL | #[derive(PartialEq)]
++   |          ^^^^^^^^^
++   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: you are implementing `Hash` explicitly but have derived `PartialEq`
++  --> $DIR/derive_hash_xor_eq.rs:49:5
++   |
++LL | /     impl Hash for Foo3 {
++LL | |         fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
++LL | |     }
++   | |_____^
++   |
++note: `PartialEq` implemented here
++  --> $DIR/derive_hash_xor_eq.rs:46:14
++   |
++LL |     #[derive(PartialEq)]
++   |              ^^^^^^^^^
++   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4df241c9fc39be35b1fe3a00ac017e637ad69e82
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++#![warn(clippy::diverging_sub_expression)]
++#![allow(clippy::match_same_arms, clippy::logic_bug)]
++
++#[allow(clippy::empty_loop)]
++fn diverge() -> ! {
++    loop {}
++}
++
++struct A;
++
++impl A {
++    fn foo(&self) -> ! {
++        diverge()
++    }
++}
++
++#[allow(unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)]
++fn main() {
++    let b = true;
++    b || diverge();
++    b || A.foo();
++}
++
++#[allow(dead_code, unused_variables)]
++fn foobar() {
++    loop {
++        let x = match 5 {
++            4 => return,
++            5 => continue,
++            6 => true || return,
++            7 => true || continue,
++            8 => break,
++            9 => diverge(),
++            3 => true || diverge(),
++            10 => match 42 {
++                99 => return,
++                _ => true || panic!("boo"),
++            },
++            _ => true || break,
++        };
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..170e7d92de4acff6b643a1ead8d762e2d61e2d8b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: sub-expression diverges
++  --> $DIR/diverging_sub_expression.rs:20:10
++   |
++LL |     b || diverge();
++   |          ^^^^^^^^^
++   |
++   = note: `-D clippy::diverging-sub-expression` implied by `-D warnings`
++
++error: sub-expression diverges
++  --> $DIR/diverging_sub_expression.rs:21:10
++   |
++LL |     b || A.foo();
++   |          ^^^^^^^
++
++error: sub-expression diverges
++  --> $DIR/diverging_sub_expression.rs:30:26
++   |
++LL |             6 => true || return,
++   |                          ^^^^^^
++
++error: sub-expression diverges
++  --> $DIR/diverging_sub_expression.rs:31:26
++   |
++LL |             7 => true || continue,
++   |                          ^^^^^^^^
++
++error: sub-expression diverges
++  --> $DIR/diverging_sub_expression.rs:34:26
++   |
++LL |             3 => true || diverge(),
++   |                          ^^^^^^^^^
++
++error: sub-expression diverges
++  --> $DIR/diverging_sub_expression.rs:39:26
++   |
++LL |             _ => true || break,
++   |                          ^^^^^
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2940d2d29011006641b553fe38fed49047d7dffd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++#![feature(associated_type_defaults)]
++#![warn(clippy::linkedlist)]
++#![allow(dead_code, clippy::needless_pass_by_value)]
++
++extern crate alloc;
++use alloc::collections::linked_list::LinkedList;
++
++trait Foo {
++    type Baz = LinkedList<u8>;
++    fn foo(_: LinkedList<u8>);
++    const BAR: Option<LinkedList<u8>>;
++}
++
++// Ok, we don’t want to warn for implementations; see issue #605.
++impl Foo for LinkedList<u8> {
++    fn foo(_: LinkedList<u8>) {}
++    const BAR: Option<LinkedList<u8>> = None;
++}
++
++struct Bar;
++impl Bar {
++    fn foo(_: LinkedList<u8>) {}
++}
++
++pub fn test(my_favourite_linked_list: LinkedList<u8>) {
++    println!("{:?}", my_favourite_linked_list)
++}
++
++pub fn test_ret() -> Option<LinkedList<u8>> {
++    unimplemented!();
++}
++
++pub fn test_local_not_linted() {
++    let _: LinkedList<u8>;
++}
++
++fn main() {
++    test(LinkedList::new());
++    test_local_not_linted();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..64fde33c64f5228edb53bd57ad423972c1e4320d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++error: I see you're using a LinkedList! Perhaps you meant some other data structure?
++  --> $DIR/dlist.rs:9:16
++   |
++LL |     type Baz = LinkedList<u8>;
++   |                ^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::linkedlist` implied by `-D warnings`
++   = help: a `VecDeque` might work
++
++error: I see you're using a LinkedList! Perhaps you meant some other data structure?
++  --> $DIR/dlist.rs:10:15
++   |
++LL |     fn foo(_: LinkedList<u8>);
++   |               ^^^^^^^^^^^^^^
++   |
++   = help: a `VecDeque` might work
++
++error: I see you're using a LinkedList! Perhaps you meant some other data structure?
++  --> $DIR/dlist.rs:11:23
++   |
++LL |     const BAR: Option<LinkedList<u8>>;
++   |                       ^^^^^^^^^^^^^^
++   |
++   = help: a `VecDeque` might work
++
++error: I see you're using a LinkedList! Perhaps you meant some other data structure?
++  --> $DIR/dlist.rs:22:15
++   |
++LL |     fn foo(_: LinkedList<u8>) {}
++   |               ^^^^^^^^^^^^^^
++   |
++   = help: a `VecDeque` might work
++
++error: I see you're using a LinkedList! Perhaps you meant some other data structure?
++  --> $DIR/dlist.rs:25:39
++   |
++LL | pub fn test(my_favourite_linked_list: LinkedList<u8>) {
++   |                                       ^^^^^^^^^^^^^^
++   |
++   = help: a `VecDeque` might work
++
++error: I see you're using a LinkedList! Perhaps you meant some other data structure?
++  --> $DIR/dlist.rs:29:29
++   |
++LL | pub fn test_ret() -> Option<LinkedList<u8>> {
++   |                             ^^^^^^^^^^^^^^
++   |
++   = help: a `VecDeque` might work
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..77620c857e66e2ca52e5f40fc0887eba94f0fd1e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,184 @@@
++//! This file tests for the `DOC_MARKDOWN` lint.
++
++#![allow(dead_code)]
++#![warn(clippy::doc_markdown)]
++#![feature(custom_inner_attributes)]
++#![rustfmt::skip]
++
++/// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
++/// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not Foo::some_fun
++/// which should be reported only once despite being __doubly bad__.
++/// Here be ::a::global:path.
++/// That's not code ~NotInCodeBlock~.
++/// be_sure_we_got_to_the_end_of_it
++fn foo_bar() {
++}
++
++/// That one tests multiline ticks.
++/// ```rust
++/// foo_bar FOO_BAR
++/// _foo bar_
++/// ```
++///
++/// ~~~rust
++/// foo_bar FOO_BAR
++/// _foo bar_
++/// ~~~
++/// be_sure_we_got_to_the_end_of_it
++fn multiline_codeblock() {
++}
++
++/// This _is a test for
++/// multiline
++/// emphasis_.
++/// be_sure_we_got_to_the_end_of_it
++fn test_emphasis() {
++}
++
++/// This tests units. See also #835.
++/// kiB MiB GiB TiB PiB EiB
++/// kib Mib Gib Tib Pib Eib
++/// kB MB GB TB PB EB
++/// kb Mb Gb Tb Pb Eb
++/// 32kiB 32MiB 32GiB 32TiB 32PiB 32EiB
++/// 32kib 32Mib 32Gib 32Tib 32Pib 32Eib
++/// 32kB 32MB 32GB 32TB 32PB 32EB
++/// 32kb 32Mb 32Gb 32Tb 32Pb 32Eb
++/// NaN
++/// be_sure_we_got_to_the_end_of_it
++fn test_units() {
++}
++
++/// This test has [a link_with_underscores][chunked-example] inside it. See #823.
++/// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues)
++/// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link].
++/// It can also be [inline_link2].
++///
++/// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example
++/// [inline_link]: https://foobar
++/// [inline_link2]: https://foobar
++/// The `main` function is the entry point of the program. Here it only calls the `foo_bar` and
++/// `multiline_ticks` functions.
++///
++/// expression of the type  `_ <bit_op> m <cmp_op> c` (where `<bit_op>`
++/// is one of {`&`, '|'} and `<cmp_op>` is one of {`!=`, `>=`, `>` ,
++/// be_sure_we_got_to_the_end_of_it
++fn main() {
++    foo_bar();
++    multiline_codeblock();
++    test_emphasis();
++    test_units();
++}
++
++/// ## CamelCaseThing
++/// Talks about `CamelCaseThing`. Titles should be ignored; see issue #897.
++///
++/// # CamelCaseThing
++///
++/// Not a title #897 CamelCaseThing
++/// be_sure_we_got_to_the_end_of_it
++fn issue897() {
++}
++
++/// I am confused by brackets? (`x_y`)
++/// I am confused by brackets? (foo `x_y`)
++/// I am confused by brackets? (`x_y` foo)
++/// be_sure_we_got_to_the_end_of_it
++fn issue900() {
++}
++
++/// Diesel queries also have a similar problem to [Iterator][iterator], where
++/// /// More talking
++/// returning them from a function requires exposing the implementation of that
++/// function. The [`helper_types`][helper_types] module exists to help with this,
++/// but you might want to hide the return type or have it conditionally change.
++/// Boxing can achieve both.
++///
++/// [iterator]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html
++/// [helper_types]: ../helper_types/index.html
++/// be_sure_we_got_to_the_end_of_it
++fn issue883() {
++}
++
++/// `foo_bar
++/// baz_quz`
++/// [foo
++/// bar](https://doc.rust-lang.org/stable/std/iter/trait.IteratorFooBar.html)
++fn multiline() {
++}
++
++/** E.g., serialization of an empty list: FooBar
++```
++That's in a code block: `PackedNode`
++```
++
++And BarQuz too.
++be_sure_we_got_to_the_end_of_it
++*/
++fn issue1073() {
++}
++
++/** E.g., serialization of an empty list: FooBar
++```
++That's in a code block: PackedNode
++```
++
++And BarQuz too.
++be_sure_we_got_to_the_end_of_it
++*/
++fn issue1073_alt() {
++}
++
++/// Tests more than three quotes:
++/// ````
++/// DoNotWarn
++/// ```
++/// StillDont
++/// ````
++/// be_sure_we_got_to_the_end_of_it
++fn four_quotes() {
++}
++
++/// See [NIST SP 800-56A, revision 2].
++///
++/// [NIST SP 800-56A, revision 2]:
++///     https://github.com/rust-lang/rust-clippy/issues/902#issuecomment-261919419
++fn issue_902_comment() {}
++
++#[cfg_attr(feature = "a", doc = " ```")]
++#[cfg_attr(not(feature = "a"), doc = " ```ignore")]
++/// fn main() {
++///     let s = "localhost:10000".to_string();
++///     println!("{}", s);
++/// }
++/// ```
++fn issue_1469() {}
++
++/**
++ * This is a doc comment that should not be a list
++ *This would also be an error under a strict common mark interpretation
++ */
++fn issue_1920() {}
++
++/// Ok: <http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels>
++///
++/// Not ok: http://www.unicode.org
++/// Not ok: https://www.unicode.org
++/// Not ok: http://www.unicode.org/
++/// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels
++fn issue_1832() {}
++
++/// Ok: CamelCase (It should not be surrounded by backticks)
++fn issue_2395() {}
++
++/// An iterator over mycrate::Collection's values.
++/// It should not lint a `'static` lifetime in ticks.
++fn issue_2210() {}
++
++/// This should not cause the lint to trigger:
++/// #REQ-data-family.lint_partof_exists
++fn issue_2343() {}
++
++/// This should not cause an ICE:
++/// __|_ _|__||_|
++fn pulldown_cmark_crash() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ae9bb394cb9ac726bcca6129a5e7bb1c3349741d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,184 @@@
++error: you should put `foo_bar` between ticks in the documentation
++  --> $DIR/doc.rs:8:9
++   |
++LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
++   |         ^^^^^^^
++   |
++   = note: `-D clippy::doc-markdown` implied by `-D warnings`
++
++error: you should put `foo::bar` between ticks in the documentation
++  --> $DIR/doc.rs:8:51
++   |
++LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
++   |                                                   ^^^^^^^^
++
++error: you should put `Foo::some_fun` between ticks in the documentation
++  --> $DIR/doc.rs:9:83
++   |
++LL | /// Markdown is _weird_. I mean _really weird_. This /_ is ok. So is `_`. But not Foo::some_fun
++   |                                                                                   ^^^^^^^^^^^^^
++
++error: you should put `a::global:path` between ticks in the documentation
++  --> $DIR/doc.rs:11:15
++   |
++LL | /// Here be ::a::global:path.
++   |               ^^^^^^^^^^^^^^
++
++error: you should put `NotInCodeBlock` between ticks in the documentation
++  --> $DIR/doc.rs:12:22
++   |
++LL | /// That's not code ~NotInCodeBlock~.
++   |                      ^^^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:13:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:27:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:34:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:48:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `link_with_underscores` between ticks in the documentation
++  --> $DIR/doc.rs:52:22
++   |
++LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823.
++   |                      ^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `inline_link2` between ticks in the documentation
++  --> $DIR/doc.rs:55:21
++   |
++LL | /// It can also be [inline_link2].
++   |                     ^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:65:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `CamelCaseThing` between ticks in the documentation
++  --> $DIR/doc.rs:73:8
++   |
++LL | /// ## CamelCaseThing
++   |        ^^^^^^^^^^^^^^
++
++error: you should put `CamelCaseThing` between ticks in the documentation
++  --> $DIR/doc.rs:76:7
++   |
++LL | /// # CamelCaseThing
++   |       ^^^^^^^^^^^^^^
++
++error: you should put `CamelCaseThing` between ticks in the documentation
++  --> $DIR/doc.rs:78:22
++   |
++LL | /// Not a title #897 CamelCaseThing
++   |                      ^^^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:79:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:86:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:99:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `FooBar` between ticks in the documentation
++  --> $DIR/doc.rs:110:43
++   |
++LL | /** E.g., serialization of an empty list: FooBar
++   |                                           ^^^^^^
++
++error: you should put `BarQuz` between ticks in the documentation
++  --> $DIR/doc.rs:115:5
++   |
++LL | And BarQuz too.
++   |     ^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:116:1
++   |
++LL | be_sure_we_got_to_the_end_of_it
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `FooBar` between ticks in the documentation
++  --> $DIR/doc.rs:121:43
++   |
++LL | /** E.g., serialization of an empty list: FooBar
++   |                                           ^^^^^^
++
++error: you should put `BarQuz` between ticks in the documentation
++  --> $DIR/doc.rs:126:5
++   |
++LL | And BarQuz too.
++   |     ^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:127:1
++   |
++LL | be_sure_we_got_to_the_end_of_it
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
++  --> $DIR/doc.rs:138:5
++   |
++LL | /// be_sure_we_got_to_the_end_of_it
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put bare URLs between `<`/`>` or make a proper Markdown link
++  --> $DIR/doc.rs:165:13
++   |
++LL | /// Not ok: http://www.unicode.org
++   |             ^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put bare URLs between `<`/`>` or make a proper Markdown link
++  --> $DIR/doc.rs:166:13
++   |
++LL | /// Not ok: https://www.unicode.org
++   |             ^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put bare URLs between `<`/`>` or make a proper Markdown link
++  --> $DIR/doc.rs:167:13
++   |
++LL | /// Not ok: http://www.unicode.org/
++   |             ^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put bare URLs between `<`/`>` or make a proper Markdown link
++  --> $DIR/doc.rs:168:13
++   |
++LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you should put `mycrate::Collection` between ticks in the documentation
++  --> $DIR/doc.rs:174:22
++   |
++LL | /// An iterator over mycrate::Collection's values.
++   |                      ^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 30 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..445fc8d31d77a4ed794d58fbb3528aaa4f9c4874
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,103 @@@
++// edition:2018
++#![warn(clippy::missing_errors_doc)]
++
++use std::io;
++
++pub fn pub_fn_missing_errors_header() -> Result<(), ()> {
++    unimplemented!();
++}
++
++pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> {
++    unimplemented!();
++}
++
++/// This is not sufficiently documented.
++pub fn pub_fn_returning_io_result() -> io::Result<()> {
++    unimplemented!();
++}
++
++/// This is not sufficiently documented.
++pub async fn async_pub_fn_returning_io_result() -> io::Result<()> {
++    unimplemented!();
++}
++
++/// # Errors
++/// A description of the errors goes here.
++pub fn pub_fn_with_errors_header() -> Result<(), ()> {
++    unimplemented!();
++}
++
++/// # Errors
++/// A description of the errors goes here.
++pub async fn async_pub_fn_with_errors_header() -> Result<(), ()> {
++    unimplemented!();
++}
++
++/// This function doesn't require the documentation because it is private
++fn priv_fn_missing_errors_header() -> Result<(), ()> {
++    unimplemented!();
++}
++
++/// This function doesn't require the documentation because it is private
++async fn async_priv_fn_missing_errors_header() -> Result<(), ()> {
++    unimplemented!();
++}
++
++pub struct Struct1;
++
++impl Struct1 {
++    /// This is not sufficiently documented.
++    pub fn pub_method_missing_errors_header() -> Result<(), ()> {
++        unimplemented!();
++    }
++
++    /// This is not sufficiently documented.
++    pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> {
++        unimplemented!();
++    }
++
++    /// # Errors
++    /// A description of the errors goes here.
++    pub fn pub_method_with_errors_header() -> Result<(), ()> {
++        unimplemented!();
++    }
++
++    /// # Errors
++    /// A description of the errors goes here.
++    pub async fn async_pub_method_with_errors_header() -> Result<(), ()> {
++        unimplemented!();
++    }
++
++    /// This function doesn't require the documentation because it is private.
++    fn priv_method_missing_errors_header() -> Result<(), ()> {
++        unimplemented!();
++    }
++
++    /// This function doesn't require the documentation because it is private.
++    async fn async_priv_method_missing_errors_header() -> Result<(), ()> {
++        unimplemented!();
++    }
++}
++
++pub trait Trait1 {
++    /// This is not sufficiently documented.
++    fn trait_method_missing_errors_header() -> Result<(), ()>;
++
++    /// # Errors
++    /// A description of the errors goes here.
++    fn trait_method_with_errors_header() -> Result<(), ()>;
++}
++
++impl Trait1 for Struct1 {
++    fn trait_method_missing_errors_header() -> Result<(), ()> {
++        unimplemented!();
++    }
++
++    fn trait_method_with_errors_header() -> Result<(), ()> {
++        unimplemented!();
++    }
++}
++
++fn main() -> Result<(), ()> {
++    Ok(())
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f44d6693d303b699a3c17c6821de9b7f2405a973
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++error: docs for function returning `Result` missing `# Errors` section
++  --> $DIR/doc_errors.rs:6:1
++   |
++LL | / pub fn pub_fn_missing_errors_header() -> Result<(), ()> {
++LL | |     unimplemented!();
++LL | | }
++   | |_^
++   |
++   = note: `-D clippy::missing-errors-doc` implied by `-D warnings`
++
++error: docs for function returning `Result` missing `# Errors` section
++  --> $DIR/doc_errors.rs:10:1
++   |
++LL | / pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> {
++LL | |     unimplemented!();
++LL | | }
++   | |_^
++
++error: docs for function returning `Result` missing `# Errors` section
++  --> $DIR/doc_errors.rs:15:1
++   |
++LL | / pub fn pub_fn_returning_io_result() -> io::Result<()> {
++LL | |     unimplemented!();
++LL | | }
++   | |_^
++
++error: docs for function returning `Result` missing `# Errors` section
++  --> $DIR/doc_errors.rs:20:1
++   |
++LL | / pub async fn async_pub_fn_returning_io_result() -> io::Result<()> {
++LL | |     unimplemented!();
++LL | | }
++   | |_^
++
++error: docs for function returning `Result` missing `# Errors` section
++  --> $DIR/doc_errors.rs:50:5
++   |
++LL | /     pub fn pub_method_missing_errors_header() -> Result<(), ()> {
++LL | |         unimplemented!();
++LL | |     }
++   | |_____^
++
++error: docs for function returning `Result` missing `# Errors` section
++  --> $DIR/doc_errors.rs:55:5
++   |
++LL | /     pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> {
++LL | |         unimplemented!();
++LL | |     }
++   | |_____^
++
++error: docs for function returning `Result` missing `# Errors` section
++  --> $DIR/doc_errors.rs:84:5
++   |
++LL |     fn trait_method_missing_errors_header() -> Result<(), ()>;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..484aa72d59a25a43c6276ebf4d48dd7ba12e7c01
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,100 @@@
++// aux-build:doc_unsafe_macros.rs
++
++#[macro_use]
++extern crate doc_unsafe_macros;
++
++/// This is not sufficiently documented
++pub unsafe fn destroy_the_planet() {
++    unimplemented!();
++}
++
++/// This one is
++///
++/// # Safety
++///
++/// This function shouldn't be called unless the horsemen are ready
++pub unsafe fn apocalypse(universe: &mut ()) {
++    unimplemented!();
++}
++
++/// This is a private function, so docs aren't necessary
++unsafe fn you_dont_see_me() {
++    unimplemented!();
++}
++
++mod private_mod {
++    pub unsafe fn only_crate_wide_accessible() {
++        unimplemented!();
++    }
++
++    pub unsafe fn republished() {
++        unimplemented!();
++    }
++}
++
++pub use private_mod::republished;
++
++pub trait UnsafeTrait {
++    unsafe fn woefully_underdocumented(self);
++
++    /// # Safety
++    unsafe fn at_least_somewhat_documented(self);
++}
++
++pub struct Struct;
++
++impl UnsafeTrait for Struct {
++    unsafe fn woefully_underdocumented(self) {
++        // all is well
++    }
++
++    unsafe fn at_least_somewhat_documented(self) {
++        // all is still well
++    }
++}
++
++impl Struct {
++    pub unsafe fn more_undocumented_unsafe() -> Self {
++        unimplemented!();
++    }
++
++    /// # Safety
++    pub unsafe fn somewhat_documented(&self) {
++        unimplemented!();
++    }
++
++    unsafe fn private(&self) {
++        unimplemented!();
++    }
++}
++
++macro_rules! very_unsafe {
++    () => {
++        pub unsafe fn whee() {
++            unimplemented!()
++        }
++
++        /// # Safety
++        ///
++        /// Please keep the seat belt fastened
++        pub unsafe fn drive() {
++            whee()
++        }
++    };
++}
++
++very_unsafe!();
++
++// we don't lint code from external macros
++undocd_unsafe!();
++
++fn main() {
++    unsafe {
++        you_dont_see_me();
++        destroy_the_planet();
++        let mut universe = ();
++        apocalypse(&mut universe);
++        private_mod::only_crate_wide_accessible();
++        drive();
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c784d41ba1724815db75e43289b7e082db391fed
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++error: unsafe function's docs miss `# Safety` section
++  --> $DIR/doc_unsafe.rs:7:1
++   |
++LL | / pub unsafe fn destroy_the_planet() {
++LL | |     unimplemented!();
++LL | | }
++   | |_^
++   |
++   = note: `-D clippy::missing-safety-doc` implied by `-D warnings`
++
++error: unsafe function's docs miss `# Safety` section
++  --> $DIR/doc_unsafe.rs:30:5
++   |
++LL | /     pub unsafe fn republished() {
++LL | |         unimplemented!();
++LL | |     }
++   | |_____^
++
++error: unsafe function's docs miss `# Safety` section
++  --> $DIR/doc_unsafe.rs:38:5
++   |
++LL |     unsafe fn woefully_underdocumented(self);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: unsafe function's docs miss `# Safety` section
++  --> $DIR/doc_unsafe.rs:57:5
++   |
++LL | /     pub unsafe fn more_undocumented_unsafe() -> Self {
++LL | |         unimplemented!();
++LL | |     }
++   | |_____^
++
++error: unsafe function's docs miss `# Safety` section
++  --> $DIR/doc_unsafe.rs:73:9
++   |
++LL | /         pub unsafe fn whee() {
++LL | |             unimplemented!()
++LL | |         }
++   | |_________^
++...
++LL |   very_unsafe!();
++   |   --------------- 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 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bb6cdaa667d4fae37dda61453a4ce7c0030031e6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++// run-rustfix
++
++fn main() {
++    let x = 1;
++    let y = 2;
++    if x <= y {
++        // do something
++    }
++    if x <= y {
++        // do something
++    }
++    if x >= y {
++        // do something
++    }
++    if x >= y {
++        // do something
++    }
++    if x != y {
++        // do something
++    }
++    if x != y {
++        // do something
++    }
++    if x == y {
++        // do something
++    }
++    if x == y {
++        // do something
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9a2a9068a28debe71293c7a2c19f013962f265c6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++// run-rustfix
++
++fn main() {
++    let x = 1;
++    let y = 2;
++    if x == y || x < y {
++        // do something
++    }
++    if x < y || x == y {
++        // do something
++    }
++    if x == y || x > y {
++        // do something
++    }
++    if x > y || x == y {
++        // do something
++    }
++    if x < y || x > y {
++        // do something
++    }
++    if x > y || x < y {
++        // do something
++    }
++    if x <= y && x >= y {
++        // do something
++    }
++    if x >= y && x <= y {
++        // do something
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5dcda7b3af4ac48187d6d84a3559793199f87593
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++error: This binary expression can be simplified
++  --> $DIR/double_comparison.rs:6:8
++   |
++LL |     if x == y || x < y {
++   |        ^^^^^^^^^^^^^^^ help: try: `x <= y`
++   |
++   = note: `-D clippy::double-comparisons` implied by `-D warnings`
++
++error: This binary expression can be simplified
++  --> $DIR/double_comparison.rs:9:8
++   |
++LL |     if x < y || x == y {
++   |        ^^^^^^^^^^^^^^^ help: try: `x <= y`
++
++error: This binary expression can be simplified
++  --> $DIR/double_comparison.rs:12:8
++   |
++LL |     if x == y || x > y {
++   |        ^^^^^^^^^^^^^^^ help: try: `x >= y`
++
++error: This binary expression can be simplified
++  --> $DIR/double_comparison.rs:15:8
++   |
++LL |     if x > y || x == y {
++   |        ^^^^^^^^^^^^^^^ help: try: `x >= y`
++
++error: This binary expression can be simplified
++  --> $DIR/double_comparison.rs:18:8
++   |
++LL |     if x < y || x > y {
++   |        ^^^^^^^^^^^^^^ help: try: `x != y`
++
++error: This binary expression can be simplified
++  --> $DIR/double_comparison.rs:21:8
++   |
++LL |     if x > y || x < y {
++   |        ^^^^^^^^^^^^^^ help: try: `x != y`
++
++error: This binary expression can be simplified
++  --> $DIR/double_comparison.rs:24:8
++   |
++LL |     if x <= y && x >= y {
++   |        ^^^^^^^^^^^^^^^^ help: try: `x == y`
++
++error: This binary expression can be simplified
++  --> $DIR/double_comparison.rs:27:8
++   |
++LL |     if x >= y && x <= y {
++   |        ^^^^^^^^^^^^^^^^ help: try: `x == y`
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a48e675e4ea23e543bf54c53417d5af9a3dcb26d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++#![warn(clippy::double_must_use)]
++
++#[must_use]
++pub fn must_use_result() -> Result<(), ()> {
++    unimplemented!();
++}
++
++#[must_use]
++pub fn must_use_tuple() -> (Result<(), ()>, u8) {
++    unimplemented!();
++}
++
++#[must_use]
++pub fn must_use_array() -> [Result<(), ()>; 1] {
++    unimplemented!();
++}
++
++#[must_use = "With note"]
++pub fn must_use_with_note() -> Result<(), ()> {
++    unimplemented!();
++}
++
++fn main() {
++    must_use_result();
++    must_use_tuple();
++    must_use_with_note();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bc37785294fca6f9c4b863eea4a7bf0d2be2931c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
++  --> $DIR/double_must_use.rs:4:1
++   |
++LL | pub fn must_use_result() -> Result<(), ()> {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::double-must-use` implied by `-D warnings`
++   = help: either add some descriptive text or remove the attribute
++
++error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
++  --> $DIR/double_must_use.rs:9:1
++   |
++LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: either add some descriptive text or remove the attribute
++
++error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
++  --> $DIR/double_must_use.rs:14:1
++   |
++LL | pub fn must_use_array() -> [Result<(), ()>; 1] {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: either add some descriptive text or remove the attribute
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d47dfcb5ba1eab1b8654a2c270700748923a4bee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++#[warn(clippy::double_neg)]
++fn main() {
++    let x = 1;
++    -x;
++    -(-x);
++    --x;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d82ed05f0543dbf65a7fb5768c39ac7703561081
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: `--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op
++  --> $DIR/double_neg.rs:6:5
++   |
++LL |     --x;
++   |     ^^^
++   |
++   = note: `-D clippy::double-neg` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9c7590c7dd6324bc3afe069cf22bce3def645366
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++#![warn(clippy::double_parens)]
++#![allow(dead_code)]
++#![feature(custom_inner_attributes)]
++#![rustfmt::skip]
++
++fn dummy_fn<T>(_: T) {}
++
++struct DummyStruct;
++
++impl DummyStruct {
++    fn dummy_method<T>(self, _: T) {}
++}
++
++fn simple_double_parens() -> i32 {
++    ((0))
++}
++
++fn fn_double_parens() {
++    dummy_fn((0));
++}
++
++fn method_double_parens(x: DummyStruct) {
++    x.dummy_method((0));
++}
++
++fn tuple_double_parens() -> (i32, i32) {
++    ((1, 2))
++}
++
++fn unit_double_parens() {
++    (())
++}
++
++fn fn_tuple_ok() {
++    dummy_fn((1, 2));
++}
++
++fn method_tuple_ok(x: DummyStruct) {
++    x.dummy_method((1, 2));
++}
++
++fn fn_unit_ok() {
++    dummy_fn(());
++}
++
++fn method_unit_ok(x: DummyStruct) {
++    x.dummy_method(());
++}
++
++// Issue #3206
++fn inside_macro() {
++    assert_eq!((1, 2), (1, 2), "Error");
++    assert_eq!(((1, 2)), (1, 2), "Error");
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0e4c9b5682dfb4748f74c808cdf56321583451ee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: Consider removing unnecessary double parentheses
++  --> $DIR/double_parens.rs:15:5
++   |
++LL |     ((0))
++   |     ^^^^^
++   |
++   = note: `-D clippy::double-parens` implied by `-D warnings`
++
++error: Consider removing unnecessary double parentheses
++  --> $DIR/double_parens.rs:19:14
++   |
++LL |     dummy_fn((0));
++   |              ^^^
++
++error: Consider removing unnecessary double parentheses
++  --> $DIR/double_parens.rs:23:20
++   |
++LL |     x.dummy_method((0));
++   |                    ^^^
++
++error: Consider removing unnecessary double parentheses
++  --> $DIR/double_parens.rs:27:5
++   |
++LL |     ((1, 2))
++   |     ^^^^^^^^
++
++error: Consider removing unnecessary double parentheses
++  --> $DIR/double_parens.rs:31:5
++   |
++LL |     (())
++   |     ^^^^
++
++error: Consider removing unnecessary double parentheses
++  --> $DIR/double_parens.rs:53:16
++   |
++LL |     assert_eq!(((1, 2)), (1, 2), "Error");
++   |                ^^^^^^^^
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6d6a9dc0783993f4f2e63520c78a23eb5c0f3331
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++#![allow(unused)]
++fn foo<T: Drop>() {}
++fn bar<T>()
++where
++    T: Drop,
++{
++}
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d360ef30a1d89048c017a061e64167a2acbd9a5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue.
++  --> $DIR/drop_bounds.rs:2:11
++   |
++LL | fn foo<T: Drop>() {}
++   |           ^^^^
++   |
++   = note: `#[deny(clippy::drop_bounds)]` on by default
++
++error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue.
++  --> $DIR/drop_bounds.rs:5:8
++   |
++LL |     T: Drop,
++   |        ^^^^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9ddd6d64701a61dd615ec0b1d1f5d5451601617f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,66 @@@
++#![warn(clippy::drop_copy, clippy::forget_copy)]
++#![allow(clippy::toplevel_ref_arg, clippy::drop_ref, clippy::forget_ref, unused_mut)]
++
++use std::mem::{drop, forget};
++use std::vec::Vec;
++
++#[derive(Copy, Clone)]
++struct SomeStruct {}
++
++struct AnotherStruct {
++    x: u8,
++    y: u8,
++    z: Vec<u8>,
++}
++
++impl Clone for AnotherStruct {
++    fn clone(&self) -> AnotherStruct {
++        AnotherStruct {
++            x: self.x,
++            y: self.y,
++            z: self.z.clone(),
++        }
++    }
++}
++
++fn main() {
++    let s1 = SomeStruct {};
++    let s2 = s1;
++    let s3 = &s1;
++    let mut s4 = s1;
++    let ref s5 = s1;
++
++    drop(s1);
++    drop(s2);
++    drop(s3);
++    drop(s4);
++    drop(s5);
++
++    forget(s1);
++    forget(s2);
++    forget(s3);
++    forget(s4);
++    forget(s5);
++
++    let a1 = AnotherStruct {
++        x: 255,
++        y: 0,
++        z: vec![1, 2, 3],
++    };
++    let a2 = &a1;
++    let mut a3 = a1.clone();
++    let ref a4 = a1;
++    let a5 = a1.clone();
++
++    drop(a2);
++    drop(a3);
++    drop(a4);
++    drop(a5);
++
++    forget(a2);
++    let a3 = &a1;
++    forget(a3);
++    forget(a4);
++    let a5 = a1.clone();
++    forget(a5);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..82a4f047ba858a355de5833a0f715648e61ce0ff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,76 @@@
++error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact.
++  --> $DIR/drop_forget_copy.rs:33:5
++   |
++LL |     drop(s1);
++   |     ^^^^^^^^
++   |
++   = note: `-D clippy::drop-copy` implied by `-D warnings`
++note: argument has type SomeStruct
++  --> $DIR/drop_forget_copy.rs:33:10
++   |
++LL |     drop(s1);
++   |          ^^
++
++error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact.
++  --> $DIR/drop_forget_copy.rs:34:5
++   |
++LL |     drop(s2);
++   |     ^^^^^^^^
++   |
++note: argument has type SomeStruct
++  --> $DIR/drop_forget_copy.rs:34:10
++   |
++LL |     drop(s2);
++   |          ^^
++
++error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact.
++  --> $DIR/drop_forget_copy.rs:36:5
++   |
++LL |     drop(s4);
++   |     ^^^^^^^^
++   |
++note: argument has type SomeStruct
++  --> $DIR/drop_forget_copy.rs:36:10
++   |
++LL |     drop(s4);
++   |          ^^
++
++error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact.
++  --> $DIR/drop_forget_copy.rs:39:5
++   |
++LL |     forget(s1);
++   |     ^^^^^^^^^^
++   |
++   = note: `-D clippy::forget-copy` implied by `-D warnings`
++note: argument has type SomeStruct
++  --> $DIR/drop_forget_copy.rs:39:12
++   |
++LL |     forget(s1);
++   |            ^^
++
++error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact.
++  --> $DIR/drop_forget_copy.rs:40:5
++   |
++LL |     forget(s2);
++   |     ^^^^^^^^^^
++   |
++note: argument has type SomeStruct
++  --> $DIR/drop_forget_copy.rs:40:12
++   |
++LL |     forget(s2);
++   |            ^^
++
++error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact.
++  --> $DIR/drop_forget_copy.rs:42:5
++   |
++LL |     forget(s4);
++   |     ^^^^^^^^^^
++   |
++note: argument has type SomeStruct
++  --> $DIR/drop_forget_copy.rs:42:12
++   |
++LL |     forget(s4);
++   |            ^^
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9181d789d4fb1a95f6b7f01505b6014fad9660c0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,72 @@@
++#![warn(clippy::drop_ref)]
++#![allow(clippy::toplevel_ref_arg)]
++
++use std::mem::drop;
++
++struct SomeStruct;
++
++fn main() {
++    drop(&SomeStruct);
++
++    let mut owned1 = SomeStruct;
++    drop(&owned1);
++    drop(&&owned1);
++    drop(&mut owned1);
++    drop(owned1); //OK
++
++    let reference1 = &SomeStruct;
++    drop(reference1);
++
++    let reference2 = &mut SomeStruct;
++    drop(reference2);
++
++    let ref reference3 = SomeStruct;
++    drop(reference3);
++}
++
++#[allow(dead_code)]
++fn test_generic_fn_drop<T>(val: T) {
++    drop(&val);
++    drop(val); //OK
++}
++
++#[allow(dead_code)]
++fn test_similarly_named_function() {
++    fn drop<T>(_val: T) {}
++    drop(&SomeStruct); //OK; call to unrelated function which happens to have the same name
++    std::mem::drop(&SomeStruct);
++}
++
++#[derive(Copy, Clone)]
++pub struct Error;
++fn produce_half_owl_error() -> Result<(), Error> {
++    Ok(())
++}
++
++fn produce_half_owl_ok() -> Result<bool, ()> {
++    Ok(true)
++}
++
++#[allow(dead_code)]
++fn test_owl_result() -> Result<(), ()> {
++    produce_half_owl_error().map_err(|_| ())?;
++    produce_half_owl_ok().map(|_| ())?;
++    // the following should not be linted,
++    // we should not force users to use toilet closures
++    // to produce owl results when drop is more convenient
++    produce_half_owl_error().map_err(drop)?;
++    produce_half_owl_ok().map_err(drop)?;
++    Ok(())
++}
++
++#[allow(dead_code)]
++fn test_owl_result_2() -> Result<u8, ()> {
++    produce_half_owl_error().map_err(|_| ())?;
++    produce_half_owl_ok().map(|_| ())?;
++    // the following should not be linted,
++    // we should not force users to use toilet closures
++    // to produce owl results when drop is more convenient
++    produce_half_owl_error().map_err(drop)?;
++    produce_half_owl_ok().map(drop)?;
++    Ok(1)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..35ae88b78a4c5d28b9cfe56de106c8bf863e5c54
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,111 @@@
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++  --> $DIR/drop_ref.rs:9:5
++   |
++LL |     drop(&SomeStruct);
++   |     ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::drop-ref` implied by `-D warnings`
++note: argument has type `&SomeStruct`
++  --> $DIR/drop_ref.rs:9:10
++   |
++LL |     drop(&SomeStruct);
++   |          ^^^^^^^^^^^
++
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++  --> $DIR/drop_ref.rs:12:5
++   |
++LL |     drop(&owned1);
++   |     ^^^^^^^^^^^^^
++   |
++note: argument has type `&SomeStruct`
++  --> $DIR/drop_ref.rs:12:10
++   |
++LL |     drop(&owned1);
++   |          ^^^^^^^
++
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++  --> $DIR/drop_ref.rs:13:5
++   |
++LL |     drop(&&owned1);
++   |     ^^^^^^^^^^^^^^
++   |
++note: argument has type `&&SomeStruct`
++  --> $DIR/drop_ref.rs:13:10
++   |
++LL |     drop(&&owned1);
++   |          ^^^^^^^^
++
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++  --> $DIR/drop_ref.rs:14:5
++   |
++LL |     drop(&mut owned1);
++   |     ^^^^^^^^^^^^^^^^^
++   |
++note: argument has type `&mut SomeStruct`
++  --> $DIR/drop_ref.rs:14:10
++   |
++LL |     drop(&mut owned1);
++   |          ^^^^^^^^^^^
++
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++  --> $DIR/drop_ref.rs:18:5
++   |
++LL |     drop(reference1);
++   |     ^^^^^^^^^^^^^^^^
++   |
++note: argument has type `&SomeStruct`
++  --> $DIR/drop_ref.rs:18:10
++   |
++LL |     drop(reference1);
++   |          ^^^^^^^^^^
++
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++  --> $DIR/drop_ref.rs:21:5
++   |
++LL |     drop(reference2);
++   |     ^^^^^^^^^^^^^^^^
++   |
++note: argument has type `&mut SomeStruct`
++  --> $DIR/drop_ref.rs:21:10
++   |
++LL |     drop(reference2);
++   |          ^^^^^^^^^^
++
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++  --> $DIR/drop_ref.rs:24:5
++   |
++LL |     drop(reference3);
++   |     ^^^^^^^^^^^^^^^^
++   |
++note: argument has type `&SomeStruct`
++  --> $DIR/drop_ref.rs:24:10
++   |
++LL |     drop(reference3);
++   |          ^^^^^^^^^^
++
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++  --> $DIR/drop_ref.rs:29:5
++   |
++LL |     drop(&val);
++   |     ^^^^^^^^^^
++   |
++note: argument has type `&T`
++  --> $DIR/drop_ref.rs:29:10
++   |
++LL |     drop(&val);
++   |          ^^^^
++
++error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
++  --> $DIR/drop_ref.rs:37:5
++   |
++LL |     std::mem::drop(&SomeStruct);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++note: argument has type `&SomeStruct`
++  --> $DIR/drop_ref.rs:37:20
++   |
++LL |     std::mem::drop(&SomeStruct);
++   |                    ^^^^^^^^^^^
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..54d748c7ce280fc2b8af175be175a0f04502dcdb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++#![warn(clippy::duplicate_underscore_argument)]
++#[allow(dead_code, unused)]
++
++fn join_the_dark_side(darth: i32, _darth: i32) {}
++fn join_the_light_side(knight: i32, _master: i32) {} // the Force is strong with this one
++
++fn main() {
++    join_the_dark_side(0, 0);
++    join_the_light_side(0, 0);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f71614a5fd16e199c5688cc6dacf335c1d303a50
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: `darth` already exists, having another argument having almost the same name makes code comprehension and documentation more difficult
++  --> $DIR/duplicate_underscore_argument.rs:4:23
++   |
++LL | fn join_the_dark_side(darth: i32, _darth: i32) {}
++   |                       ^^^^^
++   |
++   = note: `-D clippy::duplicate-underscore-argument` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ee5c7863effcb84b34a893e18dbc7710be5673b0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++// run-rustfix
++#![allow(dead_code)]
++#![warn(clippy::duration_subsec)]
++
++use std::time::Duration;
++
++fn main() {
++    let dur = Duration::new(5, 0);
++
++    let bad_millis_1 = dur.subsec_millis();
++    let bad_millis_2 = dur.subsec_millis();
++    let good_millis = dur.subsec_millis();
++    assert_eq!(bad_millis_1, good_millis);
++    assert_eq!(bad_millis_2, good_millis);
++
++    let bad_micros = dur.subsec_micros();
++    let good_micros = dur.subsec_micros();
++    assert_eq!(bad_micros, good_micros);
++
++    // Handle refs
++    let _ = (&dur).subsec_micros();
++
++    // Handle constants
++    const NANOS_IN_MICRO: u32 = 1_000;
++    let _ = dur.subsec_micros();
++
++    // Other literals aren't linted
++    let _ = dur.subsec_nanos() / 699;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3c9d2a286211017854d57039166bf80bb18976b7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++// run-rustfix
++#![allow(dead_code)]
++#![warn(clippy::duration_subsec)]
++
++use std::time::Duration;
++
++fn main() {
++    let dur = Duration::new(5, 0);
++
++    let bad_millis_1 = dur.subsec_micros() / 1_000;
++    let bad_millis_2 = dur.subsec_nanos() / 1_000_000;
++    let good_millis = dur.subsec_millis();
++    assert_eq!(bad_millis_1, good_millis);
++    assert_eq!(bad_millis_2, good_millis);
++
++    let bad_micros = dur.subsec_nanos() / 1_000;
++    let good_micros = dur.subsec_micros();
++    assert_eq!(bad_micros, good_micros);
++
++    // Handle refs
++    let _ = (&dur).subsec_nanos() / 1_000;
++
++    // Handle constants
++    const NANOS_IN_MICRO: u32 = 1_000;
++    let _ = dur.subsec_nanos() / NANOS_IN_MICRO;
++
++    // Other literals aren't linted
++    let _ = dur.subsec_nanos() / 699;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bd8adc2c57055b20e0b515c9f24c33dbcaab902d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++error: Calling `subsec_millis()` is more concise than this calculation
++  --> $DIR/duration_subsec.rs:10:24
++   |
++LL |     let bad_millis_1 = dur.subsec_micros() / 1_000;
++   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()`
++   |
++   = note: `-D clippy::duration-subsec` implied by `-D warnings`
++
++error: Calling `subsec_millis()` is more concise than this calculation
++  --> $DIR/duration_subsec.rs:11:24
++   |
++LL |     let bad_millis_2 = dur.subsec_nanos() / 1_000_000;
++   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()`
++
++error: Calling `subsec_micros()` is more concise than this calculation
++  --> $DIR/duration_subsec.rs:16:22
++   |
++LL |     let bad_micros = dur.subsec_nanos() / 1_000;
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()`
++
++error: Calling `subsec_micros()` is more concise than this calculation
++  --> $DIR/duration_subsec.rs:21:13
++   |
++LL |     let _ = (&dur).subsec_nanos() / 1_000;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()`
++
++error: Calling `subsec_micros()` is more concise than this calculation
++  --> $DIR/duration_subsec.rs:25:13
++   |
++LL |     let _ = dur.subsec_nanos() / NANOS_IN_MICRO;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()`
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..879b3ac398e451d3553bab28a1a76371b73f8d59
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++#![warn(clippy::all)]
++#![warn(clippy::else_if_without_else)]
++
++fn bla1() -> bool {
++    unimplemented!()
++}
++fn bla2() -> bool {
++    unimplemented!()
++}
++fn bla3() -> bool {
++    unimplemented!()
++}
++
++fn main() {
++    if bla1() {
++        println!("if");
++    }
++
++    if bla1() {
++        println!("if");
++    } else {
++        println!("else");
++    }
++
++    if bla1() {
++        println!("if");
++    } else if bla2() {
++        println!("else if");
++    } else {
++        println!("else")
++    }
++
++    if bla1() {
++        println!("if");
++    } else if bla2() {
++        println!("else if 1");
++    } else if bla3() {
++        println!("else if 2");
++    } else {
++        println!("else")
++    }
++
++    if bla1() {
++        println!("if");
++    } else if bla2() {
++        //~ ERROR else if without else
++        println!("else if");
++    }
++
++    if bla1() {
++        println!("if");
++    } else if bla2() {
++        println!("else if 1");
++    } else if bla3() {
++        //~ ERROR else if without else
++        println!("else if 2");
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6f47658cfb18400f0df75a7ab24e8f10fcec8ad8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++error: `if` expression with an `else if`, but without a final `else`
++  --> $DIR/else_if_without_else.rs:45:12
++   |
++LL |       } else if bla2() {
++   |  ____________^
++LL | |         //~ ERROR else if without else
++LL | |         println!("else if");
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::else-if-without-else` implied by `-D warnings`
++   = help: add an `else` block here
++
++error: `if` expression with an `else if`, but without a final `else`
++  --> $DIR/else_if_without_else.rs:54:12
++   |
++LL |       } else if bla3() {
++   |  ____________^
++LL | |         //~ ERROR else if without else
++LL | |         println!("else if 2");
++LL | |     }
++   | |_____^
++   |
++   = help: add an `else` block here
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..12428f29625c0298abcb627b27306c0ff25d3c99
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++#![allow(dead_code)]
++#![warn(clippy::empty_enum)]
++
++enum Empty {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..466dfbe7cee7ab51cd93536709cc7eee73868fa2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++error: enum with no variants
++  --> $DIR/empty_enum.rs:4:1
++   |
++LL | enum Empty {}
++   | ^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::empty-enum` implied by `-D warnings`
++   = help: consider using the uninhabited type `!` (never type) or a wrapper around it to introduce a type which can't be instantiated
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5343dff9da1db26078505730abf8edcea2383d2c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,96 @@@
++#![warn(clippy::empty_line_after_outer_attr)]
++#![allow(clippy::assertions_on_constants)]
++#![feature(custom_inner_attributes)]
++#![rustfmt::skip]
++
++// This should produce a warning
++#[crate_type = "lib"]
++
++/// some comment
++fn with_one_newline_and_comment() { assert!(true) }
++
++// This should not produce a warning
++#[crate_type = "lib"]
++/// some comment
++fn with_no_newline_and_comment() { assert!(true) }
++
++
++// This should produce a warning
++#[crate_type = "lib"]
++
++fn with_one_newline() { assert!(true) }
++
++// This should produce a warning, too
++#[crate_type = "lib"]
++
++
++fn with_two_newlines() { assert!(true) }
++
++
++// This should produce a warning
++#[crate_type = "lib"]
++
++enum Baz {
++    One,
++    Two
++}
++
++// This should produce a warning
++#[crate_type = "lib"]
++
++struct Foo {
++    one: isize,
++    two: isize
++}
++
++// This should produce a warning
++#[crate_type = "lib"]
++
++mod foo {
++}
++
++/// This doc comment should not produce a warning
++
++/** This is also a doc comment and should not produce a warning
++ */
++
++// This should not produce a warning
++#[allow(non_camel_case_types)]
++#[allow(missing_docs)]
++#[allow(missing_docs)]
++fn three_attributes() { assert!(true) }
++
++// This should not produce a warning
++#[doc = "
++Returns the escaped value of the textual representation of
++
++"]
++pub fn function() -> bool {
++    true
++}
++
++// This should not produce a warning
++#[derive(Clone, Copy)]
++pub enum FooFighter {
++    Bar1,
++
++    Bar2,
++
++    Bar3,
++
++    Bar4
++}
++
++// This should not produce a warning because the empty line is inside a block comment
++#[crate_type = "lib"]
++/*
++
++*/
++pub struct S;
++
++// This should not produce a warning
++#[crate_type = "lib"]
++/* test */
++pub struct T;
++
++fn main() { }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d8c9786541f0b75f19a7836b73861322924b4a73
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,54 @@@
++error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
++  --> $DIR/empty_line_after_outer_attribute.rs:7:1
++   |
++LL | / #[crate_type = "lib"]
++LL | |
++LL | | /// some comment
++LL | | fn with_one_newline_and_comment() { assert!(true) }
++   | |_
++   |
++   = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
++
++error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
++  --> $DIR/empty_line_after_outer_attribute.rs:19:1
++   |
++LL | / #[crate_type = "lib"]
++LL | |
++LL | | fn with_one_newline() { assert!(true) }
++   | |_
++
++error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
++  --> $DIR/empty_line_after_outer_attribute.rs:24:1
++   |
++LL | / #[crate_type = "lib"]
++LL | |
++LL | |
++LL | | fn with_two_newlines() { assert!(true) }
++   | |_
++
++error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
++  --> $DIR/empty_line_after_outer_attribute.rs:31:1
++   |
++LL | / #[crate_type = "lib"]
++LL | |
++LL | | enum Baz {
++   | |_
++
++error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
++  --> $DIR/empty_line_after_outer_attribute.rs:39:1
++   |
++LL | / #[crate_type = "lib"]
++LL | |
++LL | | struct Foo {
++   | |_
++
++error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
++  --> $DIR/empty_line_after_outer_attribute.rs:47:1
++   |
++LL | / #[crate_type = "lib"]
++LL | |
++LL | | mod foo {
++   | |_
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8fd7697eb3b29365d5cccf4efeeaea6d1b66dbe4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++// aux-build:macro_rules.rs
++
++#![warn(clippy::empty_loop)]
++
++#[macro_use]
++extern crate macro_rules;
++
++fn should_trigger() {
++    loop {}
++    loop {
++        loop {}
++    }
++
++    'outer: loop {
++        'inner: loop {}
++    }
++}
++
++fn should_not_trigger() {
++    loop {
++        panic!("This is fine")
++    }
++    let ten_millis = std::time::Duration::from_millis(10);
++    loop {
++        std::thread::sleep(ten_millis)
++    }
++
++    #[allow(clippy::never_loop)]
++    'outer: loop {
++        'inner: loop {
++            break 'inner;
++        }
++        break 'outer;
++    }
++
++    // Make sure `allow` works for this lint
++    #[allow(clippy::empty_loop)]
++    loop {}
++
++    // We don't lint loops inside macros
++    macro_rules! foo {
++        () => {
++            loop {}
++        };
++    }
++
++    // We don't lint external macros
++    foofoo!()
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e44c58ea770aebab69d41c1d82ac83f02fd85dc8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
++  --> $DIR/empty_loop.rs:9:5
++   |
++LL |     loop {}
++   |     ^^^^^^^
++   |
++   = note: `-D clippy::empty-loop` implied by `-D warnings`
++
++error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
++  --> $DIR/empty_loop.rs:11:9
++   |
++LL |         loop {}
++   |         ^^^^^^^
++
++error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
++  --> $DIR/empty_loop.rs:15:9
++   |
++LL |         'inner: loop {}
++   |         ^^^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dcdaae7e72430494a3224bb9f0312ab07e90f274
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++// run-rustfix
++
++#![allow(unused, clippy::needless_pass_by_value)]
++#![warn(clippy::map_entry)]
++
++use std::collections::{BTreeMap, HashMap};
++use std::hash::Hash;
++
++fn foo() {}
++
++fn insert_if_absent0<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
++    m.entry(k).or_insert(v);
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..55d5b21568d0e03223089aad2e5990a3a104d9eb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++// run-rustfix
++
++#![allow(unused, clippy::needless_pass_by_value)]
++#![warn(clippy::map_entry)]
++
++use std::collections::{BTreeMap, HashMap};
++use std::hash::Hash;
++
++fn foo() {}
++
++fn insert_if_absent0<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
++    if !m.contains_key(&k) {
++        m.insert(k, v);
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..87403200ced5008788562ebe66b8c6c2ea918129
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++error: usage of `contains_key` followed by `insert` on a `HashMap`
++  --> $DIR/entry_fixable.rs:12:5
++   |
++LL | /     if !m.contains_key(&k) {
++LL | |         m.insert(k, v);
++LL | |     }
++   | |_____^ help: consider using: `m.entry(k).or_insert(v);`
++   |
++   = note: `-D clippy::map-entry` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f530fc023cfbf920f46b98cacb2847966234c6c8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,73 @@@
++#![allow(unused, clippy::needless_pass_by_value)]
++#![warn(clippy::map_entry)]
++
++use std::collections::{BTreeMap, HashMap};
++use std::hash::Hash;
++
++fn foo() {}
++
++fn insert_if_absent2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
++    if !m.contains_key(&k) {
++        m.insert(k, v)
++    } else {
++        None
++    };
++}
++
++fn insert_if_present2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
++    if m.contains_key(&k) {
++        None
++    } else {
++        m.insert(k, v)
++    };
++}
++
++fn insert_if_absent3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
++    if !m.contains_key(&k) {
++        foo();
++        m.insert(k, v)
++    } else {
++        None
++    };
++}
++
++fn insert_if_present3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
++    if m.contains_key(&k) {
++        None
++    } else {
++        foo();
++        m.insert(k, v)
++    };
++}
++
++fn insert_in_btreemap<K: Ord, V>(m: &mut BTreeMap<K, V>, k: K, v: V) {
++    if !m.contains_key(&k) {
++        foo();
++        m.insert(k, v)
++    } else {
++        None
++    };
++}
++
++// should not trigger
++fn insert_other_if_absent<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, o: K, v: V) {
++    if !m.contains_key(&k) {
++        m.insert(o, v);
++    }
++}
++
++// should not trigger, because the one uses different HashMap from another one
++fn insert_from_different_map<K: Eq + Hash, V>(m: HashMap<K, V>, n: &mut HashMap<K, V>, k: K, v: V) {
++    if !m.contains_key(&k) {
++        n.insert(k, v);
++    }
++}
++
++// should not trigger, because the one uses different HashMap from another one
++fn insert_from_different_map2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, n: &mut HashMap<K, V>, k: K, v: V) {
++    if !m.contains_key(&k) {
++        n.insert(k, v);
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e58c8d22dc45e2bc6303f3afeb8dd813ded7c42f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,57 @@@
++error: usage of `contains_key` followed by `insert` on a `HashMap`
++  --> $DIR/entry_unfixable.rs:10:5
++   |
++LL | /     if !m.contains_key(&k) {
++LL | |         m.insert(k, v)
++LL | |     } else {
++LL | |         None
++LL | |     };
++   | |_____^ consider using `m.entry(k)`
++   |
++   = note: `-D clippy::map-entry` implied by `-D warnings`
++
++error: usage of `contains_key` followed by `insert` on a `HashMap`
++  --> $DIR/entry_unfixable.rs:18:5
++   |
++LL | /     if m.contains_key(&k) {
++LL | |         None
++LL | |     } else {
++LL | |         m.insert(k, v)
++LL | |     };
++   | |_____^ consider using `m.entry(k)`
++
++error: usage of `contains_key` followed by `insert` on a `HashMap`
++  --> $DIR/entry_unfixable.rs:26:5
++   |
++LL | /     if !m.contains_key(&k) {
++LL | |         foo();
++LL | |         m.insert(k, v)
++LL | |     } else {
++LL | |         None
++LL | |     };
++   | |_____^ consider using `m.entry(k)`
++
++error: usage of `contains_key` followed by `insert` on a `HashMap`
++  --> $DIR/entry_unfixable.rs:35:5
++   |
++LL | /     if m.contains_key(&k) {
++LL | |         None
++LL | |     } else {
++LL | |         foo();
++LL | |         m.insert(k, v)
++LL | |     };
++   | |_____^ consider using `m.entry(k)`
++
++error: usage of `contains_key` followed by `insert` on a `BTreeMap`
++  --> $DIR/entry_unfixable.rs:44:5
++   |
++LL | /     if !m.contains_key(&k) {
++LL | |         foo();
++LL | |         m.insert(k, v)
++LL | |     } else {
++LL | |         None
++LL | |     };
++   | |_____^ consider using `m.entry(k)`
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7d6842f5b54215e75a14fe517752e2ab8fae30d4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++// ignore-x86
++
++#![warn(clippy::enum_clike_unportable_variant)]
++#![allow(unused, non_upper_case_globals)]
++
++#[repr(usize)]
++enum NonPortable {
++    X = 0x1_0000_0000,
++    Y = 0,
++    Z = 0x7FFF_FFFF,
++    A = 0xFFFF_FFFF,
++}
++
++enum NonPortableNoHint {
++    X = 0x1_0000_0000,
++    Y = 0,
++    Z = 0x7FFF_FFFF,
++    A = 0xFFFF_FFFF,
++}
++
++#[repr(isize)]
++enum NonPortableSigned {
++    X = -1,
++    Y = 0x7FFF_FFFF,
++    Z = 0xFFFF_FFFF,
++    A = 0x1_0000_0000,
++    B = i32::MIN as isize,
++    C = (i32::MIN as isize) - 1,
++}
++
++enum NonPortableSignedNoHint {
++    X = -1,
++    Y = 0x7FFF_FFFF,
++    Z = 0xFFFF_FFFF,
++    A = 0x1_0000_0000,
++}
++
++#[repr(usize)]
++enum NonPortable2 {
++    X = <usize as Trait>::Number,
++    Y = 0,
++}
++
++trait Trait {
++    const Number: usize = 0x1_0000_0000;
++}
++
++impl Trait for usize {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..71f3f5e083e0d1f67739aa5055b7b0d78a363c26
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++error: Clike enum variant discriminant is not portable to 32-bit targets
++  --> $DIR/enum_clike_unportable_variant.rs:8:5
++   |
++LL |     X = 0x1_0000_0000,
++   |     ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::enum-clike-unportable-variant` implied by `-D warnings`
++
++error: Clike enum variant discriminant is not portable to 32-bit targets
++  --> $DIR/enum_clike_unportable_variant.rs:15:5
++   |
++LL |     X = 0x1_0000_0000,
++   |     ^^^^^^^^^^^^^^^^^
++
++error: Clike enum variant discriminant is not portable to 32-bit targets
++  --> $DIR/enum_clike_unportable_variant.rs:18:5
++   |
++LL |     A = 0xFFFF_FFFF,
++   |     ^^^^^^^^^^^^^^^
++
++error: Clike enum variant discriminant is not portable to 32-bit targets
++  --> $DIR/enum_clike_unportable_variant.rs:25:5
++   |
++LL |     Z = 0xFFFF_FFFF,
++   |     ^^^^^^^^^^^^^^^
++
++error: Clike enum variant discriminant is not portable to 32-bit targets
++  --> $DIR/enum_clike_unportable_variant.rs:26:5
++   |
++LL |     A = 0x1_0000_0000,
++   |     ^^^^^^^^^^^^^^^^^
++
++error: Clike enum variant discriminant is not portable to 32-bit targets
++  --> $DIR/enum_clike_unportable_variant.rs:28:5
++   |
++LL |     C = (i32::MIN as isize) - 1,
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: Clike enum variant discriminant is not portable to 32-bit targets
++  --> $DIR/enum_clike_unportable_variant.rs:34:5
++   |
++LL |     Z = 0xFFFF_FFFF,
++   |     ^^^^^^^^^^^^^^^
++
++error: Clike enum variant discriminant is not portable to 32-bit targets
++  --> $DIR/enum_clike_unportable_variant.rs:35:5
++   |
++LL |     A = 0x1_0000_0000,
++   |     ^^^^^^^^^^^^^^^^^
++
++error: Clike enum variant discriminant is not portable to 32-bit targets
++  --> $DIR/enum_clike_unportable_variant.rs:40:5
++   |
++LL |     X = <usize as Trait>::Number,
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a98216758bb9e9cac7533eb95df0b4ea2faa33d9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++// run-rustfix
++
++#![warn(clippy::enum_glob_use)]
++#![allow(unused)]
++#![warn(unused_imports)]
++
++use std::cmp::Ordering::Less;
++
++enum Enum {
++    Foo,
++}
++
++use self::Enum::Foo;
++
++mod in_fn_test {
++    fn blarg() {
++        use crate::Enum::Foo;
++
++        let _ = Foo;
++    }
++}
++
++mod blurg {
++    pub use std::cmp::Ordering::*; // ok, re-export
++}
++
++fn main() {
++    let _ = Foo;
++    let _ = Less;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d929c9731d341993c9f096f195b7bd661f33dde
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++// run-rustfix
++
++#![warn(clippy::enum_glob_use)]
++#![allow(unused)]
++#![warn(unused_imports)]
++
++use std::cmp::Ordering::*;
++
++enum Enum {
++    Foo,
++}
++
++use self::Enum::*;
++
++mod in_fn_test {
++    fn blarg() {
++        use crate::Enum::*;
++
++        let _ = Foo;
++    }
++}
++
++mod blurg {
++    pub use std::cmp::Ordering::*; // ok, re-export
++}
++
++fn main() {
++    let _ = Foo;
++    let _ = Less;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..69531aed39bd46dcb86ced0ea5661b25bbc11bc1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: usage of wildcard import for enum variants
++  --> $DIR/enum_glob_use.rs:7:5
++   |
++LL | use std::cmp::Ordering::*;
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::cmp::Ordering::Less`
++   |
++   = note: `-D clippy::enum-glob-use` implied by `-D warnings`
++
++error: usage of wildcard import for enum variants
++  --> $DIR/enum_glob_use.rs:13:5
++   |
++LL | use self::Enum::*;
++   |     ^^^^^^^^^^^^^ help: try: `self::Enum::Foo`
++
++error: usage of wildcard import for enum variants
++  --> $DIR/enum_glob_use.rs:17:13
++   |
++LL |         use crate::Enum::*;
++   |             ^^^^^^^^^^^^^^ help: try: `crate::Enum::Foo`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..01774a2a9845cf26c0a6c70c131e8fed7e71bb1d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,136 @@@
++#![feature(non_ascii_idents)]
++#![warn(clippy::enum_variant_names, clippy::pub_enum_variant_names)]
++#![allow(non_camel_case_types)]
++
++enum FakeCallType {
++    CALL,
++    CREATE,
++}
++
++enum FakeCallType2 {
++    CALL,
++    CREATELL,
++}
++
++enum Foo {
++    cFoo,
++    cBar,
++    cBaz,
++}
++
++enum Fooo {
++    cFoo, // no error, threshold is 3 variants by default
++    cBar,
++}
++
++enum Food {
++    FoodGood,
++    FoodMiddle,
++    FoodBad,
++}
++
++enum Stuff {
++    StuffBad, // no error
++}
++
++enum BadCallType {
++    CallTypeCall,
++    CallTypeCreate,
++    CallTypeDestroy,
++}
++
++enum TwoCallType {
++    // no error
++    CallTypeCall,
++    CallTypeCreate,
++}
++
++enum Consts {
++    ConstantInt,
++    ConstantCake,
++    ConstantLie,
++}
++
++enum Two {
++    // no error here
++    ConstantInt,
++    ConstantInfer,
++}
++
++enum Something {
++    CCall,
++    CCreate,
++    CCryogenize,
++}
++
++enum Seal {
++    With,
++    Without,
++}
++
++enum Seall {
++    With,
++    WithOut,
++    Withbroken,
++}
++
++enum Sealll {
++    With,
++    WithOut,
++}
++
++enum Seallll {
++    WithOutCake,
++    WithOutTea,
++    WithOut,
++}
++
++enum NonCaps {
++    Prefix的,
++    PrefixTea,
++    PrefixCake,
++}
++
++pub enum PubSeall {
++    WithOutCake,
++    WithOutTea,
++    WithOut,
++}
++
++#[allow(clippy::pub_enum_variant_names)]
++mod allowed {
++    pub enum PubAllowed {
++        SomeThis,
++        SomeThat,
++        SomeOtherWhat,
++    }
++}
++
++// should not lint
++enum Pat {
++    Foo,
++    Bar,
++    Path,
++}
++
++// should not lint
++enum N {
++    Pos,
++    Neg,
++    Float,
++}
++
++// should not lint
++enum Peek {
++    Peek1,
++    Peek2,
++    Peek3,
++}
++
++// should not lint
++pub enum NetworkLayer {
++    Layer2,
++    Layer3,
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2835391de7f5838a7013ff98feb3357826c03df7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,101 @@@
++error: Variant name ends with the enum's name
++  --> $DIR/enum_variants.rs:16:5
++   |
++LL |     cFoo,
++   |     ^^^^
++   |
++   = note: `-D clippy::enum-variant-names` implied by `-D warnings`
++
++error: Variant name starts with the enum's name
++  --> $DIR/enum_variants.rs:27:5
++   |
++LL |     FoodGood,
++   |     ^^^^^^^^
++
++error: Variant name starts with the enum's name
++  --> $DIR/enum_variants.rs:28:5
++   |
++LL |     FoodMiddle,
++   |     ^^^^^^^^^^
++
++error: Variant name starts with the enum's name
++  --> $DIR/enum_variants.rs:29:5
++   |
++LL |     FoodBad,
++   |     ^^^^^^^
++
++error: All variants have the same prefix: `Food`
++  --> $DIR/enum_variants.rs:26:1
++   |
++LL | / enum Food {
++LL | |     FoodGood,
++LL | |     FoodMiddle,
++LL | |     FoodBad,
++LL | | }
++   | |_^
++   |
++   = help: remove the prefixes and use full paths to the variants instead of glob imports
++
++error: All variants have the same prefix: `CallType`
++  --> $DIR/enum_variants.rs:36:1
++   |
++LL | / enum BadCallType {
++LL | |     CallTypeCall,
++LL | |     CallTypeCreate,
++LL | |     CallTypeDestroy,
++LL | | }
++   | |_^
++   |
++   = help: remove the prefixes and use full paths to the variants instead of glob imports
++
++error: All variants have the same prefix: `Constant`
++  --> $DIR/enum_variants.rs:48:1
++   |
++LL | / enum Consts {
++LL | |     ConstantInt,
++LL | |     ConstantCake,
++LL | |     ConstantLie,
++LL | | }
++   | |_^
++   |
++   = help: remove the prefixes and use full paths to the variants instead of glob imports
++
++error: All variants have the same prefix: `With`
++  --> $DIR/enum_variants.rs:82:1
++   |
++LL | / enum Seallll {
++LL | |     WithOutCake,
++LL | |     WithOutTea,
++LL | |     WithOut,
++LL | | }
++   | |_^
++   |
++   = help: remove the prefixes and use full paths to the variants instead of glob imports
++
++error: All variants have the same prefix: `Prefix`
++  --> $DIR/enum_variants.rs:88:1
++   |
++LL | / enum NonCaps {
++LL | |     Prefix的,
++LL | |     PrefixTea,
++LL | |     PrefixCake,
++LL | | }
++   | |_^
++   |
++   = help: remove the prefixes and use full paths to the variants instead of glob imports
++
++error: All variants have the same prefix: `With`
++  --> $DIR/enum_variants.rs:94:1
++   |
++LL | / pub enum PubSeall {
++LL | |     WithOutCake,
++LL | |     WithOutTea,
++LL | |     WithOut,
++LL | | }
++   | |_^
++   |
++   = note: `-D clippy::pub-enum-variant-names` implied by `-D warnings`
++   = help: remove the prefixes and use full paths to the variants instead of glob imports
++
++error: aborting due to 10 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..272b0900a31c6dee8c27f8eadf2c5b7e00245083
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,87 @@@
++// does not test any rustfixable lints
++
++#[rustfmt::skip]
++#[warn(clippy::eq_op)]
++#[allow(clippy::identity_op, clippy::double_parens, clippy::many_single_char_names)]
++#[allow(clippy::no_effect, unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)]
++#[allow(clippy::nonminimal_bool)]
++#[allow(unused)]
++fn main() {
++    // simple values and comparisons
++    1 == 1;
++    "no" == "no";
++    // even though I agree that no means no ;-)
++    false != false;
++    1.5 < 1.5;
++    1u64 >= 1u64;
++
++    // casts, methods, parentheses
++    (1 as u64) & (1 as u64);
++    1 ^ ((((((1))))));
++
++    // unary and binary operators
++    (-(2) < -(2));
++    ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
++    (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4;
++
++    // various other things
++    ([1] != [1]);
++    ((1, 2) != (1, 2));
++    vec![1, 2, 3] == vec![1, 2, 3]; //no error yet, as we don't match macros
++
++    // const folding
++    1 + 1 == 2;
++    1 - 1 == 0;
++
++    1 - 1;
++    1 / 1;
++    true && true;
++
++    true || true;
++
++
++    let a: u32 = 0;
++    let b: u32 = 0;
++
++    a == b && b == a;
++    a != b && b != a;
++    a < b && b > a;
++    a <= b && b >= a;
++
++    let mut a = vec![1];
++    a == a;
++    2*a.len() == 2*a.len(); // ok, functions
++    a.pop() == a.pop(); // ok, functions
++
++    check_ignore_macro();
++
++    // named constants
++    const A: u32 = 10;
++    const B: u32 = 10;
++    const C: u32 = A / B; // ok, different named constants
++    const D: u32 = A / A;
++}
++
++#[rustfmt::skip]
++macro_rules! check_if_named_foo {
++    ($expression:expr) => (
++        if stringify!($expression) == "foo" {
++            println!("foo!");
++        } else {
++            println!("not foo.");
++        }
++    )
++}
++
++macro_rules! bool_macro {
++    ($expression:expr) => {
++        true
++    };
++}
++
++#[allow(clippy::short_circuit_statement)]
++fn check_ignore_macro() {
++    check_if_named_foo!(foo);
++    // checks if the lint ignores macros with `!` operator
++    !bool_macro!(1) && !bool_macro!("");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5b80e6078eed779a002db07565b8b0b7c1c18fe9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,166 @@@
++error: equal expressions as operands to `==`
++  --> $DIR/eq_op.rs:11:5
++   |
++LL |     1 == 1;
++   |     ^^^^^^
++   |
++   = note: `-D clippy::eq-op` implied by `-D warnings`
++
++error: equal expressions as operands to `==`
++  --> $DIR/eq_op.rs:12:5
++   |
++LL |     "no" == "no";
++   |     ^^^^^^^^^^^^
++
++error: equal expressions as operands to `!=`
++  --> $DIR/eq_op.rs:14:5
++   |
++LL |     false != false;
++   |     ^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `<`
++  --> $DIR/eq_op.rs:15:5
++   |
++LL |     1.5 < 1.5;
++   |     ^^^^^^^^^
++
++error: equal expressions as operands to `>=`
++  --> $DIR/eq_op.rs:16:5
++   |
++LL |     1u64 >= 1u64;
++   |     ^^^^^^^^^^^^
++
++error: equal expressions as operands to `&`
++  --> $DIR/eq_op.rs:19:5
++   |
++LL |     (1 as u64) & (1 as u64);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `^`
++  --> $DIR/eq_op.rs:20:5
++   |
++LL |     1 ^ ((((((1))))));
++   |     ^^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `<`
++  --> $DIR/eq_op.rs:23:5
++   |
++LL |     (-(2) < -(2));
++   |     ^^^^^^^^^^^^^
++
++error: equal expressions as operands to `==`
++  --> $DIR/eq_op.rs:24:5
++   |
++LL |     ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `&`
++  --> $DIR/eq_op.rs:24:6
++   |
++LL |     ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
++   |      ^^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `&`
++  --> $DIR/eq_op.rs:24:27
++   |
++LL |     ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
++   |                           ^^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `==`
++  --> $DIR/eq_op.rs:25:5
++   |
++LL |     (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `!=`
++  --> $DIR/eq_op.rs:28:5
++   |
++LL |     ([1] != [1]);
++   |     ^^^^^^^^^^^^
++
++error: equal expressions as operands to `!=`
++  --> $DIR/eq_op.rs:29:5
++   |
++LL |     ((1, 2) != (1, 2));
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `==`
++  --> $DIR/eq_op.rs:33:5
++   |
++LL |     1 + 1 == 2;
++   |     ^^^^^^^^^^
++
++error: equal expressions as operands to `==`
++  --> $DIR/eq_op.rs:34:5
++   |
++LL |     1 - 1 == 0;
++   |     ^^^^^^^^^^
++
++error: equal expressions as operands to `-`
++  --> $DIR/eq_op.rs:34:5
++   |
++LL |     1 - 1 == 0;
++   |     ^^^^^
++
++error: equal expressions as operands to `-`
++  --> $DIR/eq_op.rs:36:5
++   |
++LL |     1 - 1;
++   |     ^^^^^
++
++error: equal expressions as operands to `/`
++  --> $DIR/eq_op.rs:37:5
++   |
++LL |     1 / 1;
++   |     ^^^^^
++
++error: equal expressions as operands to `&&`
++  --> $DIR/eq_op.rs:38:5
++   |
++LL |     true && true;
++   |     ^^^^^^^^^^^^
++
++error: equal expressions as operands to `||`
++  --> $DIR/eq_op.rs:40:5
++   |
++LL |     true || true;
++   |     ^^^^^^^^^^^^
++
++error: equal expressions as operands to `&&`
++  --> $DIR/eq_op.rs:46:5
++   |
++LL |     a == b && b == a;
++   |     ^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `&&`
++  --> $DIR/eq_op.rs:47:5
++   |
++LL |     a != b && b != a;
++   |     ^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `&&`
++  --> $DIR/eq_op.rs:48:5
++   |
++LL |     a < b && b > a;
++   |     ^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `&&`
++  --> $DIR/eq_op.rs:49:5
++   |
++LL |     a <= b && b >= a;
++   |     ^^^^^^^^^^^^^^^^
++
++error: equal expressions as operands to `==`
++  --> $DIR/eq_op.rs:52:5
++   |
++LL |     a == a;
++   |     ^^^^^^
++
++error: equal expressions as operands to `/`
++  --> $DIR/eq_op.rs:62:20
++   |
++LL |     const D: u32 = A / A;
++   |                    ^^^^^
++
++error: aborting due to 27 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1540062a4bc3ea106dbe64d46462f7016e5caf25
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++#[allow(clippy::no_effect)]
++#[warn(clippy::erasing_op)]
++fn main() {
++    let x: u8 = 0;
++
++    x * 0;
++    0 & x;
++    0 / x;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e54ce85f98ec78b4ddf7730bd064ebfc9a258a4d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: this operation will always return zero. This is likely not the intended outcome
++  --> $DIR/erasing_op.rs:6:5
++   |
++LL |     x * 0;
++   |     ^^^^^
++   |
++   = note: `-D clippy::erasing-op` implied by `-D warnings`
++
++error: this operation will always return zero. This is likely not the intended outcome
++  --> $DIR/erasing_op.rs:7:5
++   |
++LL |     0 & x;
++   |     ^^^^^
++
++error: this operation will always return zero. This is likely not the intended outcome
++  --> $DIR/erasing_op.rs:8:5
++   |
++LL |     0 / x;
++   |     ^^^^^
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c0a52d832c00a2793fa49252ab0bbb88c511e4c0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,176 @@@
++#![feature(box_syntax)]
++#![allow(
++    clippy::borrowed_box,
++    clippy::needless_pass_by_value,
++    clippy::unused_unit,
++    clippy::redundant_clone,
++    clippy::match_single_binding
++)]
++#![warn(clippy::boxed_local)]
++
++#[derive(Clone)]
++struct A;
++
++impl A {
++    fn foo(&self) {}
++}
++
++trait Z {
++    fn bar(&self);
++}
++
++impl Z for A {
++    fn bar(&self) {
++        //nothing
++    }
++}
++
++fn main() {}
++
++fn ok_box_trait(boxed_trait: &Box<dyn Z>) {
++    let boxed_local = boxed_trait;
++    // done
++}
++
++fn warn_call() {
++    let x = box A;
++    x.foo();
++}
++
++fn warn_arg(x: Box<A>) {
++    x.foo();
++}
++
++fn nowarn_closure_arg() {
++    let x = Some(box A);
++    x.map_or((), |x| take_ref(&x));
++}
++
++fn warn_rename_call() {
++    let x = box A;
++
++    let y = x;
++    y.foo(); // via autoderef
++}
++
++fn warn_notuse() {
++    let bz = box A;
++}
++
++fn warn_pass() {
++    let bz = box A;
++    take_ref(&bz); // via deref coercion
++}
++
++fn nowarn_return() -> Box<A> {
++    box A // moved out, "escapes"
++}
++
++fn nowarn_move() {
++    let bx = box A;
++    drop(bx) // moved in, "escapes"
++}
++fn nowarn_call() {
++    let bx = box A;
++    bx.clone(); // method only available to Box, not via autoderef
++}
++
++fn nowarn_pass() {
++    let bx = box A;
++    take_box(&bx); // fn needs &Box
++}
++
++fn take_box(x: &Box<A>) {}
++fn take_ref(x: &A) {}
++
++fn nowarn_ref_take() {
++    // false positive, should actually warn
++    let x = box A;
++    let y = &x;
++    take_box(y);
++}
++
++fn nowarn_match() {
++    let x = box A; // moved into a match
++    match x {
++        y => drop(y),
++    }
++}
++
++fn warn_match() {
++    let x = box A;
++    match &x {
++        // not moved
++        ref y => (),
++    }
++}
++
++fn nowarn_large_array() {
++    // should not warn, is large array
++    // and should not be on stack
++    let x = box [1; 10000];
++    match &x {
++        // not moved
++        ref y => (),
++    }
++}
++
++/// ICE regression test
++pub trait Foo {
++    type Item;
++}
++
++impl<'a> Foo for &'a () {
++    type Item = ();
++}
++
++pub struct PeekableSeekable<I: Foo> {
++    _peeked: I::Item,
++}
++
++pub fn new(_needs_name: Box<PeekableSeekable<&()>>) -> () {}
++
++/// Regression for #916, #1123
++///
++/// This shouldn't warn for `boxed_local`as the implementation of a trait
++/// can't change much about the trait definition.
++trait BoxedAction {
++    fn do_sth(self: Box<Self>);
++}
++
++impl BoxedAction for u64 {
++    fn do_sth(self: Box<Self>) {
++        println!("{}", *self)
++    }
++}
++
++/// Regression for #1478
++///
++/// This shouldn't warn for `boxed_local`as self itself is a box type.
++trait MyTrait {
++    fn do_sth(self);
++}
++
++impl<T> MyTrait for Box<T> {
++    fn do_sth(self) {}
++}
++
++// Issue #3739 - capture in closures
++mod issue_3739 {
++    use super::A;
++
++    fn consume<T>(_: T) {}
++    fn borrow<T>(_: &T) {}
++
++    fn closure_consume(x: Box<A>) {
++        let _ = move || {
++            consume(x);
++        };
++    }
++
++    fn closure_borrow(x: Box<A>) {
++        let _ = || {
++            borrow(&x);
++        };
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c86a769a3da4b170e2dbbc93118ed5a49dd0b216
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: local variable doesn't need to be boxed here
++  --> $DIR/escape_analysis.rs:40:13
++   |
++LL | fn warn_arg(x: Box<A>) {
++   |             ^
++   |
++   = note: `-D clippy::boxed-local` implied by `-D warnings`
++
++error: local variable doesn't need to be boxed here
++  --> $DIR/escape_analysis.rs:131:12
++   |
++LL | pub fn new(_needs_name: Box<PeekableSeekable<&()>>) -> () {}
++   |            ^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1b34c2f74eba1cf41055c0fe3436fea25f79a541
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,204 @@@
++// run-rustfix
++
++#![allow(
++    unused,
++    clippy::no_effect,
++    clippy::redundant_closure_call,
++    clippy::many_single_char_names,
++    clippy::needless_pass_by_value,
++    clippy::option_map_unit_fn
++)]
++#![warn(
++    clippy::redundant_closure,
++    clippy::redundant_closure_for_method_calls,
++    clippy::needless_borrow
++)]
++
++use std::path::PathBuf;
++
++fn main() {
++    let a = Some(1u8).map(foo);
++    meta(foo);
++    let c = Some(1u8).map(|a| {1+2; foo}(a));
++    let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
++    all(&[1, 2, 3], &2, |x, y| below(x, y)); //is adjusted
++    unsafe {
++        Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
++    }
++
++    // See #815
++    let e = Some(1u8).map(|a| divergent(a));
++    let e = Some(1u8).map(generic);
++    let e = Some(1u8).map(generic);
++    // See #515
++    let a: Option<Box<dyn (::std::ops::Deref<Target = [i32]>)>> =
++        Some(vec![1i32, 2]).map(|v| -> Box<dyn (::std::ops::Deref<Target = [i32]>)> { Box::new(v) });
++}
++
++trait TestTrait {
++    fn trait_foo(self) -> bool;
++    fn trait_foo_ref(&self) -> bool;
++}
++
++struct TestStruct<'a> {
++    some_ref: &'a i32,
++}
++
++impl<'a> TestStruct<'a> {
++    fn foo(self) -> bool {
++        false
++    }
++    unsafe fn foo_unsafe(self) -> bool {
++        true
++    }
++}
++
++impl<'a> TestTrait for TestStruct<'a> {
++    fn trait_foo(self) -> bool {
++        false
++    }
++    fn trait_foo_ref(&self) -> bool {
++        false
++    }
++}
++
++impl<'a> std::ops::Deref for TestStruct<'a> {
++    type Target = char;
++    fn deref(&self) -> &char {
++        &'a'
++    }
++}
++
++fn test_redundant_closures_containing_method_calls() {
++    let i = 10;
++    let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
++    let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
++    let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
++    let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
++    let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
++    let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
++    let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
++    unsafe {
++        let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
++    }
++    let e = Some("str").map(std::string::ToString::to_string);
++    let e = Some("str").map(str::to_string);
++    let e = Some('a').map(char::to_uppercase);
++    let e = Some('a').map(char::to_uppercase);
++    let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
++    let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
++    let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
++    let p = Some(PathBuf::new());
++    let e = p.as_ref().and_then(|s| s.to_str());
++    let c = Some(TestStruct { some_ref: &i })
++        .as_ref()
++        .map(|c| c.to_ascii_uppercase());
++
++    fn test_different_borrow_levels<T>(t: &[&T])
++    where
++        T: TestTrait,
++    {
++        t.iter().filter(|x| x.trait_foo_ref());
++        t.iter().map(|x| x.trait_foo_ref());
++    }
++
++    let mut some = Some(|x| x * x);
++    let arr = [Ok(1), Err(2)];
++    let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect();
++}
++
++struct Thunk<T>(Box<dyn FnMut() -> T>);
++
++impl<T> Thunk<T> {
++    fn new<F: 'static + FnOnce() -> T>(f: F) -> Thunk<T> {
++        let mut option = Some(f);
++        // This should not trigger redundant_closure (#1439)
++        Thunk(Box::new(move || option.take().unwrap()()))
++    }
++
++    fn unwrap(self) -> T {
++        let Thunk(mut f) = self;
++        f()
++    }
++}
++
++fn foobar() {
++    let thunk = Thunk::new(|| println!("Hello, world!"));
++    thunk.unwrap()
++}
++
++fn meta<F>(f: F)
++where
++    F: Fn(u8),
++{
++    f(1u8)
++}
++
++fn foo(_: u8) {}
++
++fn foo2(_: u8) -> u8 {
++    1u8
++}
++
++fn all<X, F>(x: &[X], y: &X, f: F) -> bool
++where
++    F: Fn(&X, &X) -> bool,
++{
++    x.iter().all(|e| f(e, y))
++}
++
++fn below(x: &u8, y: &u8) -> bool {
++    x < y
++}
++
++unsafe fn unsafe_fn(_: u8) {}
++
++fn divergent(_: u8) -> ! {
++    unimplemented!()
++}
++
++fn generic<T>(_: T) -> u8 {
++    0
++}
++
++fn passes_fn_mut(mut x: Box<dyn FnMut()>) {
++    requires_fn_once(|| x());
++}
++fn requires_fn_once<T: FnOnce()>(_: T) {}
++
++fn test_redundant_closure_with_function_pointer() {
++    type FnPtrType = fn(u8);
++    let foo_ptr: FnPtrType = foo;
++    let a = Some(1u8).map(foo_ptr);
++}
++
++fn test_redundant_closure_with_another_closure() {
++    let closure = |a| println!("{}", a);
++    let a = Some(1u8).map(closure);
++}
++
++fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 {
++    // Currently f is called when result of make_lazy is called.
++    // If the closure is removed, f will be called when make_lazy itself is
++    // called. This changes semantics, so the closure must stay.
++    Box::new(move |x| f()(x))
++}
++
++fn call<F: FnOnce(&mut String) -> String>(f: F) -> String {
++    f(&mut "Hello".to_owned())
++}
++fn test_difference_in_mutability() {
++    call(|s| s.clone());
++}
++
++struct Bar;
++impl std::ops::Deref for Bar {
++    type Target = str;
++    fn deref(&self) -> &str {
++        "hi"
++    }
++}
++
++fn test_deref_with_trait_method() {
++    let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4f050bd8479ae90e7784ffc33bd44698c17724df
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,204 @@@
++// run-rustfix
++
++#![allow(
++    unused,
++    clippy::no_effect,
++    clippy::redundant_closure_call,
++    clippy::many_single_char_names,
++    clippy::needless_pass_by_value,
++    clippy::option_map_unit_fn
++)]
++#![warn(
++    clippy::redundant_closure,
++    clippy::redundant_closure_for_method_calls,
++    clippy::needless_borrow
++)]
++
++use std::path::PathBuf;
++
++fn main() {
++    let a = Some(1u8).map(|a| foo(a));
++    meta(|a| foo(a));
++    let c = Some(1u8).map(|a| {1+2; foo}(a));
++    let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
++    all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
++    unsafe {
++        Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
++    }
++
++    // See #815
++    let e = Some(1u8).map(|a| divergent(a));
++    let e = Some(1u8).map(|a| generic(a));
++    let e = Some(1u8).map(generic);
++    // See #515
++    let a: Option<Box<dyn (::std::ops::Deref<Target = [i32]>)>> =
++        Some(vec![1i32, 2]).map(|v| -> Box<dyn (::std::ops::Deref<Target = [i32]>)> { Box::new(v) });
++}
++
++trait TestTrait {
++    fn trait_foo(self) -> bool;
++    fn trait_foo_ref(&self) -> bool;
++}
++
++struct TestStruct<'a> {
++    some_ref: &'a i32,
++}
++
++impl<'a> TestStruct<'a> {
++    fn foo(self) -> bool {
++        false
++    }
++    unsafe fn foo_unsafe(self) -> bool {
++        true
++    }
++}
++
++impl<'a> TestTrait for TestStruct<'a> {
++    fn trait_foo(self) -> bool {
++        false
++    }
++    fn trait_foo_ref(&self) -> bool {
++        false
++    }
++}
++
++impl<'a> std::ops::Deref for TestStruct<'a> {
++    type Target = char;
++    fn deref(&self) -> &char {
++        &'a'
++    }
++}
++
++fn test_redundant_closures_containing_method_calls() {
++    let i = 10;
++    let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
++    let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
++    let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
++    let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
++    let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
++    let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
++    let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
++    unsafe {
++        let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
++    }
++    let e = Some("str").map(|s| s.to_string());
++    let e = Some("str").map(str::to_string);
++    let e = Some('a').map(|s| s.to_uppercase());
++    let e = Some('a').map(char::to_uppercase);
++    let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
++    let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
++    let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
++    let p = Some(PathBuf::new());
++    let e = p.as_ref().and_then(|s| s.to_str());
++    let c = Some(TestStruct { some_ref: &i })
++        .as_ref()
++        .map(|c| c.to_ascii_uppercase());
++
++    fn test_different_borrow_levels<T>(t: &[&T])
++    where
++        T: TestTrait,
++    {
++        t.iter().filter(|x| x.trait_foo_ref());
++        t.iter().map(|x| x.trait_foo_ref());
++    }
++
++    let mut some = Some(|x| x * x);
++    let arr = [Ok(1), Err(2)];
++    let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect();
++}
++
++struct Thunk<T>(Box<dyn FnMut() -> T>);
++
++impl<T> Thunk<T> {
++    fn new<F: 'static + FnOnce() -> T>(f: F) -> Thunk<T> {
++        let mut option = Some(f);
++        // This should not trigger redundant_closure (#1439)
++        Thunk(Box::new(move || option.take().unwrap()()))
++    }
++
++    fn unwrap(self) -> T {
++        let Thunk(mut f) = self;
++        f()
++    }
++}
++
++fn foobar() {
++    let thunk = Thunk::new(|| println!("Hello, world!"));
++    thunk.unwrap()
++}
++
++fn meta<F>(f: F)
++where
++    F: Fn(u8),
++{
++    f(1u8)
++}
++
++fn foo(_: u8) {}
++
++fn foo2(_: u8) -> u8 {
++    1u8
++}
++
++fn all<X, F>(x: &[X], y: &X, f: F) -> bool
++where
++    F: Fn(&X, &X) -> bool,
++{
++    x.iter().all(|e| f(e, y))
++}
++
++fn below(x: &u8, y: &u8) -> bool {
++    x < y
++}
++
++unsafe fn unsafe_fn(_: u8) {}
++
++fn divergent(_: u8) -> ! {
++    unimplemented!()
++}
++
++fn generic<T>(_: T) -> u8 {
++    0
++}
++
++fn passes_fn_mut(mut x: Box<dyn FnMut()>) {
++    requires_fn_once(|| x());
++}
++fn requires_fn_once<T: FnOnce()>(_: T) {}
++
++fn test_redundant_closure_with_function_pointer() {
++    type FnPtrType = fn(u8);
++    let foo_ptr: FnPtrType = foo;
++    let a = Some(1u8).map(|a| foo_ptr(a));
++}
++
++fn test_redundant_closure_with_another_closure() {
++    let closure = |a| println!("{}", a);
++    let a = Some(1u8).map(|a| closure(a));
++}
++
++fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 {
++    // Currently f is called when result of make_lazy is called.
++    // If the closure is removed, f will be called when make_lazy itself is
++    // called. This changes semantics, so the closure must stay.
++    Box::new(move |x| f()(x))
++}
++
++fn call<F: FnOnce(&mut String) -> String>(f: F) -> String {
++    f(&mut "Hello".to_owned())
++}
++fn test_difference_in_mutability() {
++    call(|s| s.clone());
++}
++
++struct Bar;
++impl std::ops::Deref for Bar {
++    type Target = str;
++    fn deref(&self) -> &str {
++        "hi"
++    }
++}
++
++fn test_deref_with_trait_method() {
++    let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c4713ca8083dd1f1afb28d0dafe2c7fc793d5da3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,80 @@@
++error: redundant closure found
++  --> $DIR/eta.rs:20:27
++   |
++LL |     let a = Some(1u8).map(|a| foo(a));
++   |                           ^^^^^^^^^^ help: remove closure as shown: `foo`
++   |
++   = note: `-D clippy::redundant-closure` implied by `-D warnings`
++
++error: redundant closure found
++  --> $DIR/eta.rs:21:10
++   |
++LL |     meta(|a| foo(a));
++   |          ^^^^^^^^^^ help: remove closure as shown: `foo`
++
++error: this expression borrows a reference that is immediately dereferenced by the compiler
++  --> $DIR/eta.rs:24:21
++   |
++LL |     all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
++   |                     ^^^ help: change this to: `&2`
++   |
++   = note: `-D clippy::needless-borrow` implied by `-D warnings`
++
++error: redundant closure found
++  --> $DIR/eta.rs:31:27
++   |
++LL |     let e = Some(1u8).map(|a| generic(a));
++   |                           ^^^^^^^^^^^^^^ help: remove closure as shown: `generic`
++
++error: redundant closure found
++  --> $DIR/eta.rs:74:51
++   |
++LL |     let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
++   |                                                   ^^^^^^^^^^^ help: remove closure as shown: `TestStruct::foo`
++   |
++   = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings`
++
++error: redundant closure found
++  --> $DIR/eta.rs:76:51
++   |
++LL |     let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
++   |                                                   ^^^^^^^^^^^^^^^^^ help: remove closure as shown: `TestTrait::trait_foo`
++
++error: redundant closure found
++  --> $DIR/eta.rs:79:42
++   |
++LL |     let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
++   |                                          ^^^^^^^^^^^^^ help: remove closure as shown: `std::vec::Vec::clear`
++
++error: redundant closure found
++  --> $DIR/eta.rs:84:29
++   |
++LL |     let e = Some("str").map(|s| s.to_string());
++   |                             ^^^^^^^^^^^^^^^^^ help: remove closure as shown: `std::string::ToString::to_string`
++
++error: redundant closure found
++  --> $DIR/eta.rs:86:27
++   |
++LL |     let e = Some('a').map(|s| s.to_uppercase());
++   |                           ^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_uppercase`
++
++error: redundant closure found
++  --> $DIR/eta.rs:89:65
++   |
++LL |     let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
++   |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_ascii_uppercase`
++
++error: redundant closure found
++  --> $DIR/eta.rs:172:27
++   |
++LL |     let a = Some(1u8).map(|a| foo_ptr(a));
++   |                           ^^^^^^^^^^^^^^ help: remove closure as shown: `foo_ptr`
++
++error: redundant closure found
++  --> $DIR/eta.rs:177:27
++   |
++LL |     let a = Some(1u8).map(|a| closure(a));
++   |                           ^^^^^^^^^^^^^^ help: remove closure as shown: `closure`
++
++error: aborting due to 12 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d806bc6d40102e6f1efef7166d9f2ad22be0fc15
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,109 @@@
++#[warn(clippy::eval_order_dependence)]
++#[allow(
++    unused_assignments,
++    unused_variables,
++    clippy::many_single_char_names,
++    clippy::no_effect,
++    dead_code,
++    clippy::blacklisted_name
++)]
++fn main() {
++    let mut x = 0;
++    let a = {
++        x = 1;
++        1
++    } + x;
++
++    // Example from iss#277
++    x += {
++        x = 20;
++        2
++    };
++
++    // Does it work in weird places?
++    // ...in the base for a struct expression?
++    struct Foo {
++        a: i32,
++        b: i32,
++    };
++    let base = Foo { a: 4, b: 5 };
++    let foo = Foo {
++        a: x,
++        ..{
++            x = 6;
++            base
++        }
++    };
++    // ...inside a closure?
++    let closure = || {
++        let mut x = 0;
++        x += {
++            x = 20;
++            2
++        };
++    };
++    // ...not across a closure?
++    let mut y = 0;
++    let b = (y, || y = 1);
++
++    // && and || evaluate left-to-right.
++    let a = {
++        x = 1;
++        true
++    } && (x == 3);
++    let a = {
++        x = 1;
++        true
++    } || (x == 3);
++
++    // Make sure we don't get confused by alpha conversion.
++    let a = {
++        let mut x = 1;
++        x = 2;
++        1
++    } + x;
++
++    // No warning if we don't read the variable...
++    x = {
++        x = 20;
++        2
++    };
++    // ...if the assignment is in a closure...
++    let b = {
++        || {
++            x = 1;
++        };
++        1
++    } + x;
++    // ... or the access is under an address.
++    let b = (
++        {
++            let p = &x;
++            1
++        },
++        {
++            x = 1;
++            x
++        },
++    );
++
++    // Limitation: l-values other than simple variables don't trigger
++    // the warning.
++    let mut tup = (0, 0);
++    let c = {
++        tup.0 = 1;
++        1
++    } + tup.0;
++    // Limitation: you can get away with a read under address-of.
++    let mut z = 0;
++    let b = (
++        &{
++            z = x;
++            x
++        },
++        {
++            x = 3;
++            x
++        },
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8f4fa2228f7f4d7f7eff5594a51a23ef28116900
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++error: unsequenced read of a variable
++  --> $DIR/eval_order_dependence.rs:15:9
++   |
++LL |     } + x;
++   |         ^
++   |
++   = note: `-D clippy::eval-order-dependence` implied by `-D warnings`
++note: whether read occurs before this write depends on evaluation order
++  --> $DIR/eval_order_dependence.rs:13:9
++   |
++LL |         x = 1;
++   |         ^^^^^
++
++error: unsequenced read of a variable
++  --> $DIR/eval_order_dependence.rs:18:5
++   |
++LL |     x += {
++   |     ^
++   |
++note: whether read occurs before this write depends on evaluation order
++  --> $DIR/eval_order_dependence.rs:19:9
++   |
++LL |         x = 20;
++   |         ^^^^^^
++
++error: unsequenced read of a variable
++  --> $DIR/eval_order_dependence.rs:31:12
++   |
++LL |         a: x,
++   |            ^
++   |
++note: whether read occurs before this write depends on evaluation order
++  --> $DIR/eval_order_dependence.rs:33:13
++   |
++LL |             x = 6;
++   |             ^^^^^
++
++error: unsequenced read of a variable
++  --> $DIR/eval_order_dependence.rs:40:9
++   |
++LL |         x += {
++   |         ^
++   |
++note: whether read occurs before this write depends on evaluation order
++  --> $DIR/eval_order_dependence.rs:41:13
++   |
++LL |             x = 20;
++   |             ^^^^^^
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bf0325fec7923f9cf40b43b4ad2333e873891d94
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++// run-rustfix
++#![warn(clippy::excessive_precision)]
++#![allow(dead_code, unused_variables, clippy::print_literal)]
++
++fn main() {
++    // Consts
++    const GOOD32: f32 = 0.123_456;
++    const GOOD32_SM: f32 = 0.000_000_000_1;
++    const GOOD32_DOT: f32 = 10_000_000_000.0;
++    const GOOD32_EDGE: f32 = 1.000_000_8;
++    const GOOD64: f64 = 0.123_456_789_012;
++    const GOOD64_SM: f32 = 0.000_000_000_000_000_1;
++    const GOOD64_DOT: f32 = 10_000_000_000_000_000.0;
++
++    const BAD32_1: f32 = 0.123_456_79_f32;
++    const BAD32_2: f32 = 0.123_456_79;
++    const BAD32_3: f32 = 0.1;
++    const BAD32_EDGE: f32 = 1.000_001;
++
++    const BAD64_1: f64 = 0.123_456_789_012_345_66_f64;
++    const BAD64_2: f64 = 0.123_456_789_012_345_66;
++    const BAD64_3: f64 = 0.1;
++
++    // Literal as param
++    println!("{:?}", 8.888_888_888_888_89);
++
++    // // TODO add inferred type tests for f32
++    // Locals
++    let good32: f32 = 0.123_456_f32;
++    let good32_2: f32 = 0.123_456;
++
++    let good64: f64 = 0.123_456_789_012;
++    let good64_suf: f64 = 0.123_456_789_012f64;
++    let good64_inf = 0.123_456_789_012;
++
++    let bad32: f32 = 1.123_456_8;
++    let bad32_suf: f32 = 1.123_456_8_f32;
++    let bad32_inf = 1.123_456_8_f32;
++
++    let bad64: f64 = 0.123_456_789_012_345_66;
++    let bad64_suf: f64 = 0.123_456_789_012_345_66_f64;
++    let bad64_inf = 0.123_456_789_012_345_66;
++
++    // Vectors
++    let good_vec32: Vec<f32> = vec![0.123_456];
++    let good_vec64: Vec<f64> = vec![0.123_456_789];
++
++    let bad_vec32: Vec<f32> = vec![0.123_456_79];
++    let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_78];
++
++    // Exponential float notation
++    let good_e32: f32 = 1e-10;
++    let bad_e32: f32 = 1.123_456_8e-10;
++
++    let good_bige32: f32 = 1E-10;
++    let bad_bige32: f32 = 1.123_456_8E-10;
++
++    // Inferred type
++    let good_inferred: f32 = 1f32 * 1_000_000_000.;
++
++    // issue #2840
++    let num = 0.000_000_000_01e-10f64;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ce4722a90f9002edd571d1d2123861905f5726c3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++// run-rustfix
++#![warn(clippy::excessive_precision)]
++#![allow(dead_code, unused_variables, clippy::print_literal)]
++
++fn main() {
++    // Consts
++    const GOOD32: f32 = 0.123_456;
++    const GOOD32_SM: f32 = 0.000_000_000_1;
++    const GOOD32_DOT: f32 = 10_000_000_000.0;
++    const GOOD32_EDGE: f32 = 1.000_000_8;
++    const GOOD64: f64 = 0.123_456_789_012;
++    const GOOD64_SM: f32 = 0.000_000_000_000_000_1;
++    const GOOD64_DOT: f32 = 10_000_000_000_000_000.0;
++
++    const BAD32_1: f32 = 0.123_456_789_f32;
++    const BAD32_2: f32 = 0.123_456_789;
++    const BAD32_3: f32 = 0.100_000_000_000_1;
++    const BAD32_EDGE: f32 = 1.000_000_9;
++
++    const BAD64_1: f64 = 0.123_456_789_012_345_67f64;
++    const BAD64_2: f64 = 0.123_456_789_012_345_67;
++    const BAD64_3: f64 = 0.100_000_000_000_000_000_1;
++
++    // Literal as param
++    println!("{:?}", 8.888_888_888_888_888_888_888);
++
++    // // TODO add inferred type tests for f32
++    // Locals
++    let good32: f32 = 0.123_456_f32;
++    let good32_2: f32 = 0.123_456;
++
++    let good64: f64 = 0.123_456_789_012;
++    let good64_suf: f64 = 0.123_456_789_012f64;
++    let good64_inf = 0.123_456_789_012;
++
++    let bad32: f32 = 1.123_456_789;
++    let bad32_suf: f32 = 1.123_456_789_f32;
++    let bad32_inf = 1.123_456_789_f32;
++
++    let bad64: f64 = 0.123_456_789_012_345_67;
++    let bad64_suf: f64 = 0.123_456_789_012_345_67f64;
++    let bad64_inf = 0.123_456_789_012_345_67;
++
++    // Vectors
++    let good_vec32: Vec<f32> = vec![0.123_456];
++    let good_vec64: Vec<f64> = vec![0.123_456_789];
++
++    let bad_vec32: Vec<f32> = vec![0.123_456_789];
++    let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789];
++
++    // Exponential float notation
++    let good_e32: f32 = 1e-10;
++    let bad_e32: f32 = 1.123_456_788_888e-10;
++
++    let good_bige32: f32 = 1E-10;
++    let bad_bige32: f32 = 1.123_456_788_888E-10;
++
++    // Inferred type
++    let good_inferred: f32 = 1f32 * 1_000_000_000.;
++
++    // issue #2840
++    let num = 0.000_000_000_01e-10f64;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..599773f2f70c4378984b5c1720d56b36768cb264
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,112 @@@
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:15:26
++   |
++LL |     const BAD32_1: f32 = 0.123_456_789_f32;
++   |                          ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79_f32`
++   |
++   = note: `-D clippy::excessive-precision` implied by `-D warnings`
++
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:16:26
++   |
++LL |     const BAD32_2: f32 = 0.123_456_789;
++   |                          ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79`
++
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:17:26
++   |
++LL |     const BAD32_3: f32 = 0.100_000_000_000_1;
++   |                          ^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1`
++
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:18:29
++   |
++LL |     const BAD32_EDGE: f32 = 1.000_000_9;
++   |                             ^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.000_001`
++
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:20:26
++   |
++LL |     const BAD64_1: f64 = 0.123_456_789_012_345_67f64;
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66_f64`
++
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:21:26
++   |
++LL |     const BAD64_2: f64 = 0.123_456_789_012_345_67;
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
++
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:22:26
++   |
++LL |     const BAD64_3: f64 = 0.100_000_000_000_000_000_1;
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1`
++
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:25:22
++   |
++LL |     println!("{:?}", 8.888_888_888_888_888_888_888);
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `8.888_888_888_888_89`
++
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:36:22
++   |
++LL |     let bad32: f32 = 1.123_456_789;
++   |                      ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8`
++
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:37:26
++   |
++LL |     let bad32_suf: f32 = 1.123_456_789_f32;
++   |                          ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32`
++
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:38:21
++   |
++LL |     let bad32_inf = 1.123_456_789_f32;
++   |                     ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32`
++
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:40:22
++   |
++LL |     let bad64: f64 = 0.123_456_789_012_345_67;
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
++
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:41:26
++   |
++LL |     let bad64_suf: f64 = 0.123_456_789_012_345_67f64;
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66_f64`
++
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:42:21
++   |
++LL |     let bad64_inf = 0.123_456_789_012_345_67;
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
++
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:48:36
++   |
++LL |     let bad_vec32: Vec<f32> = vec![0.123_456_789];
++   |                                    ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79`
++
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:49:36
++   |
++LL |     let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789];
++   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_123_456_78`
++
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:53:24
++   |
++LL |     let bad_e32: f32 = 1.123_456_788_888e-10;
++   |                        ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8e-10`
++
++error: float has excessive precision
++  --> $DIR/excessive_precision.rs:56:27
++   |
++LL |     let bad_bige32: f32 = 1.123_456_788_888E-10;
++   |                           ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8E-10`
++
++error: aborting due to 18 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4eac6eb74672fac716d1aaf8726855ca8febd918
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++#[warn(clippy::exit)]
++
++fn not_main() {
++    if true {
++        std::process::exit(4);
++    }
++}
++
++fn main() {
++    if true {
++        std::process::exit(2);
++    };
++    not_main();
++    std::process::exit(1);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a8d3956aa27a0639abf44db5d8b6639d261fd918
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: usage of `process::exit`
++  --> $DIR/exit1.rs:5:9
++   |
++LL |         std::process::exit(4);
++   |         ^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::exit` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4b693ed7083f000e5346722f2f8239e9fbe78c2b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++#[warn(clippy::exit)]
++
++fn also_not_main() {
++    std::process::exit(3);
++}
++
++fn main() {
++    if true {
++        std::process::exit(2);
++    };
++    also_not_main();
++    std::process::exit(1);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7263e156a9d26cac356a48178c457de47454a474
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: usage of `process::exit`
++  --> $DIR/exit2.rs:4:5
++   |
++LL |     std::process::exit(3);
++   |     ^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::exit` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9dc0e1015a4f75894763e3c76e434fc7331e4f66
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++#[warn(clippy::exit)]
++
++fn main() {
++    if true {
++        std::process::exit(2);
++    };
++    std::process::exit(1);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0bd4252c49aa5e9e77f675f37df8f82aa0ae1251
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++#![warn(clippy::option_expect_used, clippy::result_expect_used)]
++
++fn expect_option() {
++    let opt = Some(0);
++    let _ = opt.expect("");
++}
++
++fn expect_result() {
++    let res: Result<u8, ()> = Ok(0);
++    let _ = res.expect("");
++}
++
++fn main() {
++    expect_option();
++    expect_result();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..adf9f4f192191d652593cdf04dad5eac2de4ca51
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++error: used `expect()` on `an Option` value
++  --> $DIR/expect.rs:5:13
++   |
++LL |     let _ = opt.expect("");
++   |             ^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::option-expect-used` implied by `-D warnings`
++   = help: if this value is an `None`, it will panic
++
++error: used `expect()` on `a Result` value
++  --> $DIR/expect.rs:10:13
++   |
++LL |     let _ = res.expect("");
++   |             ^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::result-expect-used` implied by `-D warnings`
++   = help: if this value is an `Err`, it will panic
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f3d8a941a92bc1171cd7545818ce604bc394b48d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,94 @@@
++// run-rustfix
++
++#![warn(clippy::expect_fun_call)]
++
++/// Checks implementation of the `EXPECT_FUN_CALL` lint
++
++fn main() {
++    struct Foo;
++
++    impl Foo {
++        fn new() -> Self {
++            Foo
++        }
++
++        fn expect(&self, msg: &str) {
++            panic!("{}", msg)
++        }
++    }
++
++    let with_some = Some("value");
++    with_some.expect("error");
++
++    let with_none: Option<i32> = None;
++    with_none.expect("error");
++
++    let error_code = 123_i32;
++    let with_none_and_format: Option<i32> = None;
++    with_none_and_format.unwrap_or_else(|| panic!("Error {}: fake error", error_code));
++
++    let with_none_and_as_str: Option<i32> = None;
++    with_none_and_as_str.unwrap_or_else(|| panic!("Error {}: fake error", error_code));
++
++    let with_ok: Result<(), ()> = Ok(());
++    with_ok.expect("error");
++
++    let with_err: Result<(), ()> = Err(());
++    with_err.expect("error");
++
++    let error_code = 123_i32;
++    let with_err_and_format: Result<(), ()> = Err(());
++    with_err_and_format.unwrap_or_else(|_| panic!("Error {}: fake error", error_code));
++
++    let with_err_and_as_str: Result<(), ()> = Err(());
++    with_err_and_as_str.unwrap_or_else(|_| panic!("Error {}: fake error", error_code));
++
++    let with_dummy_type = Foo::new();
++    with_dummy_type.expect("another test string");
++
++    let with_dummy_type_and_format = Foo::new();
++    with_dummy_type_and_format.expect(&format!("Error {}: fake error", error_code));
++
++    let with_dummy_type_and_as_str = Foo::new();
++    with_dummy_type_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
++
++    //Issue #2937
++    Some("foo").unwrap_or_else(|| panic!("{} {}", 1, 2));
++
++    //Issue #2979 - this should not lint
++    {
++        let msg = "bar";
++        Some("foo").expect(msg);
++    }
++
++    {
++        fn get_string() -> String {
++            "foo".to_string()
++        }
++
++        fn get_static_str() -> &'static str {
++            "foo"
++        }
++
++        fn get_non_static_str(_: &u32) -> &str {
++            "foo"
++        }
++
++        Some("foo").unwrap_or_else(|| { panic!(get_string()) });
++        Some("foo").unwrap_or_else(|| { panic!(get_string()) });
++        Some("foo").unwrap_or_else(|| { panic!(get_string()) });
++
++        Some("foo").unwrap_or_else(|| { panic!(get_static_str()) });
++        Some("foo").unwrap_or_else(|| { panic!(get_non_static_str(&0).to_string()) });
++    }
++
++    //Issue #3839
++    Some(true).unwrap_or_else(|| panic!("key {}, {}", 1, 2));
++
++    //Issue #4912 - the receiver is a &Option
++    {
++        let opt = Some(1);
++        let opt_ref = &opt;
++        opt_ref.unwrap_or_else(|| panic!("{:?}", opt_ref));
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..60bbaa89d42825819edae4191b6d3a3cccaa35fe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,94 @@@
++// run-rustfix
++
++#![warn(clippy::expect_fun_call)]
++
++/// Checks implementation of the `EXPECT_FUN_CALL` lint
++
++fn main() {
++    struct Foo;
++
++    impl Foo {
++        fn new() -> Self {
++            Foo
++        }
++
++        fn expect(&self, msg: &str) {
++            panic!("{}", msg)
++        }
++    }
++
++    let with_some = Some("value");
++    with_some.expect("error");
++
++    let with_none: Option<i32> = None;
++    with_none.expect("error");
++
++    let error_code = 123_i32;
++    let with_none_and_format: Option<i32> = None;
++    with_none_and_format.expect(&format!("Error {}: fake error", error_code));
++
++    let with_none_and_as_str: Option<i32> = None;
++    with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
++
++    let with_ok: Result<(), ()> = Ok(());
++    with_ok.expect("error");
++
++    let with_err: Result<(), ()> = Err(());
++    with_err.expect("error");
++
++    let error_code = 123_i32;
++    let with_err_and_format: Result<(), ()> = Err(());
++    with_err_and_format.expect(&format!("Error {}: fake error", error_code));
++
++    let with_err_and_as_str: Result<(), ()> = Err(());
++    with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
++
++    let with_dummy_type = Foo::new();
++    with_dummy_type.expect("another test string");
++
++    let with_dummy_type_and_format = Foo::new();
++    with_dummy_type_and_format.expect(&format!("Error {}: fake error", error_code));
++
++    let with_dummy_type_and_as_str = Foo::new();
++    with_dummy_type_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
++
++    //Issue #2937
++    Some("foo").expect(format!("{} {}", 1, 2).as_ref());
++
++    //Issue #2979 - this should not lint
++    {
++        let msg = "bar";
++        Some("foo").expect(msg);
++    }
++
++    {
++        fn get_string() -> String {
++            "foo".to_string()
++        }
++
++        fn get_static_str() -> &'static str {
++            "foo"
++        }
++
++        fn get_non_static_str(_: &u32) -> &str {
++            "foo"
++        }
++
++        Some("foo").expect(&get_string());
++        Some("foo").expect(get_string().as_ref());
++        Some("foo").expect(get_string().as_str());
++
++        Some("foo").expect(get_static_str());
++        Some("foo").expect(get_non_static_str(&0));
++    }
++
++    //Issue #3839
++    Some(true).expect(&format!("key {}, {}", 1, 2));
++
++    //Issue #4912 - the receiver is a &Option
++    {
++        let opt = Some(1);
++        let opt_ref = &opt;
++        opt_ref.expect(&format!("{:?}", opt_ref));
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a492e2df89d47809d96edacb664f3310e8ccad37
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,76 @@@
++error: use of `expect` followed by a function call
++  --> $DIR/expect_fun_call.rs:28:26
++   |
++LL |     with_none_and_format.expect(&format!("Error {}: fake error", error_code));
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
++   |
++   = note: `-D clippy::expect-fun-call` implied by `-D warnings`
++
++error: use of `expect` followed by a function call
++  --> $DIR/expect_fun_call.rs:31:26
++   |
++LL |     with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
++
++error: use of `expect` followed by a function call
++  --> $DIR/expect_fun_call.rs:41:25
++   |
++LL |     with_err_and_format.expect(&format!("Error {}: fake error", error_code));
++   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
++
++error: use of `expect` followed by a function call
++  --> $DIR/expect_fun_call.rs:44:25
++   |
++LL |     with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
++   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
++
++error: use of `expect` followed by a function call
++  --> $DIR/expect_fun_call.rs:56:17
++   |
++LL |     Some("foo").expect(format!("{} {}", 1, 2).as_ref());
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{} {}", 1, 2))`
++
++error: use of `expect` followed by a function call
++  --> $DIR/expect_fun_call.rs:77:21
++   |
++LL |         Some("foo").expect(&get_string());
++   |                     ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_string()) })`
++
++error: use of `expect` followed by a function call
++  --> $DIR/expect_fun_call.rs:78:21
++   |
++LL |         Some("foo").expect(get_string().as_ref());
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_string()) })`
++
++error: use of `expect` followed by a function call
++  --> $DIR/expect_fun_call.rs:79:21
++   |
++LL |         Some("foo").expect(get_string().as_str());
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_string()) })`
++
++error: use of `expect` followed by a function call
++  --> $DIR/expect_fun_call.rs:81:21
++   |
++LL |         Some("foo").expect(get_static_str());
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_static_str()) })`
++
++error: use of `expect` followed by a function call
++  --> $DIR/expect_fun_call.rs:82:21
++   |
++LL |         Some("foo").expect(get_non_static_str(&0));
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_non_static_str(&0).to_string()) })`
++
++error: use of `expect` followed by a function call
++  --> $DIR/expect_fun_call.rs:86:16
++   |
++LL |     Some(true).expect(&format!("key {}, {}", 1, 2));
++   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))`
++
++error: use of `expect` followed by a function call
++  --> $DIR/expect_fun_call.rs:92:17
++   |
++LL |         opt_ref.expect(&format!("{:?}", opt_ref));
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{:?}", opt_ref))`
++
++error: aborting due to 12 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aa6ef162fe4012bb10264f1cc481c546e7ebff77
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,147 @@@
++#![warn(clippy::explicit_counter_loop)]
++
++fn main() {
++    let mut vec = vec![1, 2, 3, 4];
++    let mut _index = 0;
++    for _v in &vec {
++        _index += 1
++    }
++
++    let mut _index = 1;
++    _index = 0;
++    for _v in &vec {
++        _index += 1
++    }
++
++    let mut _index = 0;
++    for _v in &mut vec {
++        _index += 1;
++    }
++
++    let mut _index = 0;
++    for _v in vec {
++        _index += 1;
++    }
++}
++
++mod issue_1219 {
++    pub fn test() {
++        // should not trigger the lint because variable is used after the loop #473
++        let vec = vec![1, 2, 3];
++        let mut index = 0;
++        for _v in &vec {
++            index += 1
++        }
++        println!("index: {}", index);
++
++        // should not trigger the lint because the count is conditional #1219
++        let text = "banana";
++        let mut count = 0;
++        for ch in text.chars() {
++            if ch == 'a' {
++                continue;
++            }
++            count += 1;
++            println!("{}", count);
++        }
++
++        // should not trigger the lint because the count is conditional
++        let text = "banana";
++        let mut count = 0;
++        for ch in text.chars() {
++            if ch == 'a' {
++                count += 1;
++            }
++            println!("{}", count);
++        }
++
++        // should trigger the lint because the count is not conditional
++        let text = "banana";
++        let mut count = 0;
++        for ch in text.chars() {
++            count += 1;
++            if ch == 'a' {
++                continue;
++            }
++            println!("{}", count);
++        }
++
++        // should trigger the lint because the count is not conditional
++        let text = "banana";
++        let mut count = 0;
++        for ch in text.chars() {
++            count += 1;
++            for i in 0..2 {
++                let _ = 123;
++            }
++            println!("{}", count);
++        }
++
++        // should not trigger the lint because the count is incremented multiple times
++        let text = "banana";
++        let mut count = 0;
++        for ch in text.chars() {
++            count += 1;
++            for i in 0..2 {
++                count += 1;
++            }
++            println!("{}", count);
++        }
++    }
++}
++
++mod issue_3308 {
++    pub fn test() {
++        // should not trigger the lint because the count is incremented multiple times
++        let mut skips = 0;
++        let erasures = vec![];
++        for i in 0..10 {
++            while erasures.contains(&(i + skips)) {
++                skips += 1;
++            }
++            println!("{}", skips);
++        }
++
++        // should not trigger the lint because the count is incremented multiple times
++        let mut skips = 0;
++        for i in 0..10 {
++            let mut j = 0;
++            while j < 5 {
++                skips += 1;
++                j += 1;
++            }
++            println!("{}", skips);
++        }
++
++        // should not trigger the lint because the count is incremented multiple times
++        let mut skips = 0;
++        for i in 0..10 {
++            for j in 0..5 {
++                skips += 1;
++            }
++            println!("{}", skips);
++        }
++    }
++}
++
++mod issue_1670 {
++    pub fn test() {
++        let mut count = 0;
++        for _i in 3..10 {
++            count += 1;
++        }
++    }
++}
++
++mod issue_4732 {
++    pub fn test() {
++        let slice = &[1, 2, 3];
++        let mut index = 0;
++
++        // should not trigger the lint because the count is used after the loop
++        for _v in slice {
++            index += 1
++        }
++        let _closure = || println!("index: {}", index);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..931af46efe663f31ead53ba32bf7d1ce69efaffb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++error: the variable `_index` is used as a loop counter.
++  --> $DIR/explicit_counter_loop.rs:6:5
++   |
++LL |     for _v in &vec {
++   |     ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()`
++   |
++   = note: `-D clippy::explicit-counter-loop` implied by `-D warnings`
++
++error: the variable `_index` is used as a loop counter.
++  --> $DIR/explicit_counter_loop.rs:12:5
++   |
++LL |     for _v in &vec {
++   |     ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()`
++
++error: the variable `_index` is used as a loop counter.
++  --> $DIR/explicit_counter_loop.rs:17:5
++   |
++LL |     for _v in &mut vec {
++   |     ^^^^^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter_mut().enumerate()`
++
++error: the variable `_index` is used as a loop counter.
++  --> $DIR/explicit_counter_loop.rs:22:5
++   |
++LL |     for _v in vec {
++   |     ^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.into_iter().enumerate()`
++
++error: the variable `count` is used as a loop counter.
++  --> $DIR/explicit_counter_loop.rs:61:9
++   |
++LL |         for ch in text.chars() {
++   |         ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()`
++
++error: the variable `count` is used as a loop counter.
++  --> $DIR/explicit_counter_loop.rs:72:9
++   |
++LL |         for ch in text.chars() {
++   |         ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()`
++
++error: the variable `count` is used as a loop counter.
++  --> $DIR/explicit_counter_loop.rs:130:9
++   |
++LL |         for _i in 3..10 {
++   |         ^^^^^^^^^^^^^^^ help: consider using: `for (count, _i) in (3..10).enumerate()`
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..692d2ca675f91ac6712d46be511934876151e9bd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++// run-rustfix
++#![allow(unused_imports)]
++#![warn(clippy::explicit_write)]
++
++fn stdout() -> String {
++    String::new()
++}
++
++fn stderr() -> String {
++    String::new()
++}
++
++fn main() {
++    // these should warn
++    {
++        use std::io::Write;
++        print!("test");
++        eprint!("test");
++        println!("test");
++        eprintln!("test");
++        print!("test");
++        eprint!("test");
++
++        // including newlines
++        println!("test\ntest");
++        eprintln!("test\ntest");
++    }
++    // these should not warn, different destination
++    {
++        use std::fmt::Write;
++        let mut s = String::new();
++        write!(s, "test").unwrap();
++        write!(s, "test").unwrap();
++        writeln!(s, "test").unwrap();
++        writeln!(s, "test").unwrap();
++        s.write_fmt(format_args!("test")).unwrap();
++        s.write_fmt(format_args!("test")).unwrap();
++        write!(stdout(), "test").unwrap();
++        write!(stderr(), "test").unwrap();
++        writeln!(stdout(), "test").unwrap();
++        writeln!(stderr(), "test").unwrap();
++        stdout().write_fmt(format_args!("test")).unwrap();
++        stderr().write_fmt(format_args!("test")).unwrap();
++    }
++    // these should not warn, no unwrap
++    {
++        use std::io::Write;
++        std::io::stdout().write_fmt(format_args!("test")).expect("no stdout");
++        std::io::stderr().write_fmt(format_args!("test")).expect("no stderr");
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..455c5ef55d05c4209be525017565edc7706f26be
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++// run-rustfix
++#![allow(unused_imports)]
++#![warn(clippy::explicit_write)]
++
++fn stdout() -> String {
++    String::new()
++}
++
++fn stderr() -> String {
++    String::new()
++}
++
++fn main() {
++    // these should warn
++    {
++        use std::io::Write;
++        write!(std::io::stdout(), "test").unwrap();
++        write!(std::io::stderr(), "test").unwrap();
++        writeln!(std::io::stdout(), "test").unwrap();
++        writeln!(std::io::stderr(), "test").unwrap();
++        std::io::stdout().write_fmt(format_args!("test")).unwrap();
++        std::io::stderr().write_fmt(format_args!("test")).unwrap();
++
++        // including newlines
++        writeln!(std::io::stdout(), "test\ntest").unwrap();
++        writeln!(std::io::stderr(), "test\ntest").unwrap();
++    }
++    // these should not warn, different destination
++    {
++        use std::fmt::Write;
++        let mut s = String::new();
++        write!(s, "test").unwrap();
++        write!(s, "test").unwrap();
++        writeln!(s, "test").unwrap();
++        writeln!(s, "test").unwrap();
++        s.write_fmt(format_args!("test")).unwrap();
++        s.write_fmt(format_args!("test")).unwrap();
++        write!(stdout(), "test").unwrap();
++        write!(stderr(), "test").unwrap();
++        writeln!(stdout(), "test").unwrap();
++        writeln!(stderr(), "test").unwrap();
++        stdout().write_fmt(format_args!("test")).unwrap();
++        stderr().write_fmt(format_args!("test")).unwrap();
++    }
++    // these should not warn, no unwrap
++    {
++        use std::io::Write;
++        std::io::stdout().write_fmt(format_args!("test")).expect("no stdout");
++        std::io::stderr().write_fmt(format_args!("test")).expect("no stderr");
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9feef9c0dc8443cb0ef33f4df01e59a9110bea1c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++error: use of `write!(stdout(), ...).unwrap()`
++  --> $DIR/explicit_write.rs:17:9
++   |
++LL |         write!(std::io::stdout(), "test").unwrap();
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")`
++   |
++   = note: `-D clippy::explicit-write` implied by `-D warnings`
++
++error: use of `write!(stderr(), ...).unwrap()`
++  --> $DIR/explicit_write.rs:18:9
++   |
++LL |         write!(std::io::stderr(), "test").unwrap();
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")`
++
++error: use of `writeln!(stdout(), ...).unwrap()`
++  --> $DIR/explicit_write.rs:19:9
++   |
++LL |         writeln!(std::io::stdout(), "test").unwrap();
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test")`
++
++error: use of `writeln!(stderr(), ...).unwrap()`
++  --> $DIR/explicit_write.rs:20:9
++   |
++LL |         writeln!(std::io::stderr(), "test").unwrap();
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test")`
++
++error: use of `stdout().write_fmt(...).unwrap()`
++  --> $DIR/explicit_write.rs:21:9
++   |
++LL |         std::io::stdout().write_fmt(format_args!("test")).unwrap();
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")`
++
++error: use of `stderr().write_fmt(...).unwrap()`
++  --> $DIR/explicit_write.rs:22:9
++   |
++LL |         std::io::stderr().write_fmt(format_args!("test")).unwrap();
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")`
++
++error: use of `writeln!(stdout(), ...).unwrap()`
++  --> $DIR/explicit_write.rs:25:9
++   |
++LL |         writeln!(std::io::stdout(), "test/ntest").unwrap();
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test/ntest")`
++
++error: use of `writeln!(stderr(), ...).unwrap()`
++  --> $DIR/explicit_write.rs:26:9
++   |
++LL |         writeln!(std::io::stderr(), "test/ntest").unwrap();
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test/ntest")`
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f21e8ef935bd02f71fb0cab1c48983afcbe06589
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++#![allow(unused_imports, clippy::blacklisted_name)]
++#![warn(clippy::explicit_write)]
++
++fn main() {
++    use std::io::Write;
++    let bar = "bar";
++    writeln!(std::io::stderr(), "foo {}", bar).unwrap();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..77cadb99bb5510601922ea7034a003a2826669a2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: use of `writeln!(stderr(), ...).unwrap()`. Consider using `eprintln!` instead
++  --> $DIR/explicit_write_non_rustfix.rs:7:5
++   |
++LL |     writeln!(std::io::stderr(), "foo {}", bar).unwrap();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::explicit-write` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ddbf4e98c51ab6921808eb14b64055975b4557e8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,69 @@@
++#![allow(unused, dead_code, clippy::needless_lifetimes, clippy::needless_pass_by_value)]
++#![warn(clippy::extra_unused_lifetimes)]
++
++fn empty() {}
++
++fn used_lt<'a>(x: &'a u8) {}
++
++fn unused_lt<'a>(x: u8) {}
++
++fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) {
++    // 'a is useless here since it's not directly bound
++}
++
++fn lt_return<'a, 'b: 'a>(x: &'b u8) -> &'a u8 {
++    panic!()
++}
++
++fn lt_return_only<'a>() -> &'a u8 {
++    panic!()
++}
++
++fn unused_lt_blergh<'a>(x: Option<Box<dyn Send + 'a>>) {}
++
++trait Foo<'a> {
++    fn x(&self, a: &'a u8);
++}
++
++impl<'a> Foo<'a> for u8 {
++    fn x(&self, a: &'a u8) {}
++}
++
++struct Bar;
++
++impl Bar {
++    fn x<'a>(&self) {}
++}
++
++// test for #489 (used lifetimes in bounds)
++pub fn parse<'a, I: Iterator<Item = &'a str>>(_it: &mut I) {
++    unimplemented!()
++}
++pub fn parse2<'a, I>(_it: &mut I)
++where
++    I: Iterator<Item = &'a str>,
++{
++    unimplemented!()
++}
++
++struct X {
++    x: u32,
++}
++
++impl X {
++    fn self_ref_with_lifetime<'a>(&'a self) {}
++    fn explicit_self_with_lifetime<'a>(self: &'a Self) {}
++}
++
++// Methods implementing traits must have matching lifetimes
++mod issue4291 {
++    trait BadTrait {
++        fn unused_lt<'a>(x: u8) {}
++    }
++
++    impl BadTrait for () {
++        fn unused_lt<'a>(_x: u8) {}
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..16bbb1c037d84c6f3a86c442edfc639eddc534ef
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: this lifetime isn't used in the function definition
++  --> $DIR/extra_unused_lifetimes.rs:8:14
++   |
++LL | fn unused_lt<'a>(x: u8) {}
++   |              ^^
++   |
++   = note: `-D clippy::extra-unused-lifetimes` implied by `-D warnings`
++
++error: this lifetime isn't used in the function definition
++  --> $DIR/extra_unused_lifetimes.rs:10:25
++   |
++LL | fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) {
++   |                         ^^
++
++error: this lifetime isn't used in the function definition
++  --> $DIR/extra_unused_lifetimes.rs:35:10
++   |
++LL |     fn x<'a>(&self) {}
++   |          ^^
++
++error: this lifetime isn't used in the function definition
++  --> $DIR/extra_unused_lifetimes.rs:61:22
++   |
++LL |         fn unused_lt<'a>(x: u8) {}
++   |                      ^^
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..679f4a7dc357d4a182be13c9a292d43ac229ea5c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,76 @@@
++#![deny(clippy::fallible_impl_from)]
++
++// docs example
++struct Foo(i32);
++impl From<String> for Foo {
++    fn from(s: String) -> Self {
++        Foo(s.parse().unwrap())
++    }
++}
++
++struct Valid(Vec<u8>);
++
++impl<'a> From<&'a str> for Valid {
++    fn from(s: &'a str) -> Valid {
++        Valid(s.to_owned().into_bytes())
++    }
++}
++impl From<usize> for Valid {
++    fn from(i: usize) -> Valid {
++        Valid(Vec::with_capacity(i))
++    }
++}
++
++struct Invalid;
++
++impl From<usize> for Invalid {
++    fn from(i: usize) -> Invalid {
++        if i != 42 {
++            panic!();
++        }
++        Invalid
++    }
++}
++
++impl From<Option<String>> for Invalid {
++    fn from(s: Option<String>) -> Invalid {
++        let s = s.unwrap();
++        if !s.is_empty() {
++            panic!(42);
++        } else if s.parse::<u32>().unwrap() != 42 {
++            panic!("{:?}", s);
++        }
++        Invalid
++    }
++}
++
++trait ProjStrTrait {
++    type ProjString;
++}
++impl<T> ProjStrTrait for Box<T> {
++    type ProjString = String;
++}
++impl<'a> From<&'a mut <Box<u32> as ProjStrTrait>::ProjString> for Invalid {
++    fn from(s: &'a mut <Box<u32> as ProjStrTrait>::ProjString) -> Invalid {
++        if s.parse::<u32>().ok().unwrap() != 42 {
++            panic!("{:?}", s);
++        }
++        Invalid
++    }
++}
++
++struct Unreachable;
++
++impl From<String> for Unreachable {
++    fn from(s: String) -> Unreachable {
++        if s.is_empty() {
++            return Unreachable;
++        }
++        match s.chars().next() {
++            Some(_) => Unreachable,
++            None => unreachable!(), // do not lint the unreachable macro
++        }
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ab976b947b356e0f3ea9de6a1408791093e0ced3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,93 @@@
++error: consider implementing `TryFrom` instead
++  --> $DIR/fallible_impl_from.rs:5:1
++   |
++LL | / impl From<String> for Foo {
++LL | |     fn from(s: String) -> Self {
++LL | |         Foo(s.parse().unwrap())
++LL | |     }
++LL | | }
++   | |_^
++   |
++note: the lint level is defined here
++  --> $DIR/fallible_impl_from.rs:1:9
++   |
++LL | #![deny(clippy::fallible_impl_from)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
++   = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail.
++note: potential failure(s)
++  --> $DIR/fallible_impl_from.rs:7:13
++   |
++LL |         Foo(s.parse().unwrap())
++   |             ^^^^^^^^^^^^^^^^^^
++
++error: consider implementing `TryFrom` instead
++  --> $DIR/fallible_impl_from.rs:26:1
++   |
++LL | / impl From<usize> for Invalid {
++LL | |     fn from(i: usize) -> Invalid {
++LL | |         if i != 42 {
++LL | |             panic!();
++...  |
++LL | |     }
++LL | | }
++   | |_^
++   |
++   = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail.
++note: potential failure(s)
++  --> $DIR/fallible_impl_from.rs:29:13
++   |
++LL |             panic!();
++   |             ^^^^^^^^^
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: consider implementing `TryFrom` instead
++  --> $DIR/fallible_impl_from.rs:35:1
++   |
++LL | / impl From<Option<String>> for Invalid {
++LL | |     fn from(s: Option<String>) -> Invalid {
++LL | |         let s = s.unwrap();
++LL | |         if !s.is_empty() {
++...  |
++LL | |     }
++LL | | }
++   | |_^
++   |
++   = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail.
++note: potential failure(s)
++  --> $DIR/fallible_impl_from.rs:37:17
++   |
++LL |         let s = s.unwrap();
++   |                 ^^^^^^^^^^
++LL |         if !s.is_empty() {
++LL |             panic!(42);
++   |             ^^^^^^^^^^^
++LL |         } else if s.parse::<u32>().unwrap() != 42 {
++   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^
++LL |             panic!("{:?}", s);
++   |             ^^^^^^^^^^^^^^^^^^
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: consider implementing `TryFrom` instead
++  --> $DIR/fallible_impl_from.rs:53:1
++   |
++LL | / impl<'a> From<&'a mut <Box<u32> as ProjStrTrait>::ProjString> for Invalid {
++LL | |     fn from(s: &'a mut <Box<u32> as ProjStrTrait>::ProjString) -> Invalid {
++LL | |         if s.parse::<u32>().ok().unwrap() != 42 {
++LL | |             panic!("{:?}", s);
++...  |
++LL | |     }
++LL | | }
++   | |_^
++   |
++   = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail.
++note: potential failure(s)
++  --> $DIR/fallible_impl_from.rs:55:12
++   |
++LL |         if s.parse::<u32>().ok().unwrap() != 42 {
++   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL |             panic!("{:?}", s);
++   |             ^^^^^^^^^^^^^^^^^^
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5de8fe8cdd7b0e128aca9f2502d1906414f6f4bc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++#![warn(clippy::filetype_is_file)]
++
++fn main() -> std::io::Result<()> {
++    use std::fs;
++    use std::ops::BitOr;
++
++    // !filetype.is_dir()
++    if fs::metadata("foo.txt")?.file_type().is_file() {
++        // read file
++    }
++
++    // positive of filetype.is_dir()
++    if !fs::metadata("foo.txt")?.file_type().is_file() {
++        // handle dir
++    }
++
++    // false positive of filetype.is_dir()
++    if !fs::metadata("foo.txt")?.file_type().is_file().bitor(true) {
++        // ...
++    }
++
++    Ok(())
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cd1e3ac37fe8636a0bf4e7f36192557412cf17c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++error: `FileType::is_file()` only covers regular files
++  --> $DIR/filetype_is_file.rs:8:8
++   |
++LL |     if fs::metadata("foo.txt")?.file_type().is_file() {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::filetype-is-file` implied by `-D warnings`
++   = help: use `!FileType::is_dir()` instead
++
++error: `!FileType::is_file()` only denies regular files
++  --> $DIR/filetype_is_file.rs:13:8
++   |
++LL |     if !fs::metadata("foo.txt")?.file_type().is_file() {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: use `FileType::is_dir()` instead
++
++error: `FileType::is_file()` only covers regular files
++  --> $DIR/filetype_is_file.rs:18:9
++   |
++LL |     if !fs::metadata("foo.txt")?.file_type().is_file().bitor(true) {
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: use `!FileType::is_dir()` instead
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f5d051be198ed3011ed7ae74b06c1ccec983d286
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++#![warn(clippy::all, clippy::pedantic)]
++
++fn main() {
++    let a = ["1", "lol", "3", "NaN", "5"];
++
++    let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
++    assert_eq!(element, Some(1));
++
++    #[rustfmt::skip]
++    let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
++        .into_iter()
++        .filter_map(|x| {
++            if x == 2 {
++                Some(x * 2)
++            } else {
++                None
++            }
++        })
++        .next();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d69ae212414f621ea719250b62d10c5ff9fd48cb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead.
++  --> $DIR/filter_map_next.rs:6:32
++   |
++LL |     let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
++   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::filter-map-next` implied by `-D warnings`
++   = note: replace `filter_map(|s| s.parse().ok()).next()` with `find_map(|s| s.parse().ok())`
++
++error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead.
++  --> $DIR/filter_map_next.rs:10:26
++   |
++LL |       let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
++   |  __________________________^
++LL | |         .into_iter()
++LL | |         .filter_map(|x| {
++LL | |             if x == 2 {
++...  |
++LL | |         })
++LL | |         .next();
++   | |_______________^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ef434245fd70c6c6721e5f67d9875f2cd0169cb4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++#![warn(clippy::all, clippy::pedantic)]
++#![allow(clippy::missing_docs_in_private_items)]
++
++fn main() {
++    let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect();
++
++    let _: Vec<_> = vec![5_i8; 6]
++        .into_iter()
++        .filter(|&x| x == 0)
++        .flat_map(|x| x.checked_mul(2))
++        .collect();
++
++    let _: Vec<_> = vec![5_i8; 6]
++        .into_iter()
++        .filter_map(|x| x.checked_mul(2))
++        .flat_map(|x| x.checked_mul(2))
++        .collect();
++
++    let _: Vec<_> = vec![5_i8; 6]
++        .into_iter()
++        .filter_map(|x| x.checked_mul(2))
++        .map(|x| x.checked_mul(2))
++        .collect();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..84a957a374c6b7c1352e8c06f36831ef514de86d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++error: called `filter(p).map(q)` on an `Iterator`
++  --> $DIR/filter_methods.rs:5:21
++   |
++LL |     let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect();
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::filter-map` implied by `-D warnings`
++   = help: this is more succinctly expressed by calling `.filter_map(..)` instead
++
++error: called `filter(p).flat_map(q)` on an `Iterator`
++  --> $DIR/filter_methods.rs:7:21
++   |
++LL |       let _: Vec<_> = vec![5_i8; 6]
++   |  _____________________^
++LL | |         .into_iter()
++LL | |         .filter(|&x| x == 0)
++LL | |         .flat_map(|x| x.checked_mul(2))
++   | |_______________________________________^
++   |
++   = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()`
++
++error: called `filter_map(p).flat_map(q)` on an `Iterator`
++  --> $DIR/filter_methods.rs:13:21
++   |
++LL |       let _: Vec<_> = vec![5_i8; 6]
++   |  _____________________^
++LL | |         .into_iter()
++LL | |         .filter_map(|x| x.checked_mul(2))
++LL | |         .flat_map(|x| x.checked_mul(2))
++   | |_______________________________________^
++   |
++   = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()`
++
++error: called `filter_map(p).map(q)` on an `Iterator`
++  --> $DIR/filter_methods.rs:19:21
++   |
++LL |       let _: Vec<_> = vec![5_i8; 6]
++   |  _____________________^
++LL | |         .into_iter()
++LL | |         .filter_map(|x| x.checked_mul(2))
++LL | |         .map(|x| x.checked_mul(2))
++   | |__________________________________^
++   |
++   = help: this is more succinctly expressed by only calling `.filter_map(..)` instead
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c28cca144ca3f15bb6aa90c0095d7e844d1d7ea9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++#![warn(clippy::all, clippy::pedantic)]
++
++#[derive(Debug, Copy, Clone)]
++enum Flavor {
++    Chocolate,
++}
++
++#[derive(Debug, Copy, Clone)]
++enum Dessert {
++    Banana,
++    Pudding,
++    Cake(Flavor),
++}
++
++fn main() {
++    let desserts_of_the_week = vec![Dessert::Banana, Dessert::Cake(Flavor::Chocolate), Dessert::Pudding];
++
++    let a = ["lol", "NaN", "2", "5", "Xunda"];
++
++    let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s| s.parse().unwrap());
++
++    let _: Option<Flavor> = desserts_of_the_week
++        .iter()
++        .find(|dessert| match *dessert {
++            Dessert::Cake(_) => true,
++            _ => false,
++        })
++        .map(|dessert| match *dessert {
++            Dessert::Cake(ref flavor) => *flavor,
++            _ => unreachable!(),
++        });
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..92f40fe6f1fb23b8a6e2b6681019c573ef5c2e2a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++error: called `find(p).map(q)` on an `Iterator`
++  --> $DIR/find_map.rs:20:26
++   |
++LL |     let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s| s.parse().unwrap());
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::find-map` implied by `-D warnings`
++   = help: this is more succinctly expressed by calling `.find_map(..)` instead
++
++error: called `find(p).map(q)` on an `Iterator`
++  --> $DIR/find_map.rs:22:29
++   |
++LL |       let _: Option<Flavor> = desserts_of_the_week
++   |  _____________________________^
++LL | |         .iter()
++LL | |         .find(|dessert| match *dessert {
++LL | |             Dessert::Cake(_) => true,
++...  |
++LL | |             _ => unreachable!(),
++LL | |         });
++   | |__________^
++   |
++   = help: this is more succinctly expressed by calling `.find_map(..)` instead
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..60fa7569eb9dd5f14fb0292608534a72d1b1bc20
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)]
++#![allow(
++    unused,
++    clippy::shadow_reuse,
++    clippy::shadow_unrelated,
++    clippy::no_effect,
++    clippy::unnecessary_operation,
++    clippy::op_ref
++)]
++
++#[rustfmt::skip]
++fn main() {
++    let mut f = 1.0f32;
++
++    f * 2.0;
++
++    1.0 + f;
++    f * 2.0;
++    f / 2.0;
++    f - 2.0 * 4.2;
++    -f;
++
++    f += 1.0;
++    f -= 1.0;
++    f *= 2.0;
++    f /= 2.0;
++}
++
++// also warn about floating point arith with references involved
++
++pub fn float_arith_ref() {
++    3.1_f32 + &1.2_f32;
++    &3.4_f32 + 1.5_f32;
++    &3.5_f32 + &1.3_f32;
++}
++
++pub fn float_foo(f: &f32) -> f32 {
++    let a = 5.1;
++    a + f
++}
++
++pub fn float_bar(f1: &f32, f2: &f32) -> f32 {
++    f1 + f2
++}
++
++pub fn float_baz(f1: f32, f2: &f32) -> f32 {
++    f1 + f2
++}
++
++pub fn float_qux(f1: f32, f2: f32) -> f32 {
++    (&f1 + &f2)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1ceffb35beede707299d01555ee718c7ad3bb1ea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,106 @@@
++error: floating-point arithmetic detected
++  --> $DIR/float_arithmetic.rs:15:5
++   |
++LL |     f * 2.0;
++   |     ^^^^^^^
++   |
++   = note: `-D clippy::float-arithmetic` implied by `-D warnings`
++
++error: floating-point arithmetic detected
++  --> $DIR/float_arithmetic.rs:17:5
++   |
++LL |     1.0 + f;
++   |     ^^^^^^^
++
++error: floating-point arithmetic detected
++  --> $DIR/float_arithmetic.rs:18:5
++   |
++LL |     f * 2.0;
++   |     ^^^^^^^
++
++error: floating-point arithmetic detected
++  --> $DIR/float_arithmetic.rs:19:5
++   |
++LL |     f / 2.0;
++   |     ^^^^^^^
++
++error: floating-point arithmetic detected
++  --> $DIR/float_arithmetic.rs:20:5
++   |
++LL |     f - 2.0 * 4.2;
++   |     ^^^^^^^^^^^^^
++
++error: floating-point arithmetic detected
++  --> $DIR/float_arithmetic.rs:21:5
++   |
++LL |     -f;
++   |     ^^
++
++error: floating-point arithmetic detected
++  --> $DIR/float_arithmetic.rs:23:5
++   |
++LL |     f += 1.0;
++   |     ^^^^^^^^
++
++error: floating-point arithmetic detected
++  --> $DIR/float_arithmetic.rs:24:5
++   |
++LL |     f -= 1.0;
++   |     ^^^^^^^^
++
++error: floating-point arithmetic detected
++  --> $DIR/float_arithmetic.rs:25:5
++   |
++LL |     f *= 2.0;
++   |     ^^^^^^^^
++
++error: floating-point arithmetic detected
++  --> $DIR/float_arithmetic.rs:26:5
++   |
++LL |     f /= 2.0;
++   |     ^^^^^^^^
++
++error: floating-point arithmetic detected
++  --> $DIR/float_arithmetic.rs:32:5
++   |
++LL |     3.1_f32 + &1.2_f32;
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: floating-point arithmetic detected
++  --> $DIR/float_arithmetic.rs:33:5
++   |
++LL |     &3.4_f32 + 1.5_f32;
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: floating-point arithmetic detected
++  --> $DIR/float_arithmetic.rs:34:5
++   |
++LL |     &3.5_f32 + &1.3_f32;
++   |     ^^^^^^^^^^^^^^^^^^^
++
++error: floating-point arithmetic detected
++  --> $DIR/float_arithmetic.rs:39:5
++   |
++LL |     a + f
++   |     ^^^^^
++
++error: floating-point arithmetic detected
++  --> $DIR/float_arithmetic.rs:43:5
++   |
++LL |     f1 + f2
++   |     ^^^^^^^
++
++error: floating-point arithmetic detected
++  --> $DIR/float_arithmetic.rs:47:5
++   |
++LL |     f1 + f2
++   |     ^^^^^^^
++
++error: floating-point arithmetic detected
++  --> $DIR/float_arithmetic.rs:51:5
++   |
++LL |     (&f1 + &f2)
++   |     ^^^^^^^^^^^
++
++error: aborting due to 17 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9fa0e5f5c079b5e8bf2b8a0e3f2929f87b45d809
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,119 @@@
++#![warn(clippy::float_cmp)]
++#![allow(
++    unused,
++    clippy::no_effect,
++    clippy::unnecessary_operation,
++    clippy::cast_lossless,
++    clippy::many_single_char_names
++)]
++
++use std::ops::Add;
++
++const ZERO: f32 = 0.0;
++const ONE: f32 = ZERO + 1.0;
++
++fn twice<T>(x: T) -> T
++where
++    T: Add<T, Output = T> + Copy,
++{
++    x + x
++}
++
++fn eq_fl(x: f32, y: f32) -> bool {
++    if x.is_nan() {
++        y.is_nan()
++    } else {
++        x == y
++    } // no error, inside "eq" fn
++}
++
++fn fl_eq(x: f32, y: f32) -> bool {
++    if x.is_nan() {
++        y.is_nan()
++    } else {
++        x == y
++    } // no error, inside "eq" fn
++}
++
++struct X {
++    val: f32,
++}
++
++impl PartialEq for X {
++    fn eq(&self, o: &X) -> bool {
++        if self.val.is_nan() {
++            o.val.is_nan()
++        } else {
++            self.val == o.val // no error, inside "eq" fn
++        }
++    }
++}
++
++fn main() {
++    ZERO == 0f32; //no error, comparison with zero is ok
++    1.0f32 != f32::INFINITY; // also comparison with infinity
++    1.0f32 != f32::NEG_INFINITY; // and negative infinity
++    ZERO == 0.0; //no error, comparison with zero is ok
++    ZERO + ZERO != 1.0; //no error, comparison with zero is ok
++
++    ONE == 1f32;
++    ONE == 1.0 + 0.0;
++    ONE + ONE == ZERO + ONE + ONE;
++    ONE != 2.0;
++    ONE != 0.0; // no error, comparison with zero is ok
++    twice(ONE) != ONE;
++    ONE as f64 != 2.0;
++    ONE as f64 != 0.0; // no error, comparison with zero is ok
++
++    let x: f64 = 1.0;
++
++    x == 1.0;
++    x != 0f64; // no error, comparison with zero is ok
++
++    twice(x) != twice(ONE as f64);
++
++    x < 0.0; // no errors, lower or greater comparisons need no fuzzyness
++    x > 0.0;
++    x <= 0.0;
++    x >= 0.0;
++
++    let xs: [f32; 1] = [0.0];
++    let a: *const f32 = xs.as_ptr();
++    let b: *const f32 = xs.as_ptr();
++
++    assert_eq!(a, b); // no errors
++
++    const ZERO_ARRAY: [f32; 2] = [0.0, 0.0];
++    const NON_ZERO_ARRAY: [f32; 2] = [0.0, 0.1];
++
++    let i = 0;
++    let j = 1;
++
++    ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; // ok, because lhs is zero regardless of i
++    NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
++
++    let a1: [f32; 1] = [0.0];
++    let a2: [f32; 1] = [1.1];
++
++    a1 == a2;
++    a1[0] == a2[0];
++
++    // no errors - comparing signums is ok
++    let x32 = 3.21f32;
++    1.23f32.signum() == x32.signum();
++    1.23f32.signum() == -(x32.signum());
++    1.23f32.signum() == 3.21f32.signum();
++
++    1.23f32.signum() != x32.signum();
++    1.23f32.signum() != -(x32.signum());
++    1.23f32.signum() != 3.21f32.signum();
++
++    let x64 = 3.21f64;
++    1.23f64.signum() == x64.signum();
++    1.23f64.signum() == -(x64.signum());
++    1.23f64.signum() == 3.21f64.signum();
++
++    1.23f64.signum() != x64.signum();
++    1.23f64.signum() != -(x64.signum());
++    1.23f64.signum() != 3.21f64.signum();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2d454e8e70de5ab173e7a0d511f1872f896cdac8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++error: strict comparison of `f32` or `f64`
++  --> $DIR/float_cmp.rs:65:5
++   |
++LL |     ONE as f64 != 2.0;
++   |     ^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE as f64 - 2.0).abs() > error`
++   |
++   = note: `-D clippy::float-cmp` implied by `-D warnings`
++   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64`
++  --> $DIR/float_cmp.rs:70:5
++   |
++LL |     x == 1.0;
++   |     ^^^^^^^^ help: consider comparing them within some error: `(x - 1.0).abs() < error`
++   |
++   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64`
++  --> $DIR/float_cmp.rs:73:5
++   |
++LL |     twice(x) != twice(ONE as f64);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(twice(x) - twice(ONE as f64)).abs() > error`
++   |
++   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64`
++  --> $DIR/float_cmp.rs:93:5
++   |
++LL |     NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error`
++   |
++   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64` arrays
++  --> $DIR/float_cmp.rs:98:5
++   |
++LL |     a1 == a2;
++   |     ^^^^^^^^
++   |
++   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64`
++  --> $DIR/float_cmp.rs:99:5
++   |
++LL |     a1[0] == a2[0];
++   |     ^^^^^^^^^^^^^^ help: consider comparing them within some error: `(a1[0] - a2[0]).abs() < error`
++   |
++   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dfc025558a2f430b35b1624b6cd37c5263dc76c4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++// does not test any rustfixable lints
++
++#![warn(clippy::float_cmp_const)]
++#![allow(clippy::float_cmp)]
++#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
++
++const ONE: f32 = 1.0;
++const TWO: f32 = 2.0;
++
++fn eq_one(x: f32) -> bool {
++    if x.is_nan() {
++        false
++    } else {
++        x == ONE
++    } // no error, inside "eq" fn
++}
++
++fn main() {
++    // has errors
++    1f32 == ONE;
++    TWO == ONE;
++    TWO != ONE;
++    ONE + ONE == TWO;
++    let x = 1;
++    x as f32 == ONE;
++
++    let v = 0.9;
++    v == ONE;
++    v != ONE;
++
++    // no errors, lower than or greater than comparisons
++    v < ONE;
++    v > ONE;
++    v <= ONE;
++    v >= ONE;
++
++    // no errors, zero and infinity values
++    ONE != 0f32;
++    TWO == 0f32;
++    ONE != f32::INFINITY;
++    ONE == f32::NEG_INFINITY;
++
++    // no errors, but will warn clippy::float_cmp if '#![allow(float_cmp)]' above is removed
++    let w = 1.1;
++    v == w;
++    v != w;
++    v == 1.0;
++    v != 1.0;
++
++    const ZERO_ARRAY: [f32; 3] = [0.0, 0.0, 0.0];
++    const ZERO_INF_ARRAY: [f32; 3] = [0.0, ::std::f32::INFINITY, ::std::f32::NEG_INFINITY];
++    const NON_ZERO_ARRAY: [f32; 3] = [0.0, 0.1, 0.2];
++    const NON_ZERO_ARRAY2: [f32; 3] = [0.2, 0.1, 0.0];
++
++    // no errors, zero and infinity values
++    NON_ZERO_ARRAY[0] == NON_ZERO_ARRAY2[1]; // lhs is 0.0
++    ZERO_ARRAY == NON_ZERO_ARRAY; // lhs is all zeros
++    ZERO_INF_ARRAY == NON_ZERO_ARRAY; // lhs is all zeros or infinities
++
++    // has errors
++    NON_ZERO_ARRAY == NON_ZERO_ARRAY2;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..19dc4a284b726744b219bd4aa9f2d55e40fa130a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,67 @@@
++error: strict comparison of `f32` or `f64` constant
++  --> $DIR/float_cmp_const.rs:20:5
++   |
++LL |     1f32 == ONE;
++   |     ^^^^^^^^^^^ help: consider comparing them within some error: `(1f32 - ONE).abs() < error`
++   |
++   = note: `-D clippy::float-cmp-const` implied by `-D warnings`
++   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64` constant
++  --> $DIR/float_cmp_const.rs:21:5
++   |
++LL |     TWO == ONE;
++   |     ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() < error`
++   |
++   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64` constant
++  --> $DIR/float_cmp_const.rs:22:5
++   |
++LL |     TWO != ONE;
++   |     ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() > error`
++   |
++   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64` constant
++  --> $DIR/float_cmp_const.rs:23:5
++   |
++LL |     ONE + ONE == TWO;
++   |     ^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE + ONE - TWO).abs() < error`
++   |
++   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64` constant
++  --> $DIR/float_cmp_const.rs:25:5
++   |
++LL |     x as f32 == ONE;
++   |     ^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(x as f32 - ONE).abs() < error`
++   |
++   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64` constant
++  --> $DIR/float_cmp_const.rs:28:5
++   |
++LL |     v == ONE;
++   |     ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() < error`
++   |
++   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64` constant
++  --> $DIR/float_cmp_const.rs:29:5
++   |
++LL |     v != ONE;
++   |     ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() > error`
++   |
++   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: strict comparison of `f32` or `f64` constant arrays
++  --> $DIR/float_cmp_const.rs:61:5
++   |
++LL |     NON_ZERO_ARRAY == NON_ZERO_ARRAY2;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b623e4988e7d464528647b42477c0bc88c1783a9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,98 @@@
++// run-rustfix
++#![warn(clippy::suboptimal_flops)]
++
++struct A {
++    a: f64,
++    b: f64,
++}
++
++fn fake_abs1(num: f64) -> f64 {
++    num.abs()
++}
++
++fn fake_abs2(num: f64) -> f64 {
++    num.abs()
++}
++
++fn fake_abs3(a: A) -> f64 {
++    a.a.abs()
++}
++
++fn fake_abs4(num: f64) -> f64 {
++    num.abs()
++}
++
++fn fake_abs5(a: A) -> f64 {
++    a.a.abs()
++}
++
++fn fake_nabs1(num: f64) -> f64 {
++    -num.abs()
++}
++
++fn fake_nabs2(num: f64) -> f64 {
++    -num.abs()
++}
++
++fn fake_nabs3(a: A) -> A {
++    A {
++        a: -a.a.abs(),
++        b: a.b,
++    }
++}
++
++fn not_fake_abs1(num: f64) -> f64 {
++    if num > 0.0 {
++        num
++    } else {
++        -num - 1f64
++    }
++}
++
++fn not_fake_abs2(num: f64) -> f64 {
++    if num > 0.0 {
++        num + 1.0
++    } else {
++        -(num + 1.0)
++    }
++}
++
++fn not_fake_abs3(num1: f64, num2: f64) -> f64 {
++    if num1 > 0.0 {
++        num2
++    } else {
++        -num2
++    }
++}
++
++fn not_fake_abs4(a: A) -> f64 {
++    if a.a > 0.0 {
++        a.b
++    } else {
++        -a.b
++    }
++}
++
++fn not_fake_abs5(a: A) -> f64 {
++    if a.a > 0.0 {
++        a.a
++    } else {
++        -a.b
++    }
++}
++
++fn main() {
++    fake_abs1(5.0);
++    fake_abs2(5.0);
++    fake_abs3(A { a: 5.0, b: 5.0 });
++    fake_abs4(5.0);
++    fake_abs5(A { a: 5.0, b: 5.0 });
++    fake_nabs1(5.0);
++    fake_nabs2(5.0);
++    fake_nabs3(A { a: 5.0, b: 5.0 });
++    not_fake_abs1(5.0);
++    not_fake_abs2(5.0);
++    not_fake_abs3(5.0, 5.0);
++    not_fake_abs4(A { a: 5.0, b: 5.0 });
++    not_fake_abs5(A { a: 5.0, b: 5.0 });
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cbf9c94e41e6a125aa8f3877c55c93b287c4b1cd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,126 @@@
++// run-rustfix
++#![warn(clippy::suboptimal_flops)]
++
++struct A {
++    a: f64,
++    b: f64,
++}
++
++fn fake_abs1(num: f64) -> f64 {
++    if num >= 0.0 {
++        num
++    } else {
++        -num
++    }
++}
++
++fn fake_abs2(num: f64) -> f64 {
++    if 0.0 < num {
++        num
++    } else {
++        -num
++    }
++}
++
++fn fake_abs3(a: A) -> f64 {
++    if a.a > 0.0 {
++        a.a
++    } else {
++        -a.a
++    }
++}
++
++fn fake_abs4(num: f64) -> f64 {
++    if 0.0 >= num {
++        -num
++    } else {
++        num
++    }
++}
++
++fn fake_abs5(a: A) -> f64 {
++    if a.a < 0.0 {
++        -a.a
++    } else {
++        a.a
++    }
++}
++
++fn fake_nabs1(num: f64) -> f64 {
++    if num < 0.0 {
++        num
++    } else {
++        -num
++    }
++}
++
++fn fake_nabs2(num: f64) -> f64 {
++    if 0.0 >= num {
++        num
++    } else {
++        -num
++    }
++}
++
++fn fake_nabs3(a: A) -> A {
++    A {
++        a: if a.a >= 0.0 { -a.a } else { a.a },
++        b: a.b,
++    }
++}
++
++fn not_fake_abs1(num: f64) -> f64 {
++    if num > 0.0 {
++        num
++    } else {
++        -num - 1f64
++    }
++}
++
++fn not_fake_abs2(num: f64) -> f64 {
++    if num > 0.0 {
++        num + 1.0
++    } else {
++        -(num + 1.0)
++    }
++}
++
++fn not_fake_abs3(num1: f64, num2: f64) -> f64 {
++    if num1 > 0.0 {
++        num2
++    } else {
++        -num2
++    }
++}
++
++fn not_fake_abs4(a: A) -> f64 {
++    if a.a > 0.0 {
++        a.b
++    } else {
++        -a.b
++    }
++}
++
++fn not_fake_abs5(a: A) -> f64 {
++    if a.a > 0.0 {
++        a.a
++    } else {
++        -a.b
++    }
++}
++
++fn main() {
++    fake_abs1(5.0);
++    fake_abs2(5.0);
++    fake_abs3(A { a: 5.0, b: 5.0 });
++    fake_abs4(5.0);
++    fake_abs5(A { a: 5.0, b: 5.0 });
++    fake_nabs1(5.0);
++    fake_nabs2(5.0);
++    fake_nabs3(A { a: 5.0, b: 5.0 });
++    not_fake_abs1(5.0);
++    not_fake_abs2(5.0);
++    not_fake_abs3(5.0, 5.0);
++    not_fake_abs4(A { a: 5.0, b: 5.0 });
++    not_fake_abs5(A { a: 5.0, b: 5.0 });
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..74a71f2ca7c574f8fc5750115aef190bc373e097
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,80 @@@
++error: manual implementation of `abs` method
++  --> $DIR/floating_point_abs.rs:10:5
++   |
++LL | /     if num >= 0.0 {
++LL | |         num
++LL | |     } else {
++LL | |         -num
++LL | |     }
++   | |_____^ help: try: `num.abs()`
++   |
++   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
++
++error: manual implementation of `abs` method
++  --> $DIR/floating_point_abs.rs:18:5
++   |
++LL | /     if 0.0 < num {
++LL | |         num
++LL | |     } else {
++LL | |         -num
++LL | |     }
++   | |_____^ help: try: `num.abs()`
++
++error: manual implementation of `abs` method
++  --> $DIR/floating_point_abs.rs:26:5
++   |
++LL | /     if a.a > 0.0 {
++LL | |         a.a
++LL | |     } else {
++LL | |         -a.a
++LL | |     }
++   | |_____^ help: try: `a.a.abs()`
++
++error: manual implementation of `abs` method
++  --> $DIR/floating_point_abs.rs:34:5
++   |
++LL | /     if 0.0 >= num {
++LL | |         -num
++LL | |     } else {
++LL | |         num
++LL | |     }
++   | |_____^ help: try: `num.abs()`
++
++error: manual implementation of `abs` method
++  --> $DIR/floating_point_abs.rs:42:5
++   |
++LL | /     if a.a < 0.0 {
++LL | |         -a.a
++LL | |     } else {
++LL | |         a.a
++LL | |     }
++   | |_____^ help: try: `a.a.abs()`
++
++error: manual implementation of negation of `abs` method
++  --> $DIR/floating_point_abs.rs:50:5
++   |
++LL | /     if num < 0.0 {
++LL | |         num
++LL | |     } else {
++LL | |         -num
++LL | |     }
++   | |_____^ help: try: `-num.abs()`
++
++error: manual implementation of negation of `abs` method
++  --> $DIR/floating_point_abs.rs:58:5
++   |
++LL | /     if 0.0 >= num {
++LL | |         num
++LL | |     } else {
++LL | |         -num
++LL | |     }
++   | |_____^ help: try: `-num.abs()`
++
++error: manual implementation of negation of `abs` method
++  --> $DIR/floating_point_abs.rs:67:12
++   |
++LL |         a: if a.a >= 0.0 { -a.a } else { a.a },
++   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-a.a.abs()`
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ae7805fdf018eea7228cecc02adcb4781173768e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++// run-rustfix
++#![warn(clippy::imprecise_flops)]
++
++fn main() {
++    let x = 2f32;
++    let _ = x.exp_m1();
++    let _ = x.exp_m1() + 2.0;
++    // Cases where the lint shouldn't be applied
++    let _ = x.exp() - 2.0;
++    let _ = x.exp() - 1.0 * 2.0;
++
++    let x = 2f64;
++    let _ = x.exp_m1();
++    let _ = x.exp_m1() + 2.0;
++    // Cases where the lint shouldn't be applied
++    let _ = x.exp() - 2.0;
++    let _ = x.exp() - 1.0 * 2.0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..27e0b9bcbc937a829e9e1d9ce7d7fb286c0c63d6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++// run-rustfix
++#![warn(clippy::imprecise_flops)]
++
++fn main() {
++    let x = 2f32;
++    let _ = x.exp() - 1.0;
++    let _ = x.exp() - 1.0 + 2.0;
++    // Cases where the lint shouldn't be applied
++    let _ = x.exp() - 2.0;
++    let _ = x.exp() - 1.0 * 2.0;
++
++    let x = 2f64;
++    let _ = x.exp() - 1.0;
++    let _ = x.exp() - 1.0 + 2.0;
++    // Cases where the lint shouldn't be applied
++    let _ = x.exp() - 2.0;
++    let _ = x.exp() - 1.0 * 2.0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5cd999ad47cdd0d768138c901b1a9df01d891d8b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: (e.pow(x) - 1) can be computed more accurately
++  --> $DIR/floating_point_exp.rs:6:13
++   |
++LL |     let _ = x.exp() - 1.0;
++   |             ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
++   |
++   = note: `-D clippy::imprecise-flops` implied by `-D warnings`
++
++error: (e.pow(x) - 1) can be computed more accurately
++  --> $DIR/floating_point_exp.rs:7:13
++   |
++LL |     let _ = x.exp() - 1.0 + 2.0;
++   |             ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
++
++error: (e.pow(x) - 1) can be computed more accurately
++  --> $DIR/floating_point_exp.rs:13:13
++   |
++LL |     let _ = x.exp() - 1.0;
++   |             ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
++
++error: (e.pow(x) - 1) can be computed more accurately
++  --> $DIR/floating_point_exp.rs:14:13
++   |
++LL |     let _ = x.exp() - 1.0 + 2.0;
++   |             ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..42c5e5d2bae2418c4330959aa559382a46f4940f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++// run-rustfix
++#![allow(dead_code, clippy::double_parens)]
++#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
++
++const TWO: f32 = 2.0;
++const E: f32 = std::f32::consts::E;
++
++fn check_log_base() {
++    let x = 1f32;
++    let _ = x.log2();
++    let _ = x.log10();
++    let _ = x.ln();
++    let _ = x.log2();
++    let _ = x.ln();
++
++    let x = 1f64;
++    let _ = x.log2();
++    let _ = x.log10();
++    let _ = x.ln();
++}
++
++fn check_ln1p() {
++    let x = 1f32;
++    let _ = 2.0f32.ln_1p();
++    let _ = 2.0f32.ln_1p();
++    let _ = x.ln_1p();
++    let _ = (x / 2.0).ln_1p();
++    let _ = x.powi(2).ln_1p();
++    let _ = (x.powi(2) / 2.0).ln_1p();
++    let _ = ((std::f32::consts::E - 1.0)).ln_1p();
++    let _ = x.ln_1p();
++    let _ = x.powi(2).ln_1p();
++    let _ = (x + 2.0).ln_1p();
++    let _ = (x / 2.0).ln_1p();
++    // Cases where the lint shouldn't be applied
++    let _ = (1.0 + x + 2.0).ln();
++    let _ = (x + 1.0 + 2.0).ln();
++    let _ = (x + 1.0 / 2.0).ln();
++    let _ = (1.0 + x - 2.0).ln();
++
++    let x = 1f64;
++    let _ = 2.0f64.ln_1p();
++    let _ = 2.0f64.ln_1p();
++    let _ = x.ln_1p();
++    let _ = (x / 2.0).ln_1p();
++    let _ = x.powi(2).ln_1p();
++    let _ = x.ln_1p();
++    let _ = x.powi(2).ln_1p();
++    let _ = (x + 2.0).ln_1p();
++    let _ = (x / 2.0).ln_1p();
++    // Cases where the lint shouldn't be applied
++    let _ = (1.0 + x + 2.0).ln();
++    let _ = (x + 1.0 + 2.0).ln();
++    let _ = (x + 1.0 / 2.0).ln();
++    let _ = (1.0 + x - 2.0).ln();
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8be0d9ad56fc34fa07f120eda343a6ab61257047
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++// run-rustfix
++#![allow(dead_code, clippy::double_parens)]
++#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
++
++const TWO: f32 = 2.0;
++const E: f32 = std::f32::consts::E;
++
++fn check_log_base() {
++    let x = 1f32;
++    let _ = x.log(2f32);
++    let _ = x.log(10f32);
++    let _ = x.log(std::f32::consts::E);
++    let _ = x.log(TWO);
++    let _ = x.log(E);
++
++    let x = 1f64;
++    let _ = x.log(2f64);
++    let _ = x.log(10f64);
++    let _ = x.log(std::f64::consts::E);
++}
++
++fn check_ln1p() {
++    let x = 1f32;
++    let _ = (1f32 + 2.).ln();
++    let _ = (1f32 + 2.0).ln();
++    let _ = (1.0 + x).ln();
++    let _ = (1.0 + x / 2.0).ln();
++    let _ = (1.0 + x.powi(2)).ln();
++    let _ = (1.0 + x.powi(2) / 2.0).ln();
++    let _ = (1.0 + (std::f32::consts::E - 1.0)).ln();
++    let _ = (x + 1.0).ln();
++    let _ = (x.powi(2) + 1.0).ln();
++    let _ = (x + 2.0 + 1.0).ln();
++    let _ = (x / 2.0 + 1.0).ln();
++    // Cases where the lint shouldn't be applied
++    let _ = (1.0 + x + 2.0).ln();
++    let _ = (x + 1.0 + 2.0).ln();
++    let _ = (x + 1.0 / 2.0).ln();
++    let _ = (1.0 + x - 2.0).ln();
++
++    let x = 1f64;
++    let _ = (1f64 + 2.).ln();
++    let _ = (1f64 + 2.0).ln();
++    let _ = (1.0 + x).ln();
++    let _ = (1.0 + x / 2.0).ln();
++    let _ = (1.0 + x.powi(2)).ln();
++    let _ = (x + 1.0).ln();
++    let _ = (x.powi(2) + 1.0).ln();
++    let _ = (x + 2.0 + 1.0).ln();
++    let _ = (x / 2.0 + 1.0).ln();
++    // Cases where the lint shouldn't be applied
++    let _ = (1.0 + x + 2.0).ln();
++    let _ = (x + 1.0 + 2.0).ln();
++    let _ = (x + 1.0 / 2.0).ln();
++    let _ = (1.0 + x - 2.0).ln();
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..943fbdb0b83232964312d49beedce567093ce20b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,174 @@@
++error: logarithm for bases 2, 10 and e can be computed more accurately
++  --> $DIR/floating_point_log.rs:10:13
++   |
++LL |     let _ = x.log(2f32);
++   |             ^^^^^^^^^^^ help: consider using: `x.log2()`
++   |
++   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
++
++error: logarithm for bases 2, 10 and e can be computed more accurately
++  --> $DIR/floating_point_log.rs:11:13
++   |
++LL |     let _ = x.log(10f32);
++   |             ^^^^^^^^^^^^ help: consider using: `x.log10()`
++
++error: logarithm for bases 2, 10 and e can be computed more accurately
++  --> $DIR/floating_point_log.rs:12:13
++   |
++LL |     let _ = x.log(std::f32::consts::E);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()`
++
++error: logarithm for bases 2, 10 and e can be computed more accurately
++  --> $DIR/floating_point_log.rs:13:13
++   |
++LL |     let _ = x.log(TWO);
++   |             ^^^^^^^^^^ help: consider using: `x.log2()`
++
++error: logarithm for bases 2, 10 and e can be computed more accurately
++  --> $DIR/floating_point_log.rs:14:13
++   |
++LL |     let _ = x.log(E);
++   |             ^^^^^^^^ help: consider using: `x.ln()`
++
++error: logarithm for bases 2, 10 and e can be computed more accurately
++  --> $DIR/floating_point_log.rs:17:13
++   |
++LL |     let _ = x.log(2f64);
++   |             ^^^^^^^^^^^ help: consider using: `x.log2()`
++
++error: logarithm for bases 2, 10 and e can be computed more accurately
++  --> $DIR/floating_point_log.rs:18:13
++   |
++LL |     let _ = x.log(10f64);
++   |             ^^^^^^^^^^^^ help: consider using: `x.log10()`
++
++error: logarithm for bases 2, 10 and e can be computed more accurately
++  --> $DIR/floating_point_log.rs:19:13
++   |
++LL |     let _ = x.log(std::f64::consts::E);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:24:13
++   |
++LL |     let _ = (1f32 + 2.).ln();
++   |             ^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()`
++   |
++   = note: `-D clippy::imprecise-flops` implied by `-D warnings`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:25:13
++   |
++LL |     let _ = (1f32 + 2.0).ln();
++   |             ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:26:13
++   |
++LL |     let _ = (1.0 + x).ln();
++   |             ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:27:13
++   |
++LL |     let _ = (1.0 + x / 2.0).ln();
++   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:28:13
++   |
++LL |     let _ = (1.0 + x.powi(2)).ln();
++   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:29:13
++   |
++LL |     let _ = (1.0 + x.powi(2) / 2.0).ln();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(2) / 2.0).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:30:13
++   |
++LL |     let _ = (1.0 + (std::f32::consts::E - 1.0)).ln();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `((std::f32::consts::E - 1.0)).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:31:13
++   |
++LL |     let _ = (x + 1.0).ln();
++   |             ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:32:13
++   |
++LL |     let _ = (x.powi(2) + 1.0).ln();
++   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:33:13
++   |
++LL |     let _ = (x + 2.0 + 1.0).ln();
++   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:34:13
++   |
++LL |     let _ = (x / 2.0 + 1.0).ln();
++   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:42:13
++   |
++LL |     let _ = (1f64 + 2.).ln();
++   |             ^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:43:13
++   |
++LL |     let _ = (1f64 + 2.0).ln();
++   |             ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:44:13
++   |
++LL |     let _ = (1.0 + x).ln();
++   |             ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:45:13
++   |
++LL |     let _ = (1.0 + x / 2.0).ln();
++   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:46:13
++   |
++LL |     let _ = (1.0 + x.powi(2)).ln();
++   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:47:13
++   |
++LL |     let _ = (x + 1.0).ln();
++   |             ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:48:13
++   |
++LL |     let _ = (x.powi(2) + 1.0).ln();
++   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:49:13
++   |
++LL |     let _ = (x + 2.0 + 1.0).ln();
++   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()`
++
++error: ln(1 + x) can be computed more accurately
++  --> $DIR/floating_point_log.rs:50:13
++   |
++LL |     let _ = (x / 2.0 + 1.0).ln();
++   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
++
++error: aborting due to 28 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e343c37740da5f8207c5999238b2f48655914e35
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++// run-rustfix
++#![warn(clippy::suboptimal_flops)]
++
++fn main() {
++    let a: f64 = 1234.567;
++    let b: f64 = 45.67834;
++    let c: f64 = 0.0004;
++    let d: f64 = 0.0001;
++
++    let _ = a.mul_add(b, c);
++    let _ = a.mul_add(b, c);
++    let _ = 2.0f64.mul_add(4.0, a);
++    let _ = 2.0f64.mul_add(4., a);
++
++    let _ = a.mul_add(b, c);
++    let _ = a.mul_add(b, c);
++    let _ = (a * b).mul_add(c, d);
++
++    let _ = a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c)) + c;
++    let _ = 1234.567_f64.mul_add(45.67834_f64, 0.0004_f64);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..810f929c8568b9bc1573e6c0ad976ac55933a785
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++// run-rustfix
++#![warn(clippy::suboptimal_flops)]
++
++fn main() {
++    let a: f64 = 1234.567;
++    let b: f64 = 45.67834;
++    let c: f64 = 0.0004;
++    let d: f64 = 0.0001;
++
++    let _ = a * b + c;
++    let _ = c + a * b;
++    let _ = a + 2.0 * 4.0;
++    let _ = a + 2. * 4.;
++
++    let _ = (a * b) + c;
++    let _ = c + (a * b);
++    let _ = a * b * c + d;
++
++    let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
++    let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2dfbf562d15fc0c6a16bc2960ae02488870fad3f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++error: multiply and add expressions can be calculated more efficiently and accurately
++  --> $DIR/floating_point_mul_add.rs:10:13
++   |
++LL |     let _ = a * b + c;
++   |             ^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
++   |
++   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
++
++error: multiply and add expressions can be calculated more efficiently and accurately
++  --> $DIR/floating_point_mul_add.rs:11:13
++   |
++LL |     let _ = c + a * b;
++   |             ^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
++
++error: multiply and add expressions can be calculated more efficiently and accurately
++  --> $DIR/floating_point_mul_add.rs:12:13
++   |
++LL |     let _ = a + 2.0 * 4.0;
++   |             ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, a)`
++
++error: multiply and add expressions can be calculated more efficiently and accurately
++  --> $DIR/floating_point_mul_add.rs:13:13
++   |
++LL |     let _ = a + 2. * 4.;
++   |             ^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4., a)`
++
++error: multiply and add expressions can be calculated more efficiently and accurately
++  --> $DIR/floating_point_mul_add.rs:15:13
++   |
++LL |     let _ = (a * b) + c;
++   |             ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
++
++error: multiply and add expressions can be calculated more efficiently and accurately
++  --> $DIR/floating_point_mul_add.rs:16:13
++   |
++LL |     let _ = c + (a * b);
++   |             ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
++
++error: multiply and add expressions can be calculated more efficiently and accurately
++  --> $DIR/floating_point_mul_add.rs:17:13
++   |
++LL |     let _ = a * b * c + d;
++   |             ^^^^^^^^^^^^^ help: consider using: `(a * b).mul_add(c, d)`
++
++error: multiply and add expressions can be calculated more efficiently and accurately
++  --> $DIR/floating_point_mul_add.rs:19:13
++   |
++LL |     let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))`
++
++error: multiply and add expressions can be calculated more efficiently and accurately
++  --> $DIR/floating_point_mul_add.rs:20:13
++   |
++LL |     let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)`
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..78a9d44829bb1ab708b1b39ed2fabffe2b3e24e4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++// run-rustfix
++#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
++
++fn main() {
++    let x = 3f32;
++    let _ = x.exp2();
++    let _ = 3.1f32.exp2();
++    let _ = (-3.1f32).exp2();
++    let _ = x.exp();
++    let _ = 3.1f32.exp();
++    let _ = (-3.1f32).exp();
++    let _ = x.sqrt();
++    let _ = x.cbrt();
++    let _ = x.powi(2);
++    let _ = x.powi(-2);
++    let _ = x.powi(16_777_215);
++    let _ = x.powi(-16_777_215);
++    // Cases where the lint shouldn't be applied
++    let _ = x.powf(2.1);
++    let _ = x.powf(-2.1);
++    let _ = x.powf(16_777_216.0);
++    let _ = x.powf(-16_777_216.0);
++
++    let x = 3f64;
++    let _ = x.exp2();
++    let _ = 3.1f64.exp2();
++    let _ = (-3.1f64).exp2();
++    let _ = x.exp();
++    let _ = 3.1f64.exp();
++    let _ = (-3.1f64).exp();
++    let _ = x.sqrt();
++    let _ = x.cbrt();
++    let _ = x.powi(2);
++    let _ = x.powi(-2);
++    let _ = x.powi(-2_147_483_648);
++    let _ = x.powi(2_147_483_647);
++    // Cases where the lint shouldn't be applied
++    let _ = x.powf(2.1);
++    let _ = x.powf(-2.1);
++    let _ = x.powf(-2_147_483_649.0);
++    let _ = x.powf(2_147_483_648.0);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dbc1cac5cb4315cb03ceb516542e15b4aa8e21a9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++// run-rustfix
++#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
++
++fn main() {
++    let x = 3f32;
++    let _ = 2f32.powf(x);
++    let _ = 2f32.powf(3.1);
++    let _ = 2f32.powf(-3.1);
++    let _ = std::f32::consts::E.powf(x);
++    let _ = std::f32::consts::E.powf(3.1);
++    let _ = std::f32::consts::E.powf(-3.1);
++    let _ = x.powf(1.0 / 2.0);
++    let _ = x.powf(1.0 / 3.0);
++    let _ = x.powf(2.0);
++    let _ = x.powf(-2.0);
++    let _ = x.powf(16_777_215.0);
++    let _ = x.powf(-16_777_215.0);
++    // Cases where the lint shouldn't be applied
++    let _ = x.powf(2.1);
++    let _ = x.powf(-2.1);
++    let _ = x.powf(16_777_216.0);
++    let _ = x.powf(-16_777_216.0);
++
++    let x = 3f64;
++    let _ = 2f64.powf(x);
++    let _ = 2f64.powf(3.1);
++    let _ = 2f64.powf(-3.1);
++    let _ = std::f64::consts::E.powf(x);
++    let _ = std::f64::consts::E.powf(3.1);
++    let _ = std::f64::consts::E.powf(-3.1);
++    let _ = x.powf(1.0 / 2.0);
++    let _ = x.powf(1.0 / 3.0);
++    let _ = x.powf(2.0);
++    let _ = x.powf(-2.0);
++    let _ = x.powf(-2_147_483_648.0);
++    let _ = x.powf(2_147_483_647.0);
++    // Cases where the lint shouldn't be applied
++    let _ = x.powf(2.1);
++    let _ = x.powf(-2.1);
++    let _ = x.powf(-2_147_483_649.0);
++    let _ = x.powf(2_147_483_648.0);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ad5163f0079be937ae34e629eaa5a5148c17190a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,150 @@@
++error: exponent for bases 2 and e can be computed more accurately
++  --> $DIR/floating_point_powf.rs:6:13
++   |
++LL |     let _ = 2f32.powf(x);
++   |             ^^^^^^^^^^^^ help: consider using: `x.exp2()`
++   |
++   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
++
++error: exponent for bases 2 and e can be computed more accurately
++  --> $DIR/floating_point_powf.rs:7:13
++   |
++LL |     let _ = 2f32.powf(3.1);
++   |             ^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp2()`
++
++error: exponent for bases 2 and e can be computed more accurately
++  --> $DIR/floating_point_powf.rs:8:13
++   |
++LL |     let _ = 2f32.powf(-3.1);
++   |             ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp2()`
++
++error: exponent for bases 2 and e can be computed more accurately
++  --> $DIR/floating_point_powf.rs:9:13
++   |
++LL |     let _ = std::f32::consts::E.powf(x);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()`
++
++error: exponent for bases 2 and e can be computed more accurately
++  --> $DIR/floating_point_powf.rs:10:13
++   |
++LL |     let _ = std::f32::consts::E.powf(3.1);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp()`
++
++error: exponent for bases 2 and e can be computed more accurately
++  --> $DIR/floating_point_powf.rs:11:13
++   |
++LL |     let _ = std::f32::consts::E.powf(-3.1);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp()`
++
++error: square-root of a number can be computed more efficiently and accurately
++  --> $DIR/floating_point_powf.rs:12:13
++   |
++LL |     let _ = x.powf(1.0 / 2.0);
++   |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()`
++
++error: cube-root of a number can be computed more accurately
++  --> $DIR/floating_point_powf.rs:13:13
++   |
++LL |     let _ = x.powf(1.0 / 3.0);
++   |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()`
++   |
++   = note: `-D clippy::imprecise-flops` implied by `-D warnings`
++
++error: exponentiation with integer powers can be computed more efficiently
++  --> $DIR/floating_point_powf.rs:14:13
++   |
++LL |     let _ = x.powf(2.0);
++   |             ^^^^^^^^^^^ help: consider using: `x.powi(2)`
++
++error: exponentiation with integer powers can be computed more efficiently
++  --> $DIR/floating_point_powf.rs:15:13
++   |
++LL |     let _ = x.powf(-2.0);
++   |             ^^^^^^^^^^^^ help: consider using: `x.powi(-2)`
++
++error: exponentiation with integer powers can be computed more efficiently
++  --> $DIR/floating_point_powf.rs:16:13
++   |
++LL |     let _ = x.powf(16_777_215.0);
++   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(16_777_215)`
++
++error: exponentiation with integer powers can be computed more efficiently
++  --> $DIR/floating_point_powf.rs:17:13
++   |
++LL |     let _ = x.powf(-16_777_215.0);
++   |             ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-16_777_215)`
++
++error: exponent for bases 2 and e can be computed more accurately
++  --> $DIR/floating_point_powf.rs:25:13
++   |
++LL |     let _ = 2f64.powf(x);
++   |             ^^^^^^^^^^^^ help: consider using: `x.exp2()`
++
++error: exponent for bases 2 and e can be computed more accurately
++  --> $DIR/floating_point_powf.rs:26:13
++   |
++LL |     let _ = 2f64.powf(3.1);
++   |             ^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp2()`
++
++error: exponent for bases 2 and e can be computed more accurately
++  --> $DIR/floating_point_powf.rs:27:13
++   |
++LL |     let _ = 2f64.powf(-3.1);
++   |             ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp2()`
++
++error: exponent for bases 2 and e can be computed more accurately
++  --> $DIR/floating_point_powf.rs:28:13
++   |
++LL |     let _ = std::f64::consts::E.powf(x);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()`
++
++error: exponent for bases 2 and e can be computed more accurately
++  --> $DIR/floating_point_powf.rs:29:13
++   |
++LL |     let _ = std::f64::consts::E.powf(3.1);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp()`
++
++error: exponent for bases 2 and e can be computed more accurately
++  --> $DIR/floating_point_powf.rs:30:13
++   |
++LL |     let _ = std::f64::consts::E.powf(-3.1);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp()`
++
++error: square-root of a number can be computed more efficiently and accurately
++  --> $DIR/floating_point_powf.rs:31:13
++   |
++LL |     let _ = x.powf(1.0 / 2.0);
++   |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()`
++
++error: cube-root of a number can be computed more accurately
++  --> $DIR/floating_point_powf.rs:32:13
++   |
++LL |     let _ = x.powf(1.0 / 3.0);
++   |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()`
++
++error: exponentiation with integer powers can be computed more efficiently
++  --> $DIR/floating_point_powf.rs:33:13
++   |
++LL |     let _ = x.powf(2.0);
++   |             ^^^^^^^^^^^ help: consider using: `x.powi(2)`
++
++error: exponentiation with integer powers can be computed more efficiently
++  --> $DIR/floating_point_powf.rs:34:13
++   |
++LL |     let _ = x.powf(-2.0);
++   |             ^^^^^^^^^^^^ help: consider using: `x.powi(-2)`
++
++error: exponentiation with integer powers can be computed more efficiently
++  --> $DIR/floating_point_powf.rs:35:13
++   |
++LL |     let _ = x.powf(-2_147_483_648.0);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-2_147_483_648)`
++
++error: exponentiation with integer powers can be computed more efficiently
++  --> $DIR/floating_point_powf.rs:36:13
++   |
++LL |     let _ = x.powf(2_147_483_647.0);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2_147_483_647)`
++
++error: aborting due to 24 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..362dcb4fd80ca350d3220060fe1139786b68754f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++use std::fmt::Debug;
++use std::ptr;
++use std::rc::Rc;
++use std::sync::Arc;
++
++fn a() {}
++
++#[warn(clippy::fn_address_comparisons)]
++fn main() {
++    type F = fn();
++    let f: F = a;
++    let g: F = f;
++
++    // These should fail:
++    let _ = f == a;
++    let _ = f != a;
++
++    // These should be fine:
++    let _ = f == g;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9c1b5419a4319a7249fe196357db1ddf491cce64
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: comparing with a non-unique address of a function item
++  --> $DIR/fn_address_comparisons.rs:15:13
++   |
++LL |     let _ = f == a;
++   |             ^^^^^^
++   |
++   = note: `-D clippy::fn-address-comparisons` implied by `-D warnings`
++
++error: comparing with a non-unique address of a function item
++  --> $DIR/fn_address_comparisons.rs:16:13
++   |
++LL |     let _ = f != a;
++   |             ^^^^^^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7d6fd607e6545469dc9fa2e0286eae5417d00628
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++#![warn(clippy::fn_params_excessive_bools)]
++
++extern "C" {
++    fn f(_: bool, _: bool, _: bool, _: bool);
++}
++
++macro_rules! foo {
++    () => {
++        fn fff(_: bool, _: bool, _: bool, _: bool) {}
++    };
++}
++
++foo!();
++
++#[no_mangle]
++extern "C" fn k(_: bool, _: bool, _: bool, _: bool) {}
++fn g(_: bool, _: bool, _: bool, _: bool) {}
++fn h(_: bool, _: bool, _: bool) {}
++fn e(_: S, _: S, _: Box<S>, _: Vec<u32>) {}
++fn t(_: S, _: S, _: Box<S>, _: Vec<u32>, _: bool, _: bool, _: bool, _: bool) {}
++
++struct S {}
++trait Trait {
++    fn f(_: bool, _: bool, _: bool, _: bool);
++    fn g(_: bool, _: bool, _: bool, _: Vec<u32>);
++}
++
++impl S {
++    fn f(&self, _: bool, _: bool, _: bool, _: bool) {}
++    fn g(&self, _: bool, _: bool, _: bool) {}
++    #[no_mangle]
++    extern "C" fn h(_: bool, _: bool, _: bool, _: bool) {}
++}
++
++impl Trait for S {
++    fn f(_: bool, _: bool, _: bool, _: bool) {}
++    fn g(_: bool, _: bool, _: bool, _: Vec<u32>) {}
++}
++
++fn main() {
++    fn n(_: bool, _: u32, _: bool, _: Box<u32>, _: bool, _: bool) {
++        fn nn(_: bool, _: bool, _: bool, _: bool) {}
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4e5dbc261d66bababa570af2a3b283536f668e45
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++error: more than 3 bools in function parameters
++  --> $DIR/fn_params_excessive_bools.rs:17:1
++   |
++LL | fn g(_: bool, _: bool, _: bool, _: bool) {}
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::fn-params-excessive-bools` implied by `-D warnings`
++   = help: consider refactoring bools into two-variant enums
++
++error: more than 3 bools in function parameters
++  --> $DIR/fn_params_excessive_bools.rs:20:1
++   |
++LL | fn t(_: S, _: S, _: Box<S>, _: Vec<u32>, _: bool, _: bool, _: bool, _: bool) {}
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider refactoring bools into two-variant enums
++
++error: more than 3 bools in function parameters
++  --> $DIR/fn_params_excessive_bools.rs:24:5
++   |
++LL |     fn f(_: bool, _: bool, _: bool, _: bool);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider refactoring bools into two-variant enums
++
++error: more than 3 bools in function parameters
++  --> $DIR/fn_params_excessive_bools.rs:29:5
++   |
++LL |     fn f(&self, _: bool, _: bool, _: bool, _: bool) {}
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider refactoring bools into two-variant enums
++
++error: more than 3 bools in function parameters
++  --> $DIR/fn_params_excessive_bools.rs:41:5
++   |
++LL | /     fn n(_: bool, _: u32, _: bool, _: Box<u32>, _: bool, _: bool) {
++LL | |         fn nn(_: bool, _: bool, _: bool, _: bool) {}
++LL | |     }
++   | |_____^
++   |
++   = help: consider refactoring bools into two-variant enums
++
++error: more than 3 bools in function parameters
++  --> $DIR/fn_params_excessive_bools.rs:42:9
++   |
++LL |         fn nn(_: bool, _: bool, _: bool, _: bool) {}
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider refactoring bools into two-variant enums
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a456c085c876108b618f77007aeb1d55f230abba
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++// ignore-32bit
++
++#![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)]
++
++fn foo() -> String {
++    String::new()
++}
++
++fn test_function_to_numeric_cast() {
++    let _ = foo as i8;
++    let _ = foo as i16;
++    let _ = foo as i32;
++    let _ = foo as i64;
++    let _ = foo as i128;
++    let _ = foo as isize;
++
++    let _ = foo as u8;
++    let _ = foo as u16;
++    let _ = foo as u32;
++    let _ = foo as u64;
++    let _ = foo as u128;
++
++    // Casting to usize is OK and should not warn
++    let _ = foo as usize;
++
++    // Cast `f` (a `FnDef`) to `fn()` should not warn
++    fn f() {}
++    let _ = f as fn();
++}
++
++fn test_function_var_to_numeric_cast() {
++    let abc: fn() -> String = foo;
++
++    let _ = abc as i8;
++    let _ = abc as i16;
++    let _ = abc as i32;
++    let _ = abc as i64;
++    let _ = abc as i128;
++    let _ = abc as isize;
++
++    let _ = abc as u8;
++    let _ = abc as u16;
++    let _ = abc as u32;
++    let _ = abc as u64;
++    let _ = abc as u128;
++
++    // Casting to usize is OK and should not warn
++    let _ = abc as usize;
++}
++
++fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 {
++    f as i32
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e9549e157cd91deb06f1677ca7f52be6edee7ef3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,144 @@@
++error: casting function pointer `foo` to `i8`, which truncates the value
++  --> $DIR/fn_to_numeric_cast.rs:10:13
++   |
++LL |     let _ = foo as i8;
++   |             ^^^^^^^^^ help: try: `foo as usize`
++   |
++   = note: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings`
++
++error: casting function pointer `foo` to `i16`, which truncates the value
++  --> $DIR/fn_to_numeric_cast.rs:11:13
++   |
++LL |     let _ = foo as i16;
++   |             ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `i32`, which truncates the value
++  --> $DIR/fn_to_numeric_cast.rs:12:13
++   |
++LL |     let _ = foo as i32;
++   |             ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `i64`
++  --> $DIR/fn_to_numeric_cast.rs:13:13
++   |
++LL |     let _ = foo as i64;
++   |             ^^^^^^^^^^ help: try: `foo as usize`
++   |
++   = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings`
++
++error: casting function pointer `foo` to `i128`
++  --> $DIR/fn_to_numeric_cast.rs:14:13
++   |
++LL |     let _ = foo as i128;
++   |             ^^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `isize`
++  --> $DIR/fn_to_numeric_cast.rs:15:13
++   |
++LL |     let _ = foo as isize;
++   |             ^^^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u8`, which truncates the value
++  --> $DIR/fn_to_numeric_cast.rs:17:13
++   |
++LL |     let _ = foo as u8;
++   |             ^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u16`, which truncates the value
++  --> $DIR/fn_to_numeric_cast.rs:18:13
++   |
++LL |     let _ = foo as u16;
++   |             ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u32`, which truncates the value
++  --> $DIR/fn_to_numeric_cast.rs:19:13
++   |
++LL |     let _ = foo as u32;
++   |             ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u64`
++  --> $DIR/fn_to_numeric_cast.rs:20:13
++   |
++LL |     let _ = foo as u64;
++   |             ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u128`
++  --> $DIR/fn_to_numeric_cast.rs:21:13
++   |
++LL |     let _ = foo as u128;
++   |             ^^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `abc` to `i8`, which truncates the value
++  --> $DIR/fn_to_numeric_cast.rs:34:13
++   |
++LL |     let _ = abc as i8;
++   |             ^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `i16`, which truncates the value
++  --> $DIR/fn_to_numeric_cast.rs:35:13
++   |
++LL |     let _ = abc as i16;
++   |             ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `i32`, which truncates the value
++  --> $DIR/fn_to_numeric_cast.rs:36:13
++   |
++LL |     let _ = abc as i32;
++   |             ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `i64`
++  --> $DIR/fn_to_numeric_cast.rs:37:13
++   |
++LL |     let _ = abc as i64;
++   |             ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `i128`
++  --> $DIR/fn_to_numeric_cast.rs:38:13
++   |
++LL |     let _ = abc as i128;
++   |             ^^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `isize`
++  --> $DIR/fn_to_numeric_cast.rs:39:13
++   |
++LL |     let _ = abc as isize;
++   |             ^^^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u8`, which truncates the value
++  --> $DIR/fn_to_numeric_cast.rs:41:13
++   |
++LL |     let _ = abc as u8;
++   |             ^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u16`, which truncates the value
++  --> $DIR/fn_to_numeric_cast.rs:42:13
++   |
++LL |     let _ = abc as u16;
++   |             ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u32`, which truncates the value
++  --> $DIR/fn_to_numeric_cast.rs:43:13
++   |
++LL |     let _ = abc as u32;
++   |             ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u64`
++  --> $DIR/fn_to_numeric_cast.rs:44:13
++   |
++LL |     let _ = abc as u64;
++   |             ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u128`
++  --> $DIR/fn_to_numeric_cast.rs:45:13
++   |
++LL |     let _ = abc as u128;
++   |             ^^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `f` to `i32`, which truncates the value
++  --> $DIR/fn_to_numeric_cast.rs:52:5
++   |
++LL |     f as i32
++   |     ^^^^^^^^ help: try: `f as usize`
++
++error: aborting due to 23 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..04ee985c0863d8a5af686a110e328c3861050cc1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++// ignore-64bit
++
++#![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)]
++
++fn foo() -> String {
++    String::new()
++}
++
++fn test_function_to_numeric_cast() {
++    let _ = foo as i8;
++    let _ = foo as i16;
++    let _ = foo as i32;
++    let _ = foo as i64;
++    let _ = foo as i128;
++    let _ = foo as isize;
++
++    let _ = foo as u8;
++    let _ = foo as u16;
++    let _ = foo as u32;
++    let _ = foo as u64;
++    let _ = foo as u128;
++
++    // Casting to usize is OK and should not warn
++    let _ = foo as usize;
++
++    // Cast `f` (a `FnDef`) to `fn()` should not warn
++    fn f() {}
++    let _ = f as fn();
++}
++
++fn test_function_var_to_numeric_cast() {
++    let abc: fn() -> String = foo;
++
++    let _ = abc as i8;
++    let _ = abc as i16;
++    let _ = abc as i32;
++    let _ = abc as i64;
++    let _ = abc as i128;
++    let _ = abc as isize;
++
++    let _ = abc as u8;
++    let _ = abc as u16;
++    let _ = abc as u32;
++    let _ = abc as u64;
++    let _ = abc as u128;
++
++    // Casting to usize is OK and should not warn
++    let _ = abc as usize;
++}
++
++fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 {
++    f as i32
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..08dd611d675241592cc7245aeab0b7b9b9d8c877
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,144 @@@
++error: casting function pointer `foo` to `i8`, which truncates the value
++  --> $DIR/fn_to_numeric_cast_32bit.rs:10:13
++   |
++LL |     let _ = foo as i8;
++   |             ^^^^^^^^^ help: try: `foo as usize`
++   |
++   = note: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings`
++
++error: casting function pointer `foo` to `i16`, which truncates the value
++  --> $DIR/fn_to_numeric_cast_32bit.rs:11:13
++   |
++LL |     let _ = foo as i16;
++   |             ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `i32`
++  --> $DIR/fn_to_numeric_cast_32bit.rs:12:13
++   |
++LL |     let _ = foo as i32;
++   |             ^^^^^^^^^^ help: try: `foo as usize`
++   |
++   = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings`
++
++error: casting function pointer `foo` to `i64`
++  --> $DIR/fn_to_numeric_cast_32bit.rs:13:13
++   |
++LL |     let _ = foo as i64;
++   |             ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `i128`
++  --> $DIR/fn_to_numeric_cast_32bit.rs:14:13
++   |
++LL |     let _ = foo as i128;
++   |             ^^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `isize`
++  --> $DIR/fn_to_numeric_cast_32bit.rs:15:13
++   |
++LL |     let _ = foo as isize;
++   |             ^^^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u8`, which truncates the value
++  --> $DIR/fn_to_numeric_cast_32bit.rs:17:13
++   |
++LL |     let _ = foo as u8;
++   |             ^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u16`, which truncates the value
++  --> $DIR/fn_to_numeric_cast_32bit.rs:18:13
++   |
++LL |     let _ = foo as u16;
++   |             ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u32`
++  --> $DIR/fn_to_numeric_cast_32bit.rs:19:13
++   |
++LL |     let _ = foo as u32;
++   |             ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u64`
++  --> $DIR/fn_to_numeric_cast_32bit.rs:20:13
++   |
++LL |     let _ = foo as u64;
++   |             ^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `foo` to `u128`
++  --> $DIR/fn_to_numeric_cast_32bit.rs:21:13
++   |
++LL |     let _ = foo as u128;
++   |             ^^^^^^^^^^^ help: try: `foo as usize`
++
++error: casting function pointer `abc` to `i8`, which truncates the value
++  --> $DIR/fn_to_numeric_cast_32bit.rs:34:13
++   |
++LL |     let _ = abc as i8;
++   |             ^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `i16`, which truncates the value
++  --> $DIR/fn_to_numeric_cast_32bit.rs:35:13
++   |
++LL |     let _ = abc as i16;
++   |             ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `i32`
++  --> $DIR/fn_to_numeric_cast_32bit.rs:36:13
++   |
++LL |     let _ = abc as i32;
++   |             ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `i64`
++  --> $DIR/fn_to_numeric_cast_32bit.rs:37:13
++   |
++LL |     let _ = abc as i64;
++   |             ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `i128`
++  --> $DIR/fn_to_numeric_cast_32bit.rs:38:13
++   |
++LL |     let _ = abc as i128;
++   |             ^^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `isize`
++  --> $DIR/fn_to_numeric_cast_32bit.rs:39:13
++   |
++LL |     let _ = abc as isize;
++   |             ^^^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u8`, which truncates the value
++  --> $DIR/fn_to_numeric_cast_32bit.rs:41:13
++   |
++LL |     let _ = abc as u8;
++   |             ^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u16`, which truncates the value
++  --> $DIR/fn_to_numeric_cast_32bit.rs:42:13
++   |
++LL |     let _ = abc as u16;
++   |             ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u32`
++  --> $DIR/fn_to_numeric_cast_32bit.rs:43:13
++   |
++LL |     let _ = abc as u32;
++   |             ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u64`
++  --> $DIR/fn_to_numeric_cast_32bit.rs:44:13
++   |
++LL |     let _ = abc as u64;
++   |             ^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `abc` to `u128`
++  --> $DIR/fn_to_numeric_cast_32bit.rs:45:13
++   |
++LL |     let _ = abc as u128;
++   |             ^^^^^^^^^^^ help: try: `abc as usize`
++
++error: casting function pointer `f` to `i32`
++  --> $DIR/fn_to_numeric_cast_32bit.rs:52:5
++   |
++LL |     f as i32
++   |     ^^^^^^^^ help: try: `f as usize`
++
++error: aborting due to 23 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..39a8d960a7e91fd6af65bd71618f60bfc6f5bcf7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++#![warn(clippy::for_kv_map)]
++#![allow(clippy::used_underscore_binding)]
++
++use std::collections::*;
++use std::rc::Rc;
++
++fn main() {
++    let m: HashMap<u64, u64> = HashMap::new();
++    for (_, v) in &m {
++        let _v = v;
++    }
++
++    let m: Rc<HashMap<u64, u64>> = Rc::new(HashMap::new());
++    for (_, v) in &*m {
++        let _v = v;
++        // Here the `*` is not actually necessary, but the test tests that we don't
++        // suggest
++        // `in *m.values()` as we used to
++    }
++
++    let mut m: HashMap<u64, u64> = HashMap::new();
++    for (_, v) in &mut m {
++        let _v = v;
++    }
++
++    let m: &mut HashMap<u64, u64> = &mut HashMap::new();
++    for (_, v) in &mut *m {
++        let _v = v;
++    }
++
++    let m: HashMap<u64, u64> = HashMap::new();
++    let rm = &m;
++    for (k, _value) in rm {
++        let _k = k;
++    }
++
++    // The following should not produce warnings.
++
++    let m: HashMap<u64, u64> = HashMap::new();
++    // No error, _value is actually used
++    for (k, _value) in &m {
++        let _ = _value;
++        let _k = k;
++    }
++
++    let m: HashMap<u64, String> = Default::default();
++    for (_, v) in m {
++        let _v = v;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..241758c542cfbe82aea3c2c00ad1e567fab11d68
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++error: you seem to want to iterate on a map's values
++  --> $DIR/for_kv_map.rs:9:19
++   |
++LL |     for (_, v) in &m {
++   |                   ^^
++   |
++   = note: `-D clippy::for-kv-map` implied by `-D warnings`
++help: use the corresponding method
++   |
++LL |     for v in m.values() {
++   |         ^    ^^^^^^^^^^
++
++error: you seem to want to iterate on a map's values
++  --> $DIR/for_kv_map.rs:14:19
++   |
++LL |     for (_, v) in &*m {
++   |                   ^^^
++   |
++help: use the corresponding method
++   |
++LL |     for v in (*m).values() {
++   |         ^    ^^^^^^^^^^^^^
++
++error: you seem to want to iterate on a map's values
++  --> $DIR/for_kv_map.rs:22:19
++   |
++LL |     for (_, v) in &mut m {
++   |                   ^^^^^^
++   |
++help: use the corresponding method
++   |
++LL |     for v in m.values_mut() {
++   |         ^    ^^^^^^^^^^^^^^
++
++error: you seem to want to iterate on a map's values
++  --> $DIR/for_kv_map.rs:27:19
++   |
++LL |     for (_, v) in &mut *m {
++   |                   ^^^^^^^
++   |
++help: use the corresponding method
++   |
++LL |     for v in (*m).values_mut() {
++   |         ^    ^^^^^^^^^^^^^^^^^
++
++error: you seem to want to iterate on a map's keys
++  --> $DIR/for_kv_map.rs:33:24
++   |
++LL |     for (k, _value) in rm {
++   |                        ^^
++   |
++help: use the corresponding method
++   |
++LL |     for k in rm.keys() {
++   |         ^    ^^^^^^^^^
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5fc84ada9efdd4368f1f6806c4c74dcffabdb917
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,337 @@@
++// run-rustfix
++
++#![allow(dead_code, unused)]
++
++use std::collections::*;
++
++#[warn(clippy::all)]
++struct Unrelated(Vec<u8>);
++impl Unrelated {
++    fn next(&self) -> std::slice::Iter<u8> {
++        self.0.iter()
++    }
++
++    fn iter(&self) -> std::slice::Iter<u8> {
++        self.0.iter()
++    }
++}
++
++#[warn(
++    clippy::needless_range_loop,
++    clippy::explicit_iter_loop,
++    clippy::explicit_into_iter_loop,
++    clippy::iter_next_loop,
++    clippy::reverse_range_loop,
++    clippy::for_kv_map
++)]
++#[allow(
++    clippy::linkedlist,
++    clippy::shadow_unrelated,
++    clippy::unnecessary_mut_passed,
++    clippy::similar_names
++)]
++#[allow(clippy::many_single_char_names, unused_variables)]
++fn main() {
++    const MAX_LEN: usize = 42;
++    let mut vec = vec![1, 2, 3, 4];
++
++    for i in (0..10).rev() {
++        println!("{}", i);
++    }
++
++    for i in (0..=10).rev() {
++        println!("{}", i);
++    }
++
++    for i in (0..MAX_LEN).rev() {
++        println!("{}", i);
++    }
++
++    for i in 5..=5 {
++        // not an error, this is the range with only one element “5”
++        println!("{}", i);
++    }
++
++    for i in 0..10 {
++        // not an error, the start index is less than the end index
++        println!("{}", i);
++    }
++
++    for i in -10..0 {
++        // not an error
++        println!("{}", i);
++    }
++
++    for i in (10..0).map(|x| x * 2) {
++        // not an error, it can't be known what arbitrary methods do to a range
++        println!("{}", i);
++    }
++
++    // testing that the empty range lint folds constants
++    for i in (5 + 4..10).rev() {
++        println!("{}", i);
++    }
++
++    for i in ((3 - 1)..(5 + 2)).rev() {
++        println!("{}", i);
++    }
++
++    for i in (2 * 2)..(2 * 3) {
++        // no error, 4..6 is fine
++        println!("{}", i);
++    }
++
++    let x = 42;
++    for i in x..10 {
++        // no error, not constant-foldable
++        println!("{}", i);
++    }
++
++    // See #601
++    for i in 0..10 {
++        // no error, id_col does not exist outside the loop
++        let mut id_col = vec![0f64; 10];
++        id_col[i] = 1f64;
++    }
++
++    for _v in &vec {}
++
++    for _v in &mut vec {}
++
++    let out_vec = vec![1, 2, 3];
++    for _v in out_vec {}
++
++    for _v in &vec {} // these are fine
++    for _v in &mut vec {} // these are fine
++
++    for _v in &[1, 2, 3] {}
++
++    for _v in (&mut [1, 2, 3]).iter() {} // no error
++
++    for _v in &[0; 32] {}
++
++    for _v in [0; 33].iter() {} // no error
++
++    let ll: LinkedList<()> = LinkedList::new();
++    for _v in &ll {}
++
++    let vd: VecDeque<()> = VecDeque::new();
++    for _v in &vd {}
++
++    let bh: BinaryHeap<()> = BinaryHeap::new();
++    for _v in &bh {}
++
++    let hm: HashMap<(), ()> = HashMap::new();
++    for _v in &hm {}
++
++    let bt: BTreeMap<(), ()> = BTreeMap::new();
++    for _v in &bt {}
++
++    let hs: HashSet<()> = HashSet::new();
++    for _v in &hs {}
++
++    let bs: BTreeSet<()> = BTreeSet::new();
++    for _v in &bs {}
++
++    let u = Unrelated(vec![]);
++    for _v in u.next() {} // no error
++    for _v in u.iter() {} // no error
++
++    let mut out = vec![];
++    vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>();
++    let _y = vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>(); // this is fine
++
++    // Loop with explicit counter variable
++
++    // Potential false positives
++    let mut _index = 0;
++    _index = 1;
++    for _v in &vec {
++        _index += 1
++    }
++
++    let mut _index = 0;
++    _index += 1;
++    for _v in &vec {
++        _index += 1
++    }
++
++    let mut _index = 0;
++    if true {
++        _index = 1
++    }
++    for _v in &vec {
++        _index += 1
++    }
++
++    let mut _index = 0;
++    let mut _index = 1;
++    for _v in &vec {
++        _index += 1
++    }
++
++    let mut _index = 0;
++    for _v in &vec {
++        _index += 1;
++        _index += 1
++    }
++
++    let mut _index = 0;
++    for _v in &vec {
++        _index *= 2;
++        _index += 1
++    }
++
++    let mut _index = 0;
++    for _v in &vec {
++        _index = 1;
++        _index += 1
++    }
++
++    let mut _index = 0;
++
++    for _v in &vec {
++        let mut _index = 0;
++        _index += 1
++    }
++
++    let mut _index = 0;
++    for _v in &vec {
++        _index += 1;
++        _index = 0;
++    }
++
++    let mut _index = 0;
++    for _v in &vec {
++        for _x in 0..1 {
++            _index += 1;
++        }
++        _index += 1
++    }
++
++    let mut _index = 0;
++    for x in &vec {
++        if *x == 1 {
++            _index += 1
++        }
++    }
++
++    let mut _index = 0;
++    if true {
++        _index = 1
++    };
++    for _v in &vec {
++        _index += 1
++    }
++
++    let mut _index = 1;
++    if false {
++        _index = 0
++    };
++    for _v in &vec {
++        _index += 1
++    }
++
++    let mut index = 0;
++    {
++        let mut _x = &mut index;
++    }
++    for _v in &vec {
++        _index += 1
++    }
++
++    let mut index = 0;
++    for _v in &vec {
++        index += 1
++    }
++    println!("index: {}", index);
++
++    fn f<T>(_: &T, _: &T) -> bool {
++        unimplemented!()
++    }
++    fn g<T>(_: &mut [T], _: usize, _: usize) {
++        unimplemented!()
++    }
++    for i in 1..vec.len() {
++        if f(&vec[i - 1], &vec[i]) {
++            g(&mut vec, i - 1, i);
++        }
++    }
++
++    for mid in 1..vec.len() {
++        let (_, _) = vec.split_at(mid);
++    }
++}
++
++fn partition<T: PartialOrd + Send>(v: &mut [T]) -> usize {
++    let pivot = v.len() - 1;
++    let mut i = 0;
++    for j in 0..pivot {
++        if v[j] <= v[pivot] {
++            v.swap(i, j);
++            i += 1;
++        }
++    }
++    v.swap(i, pivot);
++    i
++}
++
++#[warn(clippy::needless_range_loop)]
++pub fn manual_copy_same_destination(dst: &mut [i32], d: usize, s: usize) {
++    // Same source and destination - don't trigger lint
++    for i in 0..dst.len() {
++        dst[d + i] = dst[s + i];
++    }
++}
++
++mod issue_2496 {
++    pub trait Handle {
++        fn new_for_index(index: usize) -> Self;
++        fn index(&self) -> usize;
++    }
++
++    pub fn test<H: Handle>() -> H {
++        for x in 0..5 {
++            let next_handle = H::new_for_index(x);
++            println!("{}", next_handle.index());
++        }
++        unimplemented!()
++    }
++}
++
++// explicit_into_iter_loop bad suggestions
++#[warn(clippy::explicit_into_iter_loop, clippy::explicit_iter_loop)]
++mod issue_4958 {
++    fn takes_iterator<T>(iterator: &T)
++    where
++        for<'a> &'a T: IntoIterator<Item = &'a String>,
++    {
++        for i in iterator {
++            println!("{}", i);
++        }
++    }
++
++    struct T;
++    impl IntoIterator for &T {
++        type Item = ();
++        type IntoIter = std::vec::IntoIter<Self::Item>;
++        fn into_iter(self) -> Self::IntoIter {
++            vec![].into_iter()
++        }
++    }
++
++    fn more_tests() {
++        let t = T;
++        let r = &t;
++        let rr = &&t;
++
++        // This case is handled by `explicit_iter_loop`. No idea why.
++        for _ in &t {}
++
++        for _ in r {}
++
++        // No suggestion for this.
++        // We'd have to suggest `for _ in *rr {}` which is less clear.
++        for _ in rr.into_iter() {}
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4165b0dc004942cae3f3fc0ef23b75ed7107d14f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,337 @@@
++// run-rustfix
++
++#![allow(dead_code, unused)]
++
++use std::collections::*;
++
++#[warn(clippy::all)]
++struct Unrelated(Vec<u8>);
++impl Unrelated {
++    fn next(&self) -> std::slice::Iter<u8> {
++        self.0.iter()
++    }
++
++    fn iter(&self) -> std::slice::Iter<u8> {
++        self.0.iter()
++    }
++}
++
++#[warn(
++    clippy::needless_range_loop,
++    clippy::explicit_iter_loop,
++    clippy::explicit_into_iter_loop,
++    clippy::iter_next_loop,
++    clippy::reverse_range_loop,
++    clippy::for_kv_map
++)]
++#[allow(
++    clippy::linkedlist,
++    clippy::shadow_unrelated,
++    clippy::unnecessary_mut_passed,
++    clippy::similar_names
++)]
++#[allow(clippy::many_single_char_names, unused_variables)]
++fn main() {
++    const MAX_LEN: usize = 42;
++    let mut vec = vec![1, 2, 3, 4];
++
++    for i in 10..0 {
++        println!("{}", i);
++    }
++
++    for i in 10..=0 {
++        println!("{}", i);
++    }
++
++    for i in MAX_LEN..0 {
++        println!("{}", i);
++    }
++
++    for i in 5..=5 {
++        // not an error, this is the range with only one element “5”
++        println!("{}", i);
++    }
++
++    for i in 0..10 {
++        // not an error, the start index is less than the end index
++        println!("{}", i);
++    }
++
++    for i in -10..0 {
++        // not an error
++        println!("{}", i);
++    }
++
++    for i in (10..0).map(|x| x * 2) {
++        // not an error, it can't be known what arbitrary methods do to a range
++        println!("{}", i);
++    }
++
++    // testing that the empty range lint folds constants
++    for i in 10..5 + 4 {
++        println!("{}", i);
++    }
++
++    for i in (5 + 2)..(3 - 1) {
++        println!("{}", i);
++    }
++
++    for i in (2 * 2)..(2 * 3) {
++        // no error, 4..6 is fine
++        println!("{}", i);
++    }
++
++    let x = 42;
++    for i in x..10 {
++        // no error, not constant-foldable
++        println!("{}", i);
++    }
++
++    // See #601
++    for i in 0..10 {
++        // no error, id_col does not exist outside the loop
++        let mut id_col = vec![0f64; 10];
++        id_col[i] = 1f64;
++    }
++
++    for _v in vec.iter() {}
++
++    for _v in vec.iter_mut() {}
++
++    let out_vec = vec![1, 2, 3];
++    for _v in out_vec.into_iter() {}
++
++    for _v in &vec {} // these are fine
++    for _v in &mut vec {} // these are fine
++
++    for _v in [1, 2, 3].iter() {}
++
++    for _v in (&mut [1, 2, 3]).iter() {} // no error
++
++    for _v in [0; 32].iter() {}
++
++    for _v in [0; 33].iter() {} // no error
++
++    let ll: LinkedList<()> = LinkedList::new();
++    for _v in ll.iter() {}
++
++    let vd: VecDeque<()> = VecDeque::new();
++    for _v in vd.iter() {}
++
++    let bh: BinaryHeap<()> = BinaryHeap::new();
++    for _v in bh.iter() {}
++
++    let hm: HashMap<(), ()> = HashMap::new();
++    for _v in hm.iter() {}
++
++    let bt: BTreeMap<(), ()> = BTreeMap::new();
++    for _v in bt.iter() {}
++
++    let hs: HashSet<()> = HashSet::new();
++    for _v in hs.iter() {}
++
++    let bs: BTreeSet<()> = BTreeSet::new();
++    for _v in bs.iter() {}
++
++    let u = Unrelated(vec![]);
++    for _v in u.next() {} // no error
++    for _v in u.iter() {} // no error
++
++    let mut out = vec![];
++    vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>();
++    let _y = vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>(); // this is fine
++
++    // Loop with explicit counter variable
++
++    // Potential false positives
++    let mut _index = 0;
++    _index = 1;
++    for _v in &vec {
++        _index += 1
++    }
++
++    let mut _index = 0;
++    _index += 1;
++    for _v in &vec {
++        _index += 1
++    }
++
++    let mut _index = 0;
++    if true {
++        _index = 1
++    }
++    for _v in &vec {
++        _index += 1
++    }
++
++    let mut _index = 0;
++    let mut _index = 1;
++    for _v in &vec {
++        _index += 1
++    }
++
++    let mut _index = 0;
++    for _v in &vec {
++        _index += 1;
++        _index += 1
++    }
++
++    let mut _index = 0;
++    for _v in &vec {
++        _index *= 2;
++        _index += 1
++    }
++
++    let mut _index = 0;
++    for _v in &vec {
++        _index = 1;
++        _index += 1
++    }
++
++    let mut _index = 0;
++
++    for _v in &vec {
++        let mut _index = 0;
++        _index += 1
++    }
++
++    let mut _index = 0;
++    for _v in &vec {
++        _index += 1;
++        _index = 0;
++    }
++
++    let mut _index = 0;
++    for _v in &vec {
++        for _x in 0..1 {
++            _index += 1;
++        }
++        _index += 1
++    }
++
++    let mut _index = 0;
++    for x in &vec {
++        if *x == 1 {
++            _index += 1
++        }
++    }
++
++    let mut _index = 0;
++    if true {
++        _index = 1
++    };
++    for _v in &vec {
++        _index += 1
++    }
++
++    let mut _index = 1;
++    if false {
++        _index = 0
++    };
++    for _v in &vec {
++        _index += 1
++    }
++
++    let mut index = 0;
++    {
++        let mut _x = &mut index;
++    }
++    for _v in &vec {
++        _index += 1
++    }
++
++    let mut index = 0;
++    for _v in &vec {
++        index += 1
++    }
++    println!("index: {}", index);
++
++    fn f<T>(_: &T, _: &T) -> bool {
++        unimplemented!()
++    }
++    fn g<T>(_: &mut [T], _: usize, _: usize) {
++        unimplemented!()
++    }
++    for i in 1..vec.len() {
++        if f(&vec[i - 1], &vec[i]) {
++            g(&mut vec, i - 1, i);
++        }
++    }
++
++    for mid in 1..vec.len() {
++        let (_, _) = vec.split_at(mid);
++    }
++}
++
++fn partition<T: PartialOrd + Send>(v: &mut [T]) -> usize {
++    let pivot = v.len() - 1;
++    let mut i = 0;
++    for j in 0..pivot {
++        if v[j] <= v[pivot] {
++            v.swap(i, j);
++            i += 1;
++        }
++    }
++    v.swap(i, pivot);
++    i
++}
++
++#[warn(clippy::needless_range_loop)]
++pub fn manual_copy_same_destination(dst: &mut [i32], d: usize, s: usize) {
++    // Same source and destination - don't trigger lint
++    for i in 0..dst.len() {
++        dst[d + i] = dst[s + i];
++    }
++}
++
++mod issue_2496 {
++    pub trait Handle {
++        fn new_for_index(index: usize) -> Self;
++        fn index(&self) -> usize;
++    }
++
++    pub fn test<H: Handle>() -> H {
++        for x in 0..5 {
++            let next_handle = H::new_for_index(x);
++            println!("{}", next_handle.index());
++        }
++        unimplemented!()
++    }
++}
++
++// explicit_into_iter_loop bad suggestions
++#[warn(clippy::explicit_into_iter_loop, clippy::explicit_iter_loop)]
++mod issue_4958 {
++    fn takes_iterator<T>(iterator: &T)
++    where
++        for<'a> &'a T: IntoIterator<Item = &'a String>,
++    {
++        for i in iterator.into_iter() {
++            println!("{}", i);
++        }
++    }
++
++    struct T;
++    impl IntoIterator for &T {
++        type Item = ();
++        type IntoIter = std::vec::IntoIter<Self::Item>;
++        fn into_iter(self) -> Self::IntoIter {
++            vec![].into_iter()
++        }
++    }
++
++    fn more_tests() {
++        let t = T;
++        let r = &t;
++        let rr = &&t;
++
++        // This case is handled by `explicit_iter_loop`. No idea why.
++        for _ in t.into_iter() {}
++
++        for _ in r.into_iter() {}
++
++        // No suggestion for this.
++        // We'd have to suggest `for _ in *rr {}` which is less clear.
++        for _ in rr.into_iter() {}
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cffb4b9f0a9c0bf1eecca85f10488dff5588246f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,152 @@@
++error: this range is empty so this for loop will never run
++  --> $DIR/for_loop_fixable.rs:38:14
++   |
++LL |     for i in 10..0 {
++   |              ^^^^^
++   |
++   = note: `-D clippy::reverse-range-loop` implied by `-D warnings`
++help: consider using the following if you are attempting to iterate over this range in reverse
++   |
++LL |     for i in (0..10).rev() {
++   |              ^^^^^^^^^^^^^
++
++error: this range is empty so this for loop will never run
++  --> $DIR/for_loop_fixable.rs:42:14
++   |
++LL |     for i in 10..=0 {
++   |              ^^^^^^
++   |
++help: consider using the following if you are attempting to iterate over this range in reverse
++   |
++LL |     for i in (0..=10).rev() {
++   |              ^^^^^^^^^^^^^^
++
++error: this range is empty so this for loop will never run
++  --> $DIR/for_loop_fixable.rs:46:14
++   |
++LL |     for i in MAX_LEN..0 {
++   |              ^^^^^^^^^^
++   |
++help: consider using the following if you are attempting to iterate over this range in reverse
++   |
++LL |     for i in (0..MAX_LEN).rev() {
++   |              ^^^^^^^^^^^^^^^^^^
++
++error: this range is empty so this for loop will never run
++  --> $DIR/for_loop_fixable.rs:71:14
++   |
++LL |     for i in 10..5 + 4 {
++   |              ^^^^^^^^^
++   |
++help: consider using the following if you are attempting to iterate over this range in reverse
++   |
++LL |     for i in (5 + 4..10).rev() {
++   |              ^^^^^^^^^^^^^^^^^
++
++error: this range is empty so this for loop will never run
++  --> $DIR/for_loop_fixable.rs:75:14
++   |
++LL |     for i in (5 + 2)..(3 - 1) {
++   |              ^^^^^^^^^^^^^^^^
++   |
++help: consider using the following if you are attempting to iterate over this range in reverse
++   |
++LL |     for i in ((3 - 1)..(5 + 2)).rev() {
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++  --> $DIR/for_loop_fixable.rs:97:15
++   |
++LL |     for _v in vec.iter() {}
++   |               ^^^^^^^^^^ help: to write this more concisely, try: `&vec`
++   |
++   = note: `-D clippy::explicit-iter-loop` implied by `-D warnings`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++  --> $DIR/for_loop_fixable.rs:99:15
++   |
++LL |     for _v in vec.iter_mut() {}
++   |               ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec`
++
++error: it is more concise to loop over containers instead of using explicit iteration methods
++  --> $DIR/for_loop_fixable.rs:102:15
++   |
++LL |     for _v in out_vec.into_iter() {}
++   |               ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `out_vec`
++   |
++   = note: `-D clippy::explicit-into-iter-loop` implied by `-D warnings`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++  --> $DIR/for_loop_fixable.rs:107:15
++   |
++LL |     for _v in [1, 2, 3].iter() {}
++   |               ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++  --> $DIR/for_loop_fixable.rs:111:15
++   |
++LL |     for _v in [0; 32].iter() {}
++   |               ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++  --> $DIR/for_loop_fixable.rs:116:15
++   |
++LL |     for _v in ll.iter() {}
++   |               ^^^^^^^^^ help: to write this more concisely, try: `&ll`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++  --> $DIR/for_loop_fixable.rs:119:15
++   |
++LL |     for _v in vd.iter() {}
++   |               ^^^^^^^^^ help: to write this more concisely, try: `&vd`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++  --> $DIR/for_loop_fixable.rs:122:15
++   |
++LL |     for _v in bh.iter() {}
++   |               ^^^^^^^^^ help: to write this more concisely, try: `&bh`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++  --> $DIR/for_loop_fixable.rs:125:15
++   |
++LL |     for _v in hm.iter() {}
++   |               ^^^^^^^^^ help: to write this more concisely, try: `&hm`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++  --> $DIR/for_loop_fixable.rs:128:15
++   |
++LL |     for _v in bt.iter() {}
++   |               ^^^^^^^^^ help: to write this more concisely, try: `&bt`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++  --> $DIR/for_loop_fixable.rs:131:15
++   |
++LL |     for _v in hs.iter() {}
++   |               ^^^^^^^^^ help: to write this more concisely, try: `&hs`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++  --> $DIR/for_loop_fixable.rs:134:15
++   |
++LL |     for _v in bs.iter() {}
++   |               ^^^^^^^^^ help: to write this more concisely, try: `&bs`
++
++error: it is more concise to loop over containers instead of using explicit iteration methods
++  --> $DIR/for_loop_fixable.rs:309:18
++   |
++LL |         for i in iterator.into_iter() {
++   |                  ^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `iterator`
++
++error: it is more concise to loop over references to containers instead of using explicit iteration methods
++  --> $DIR/for_loop_fixable.rs:329:18
++   |
++LL |         for _ in t.into_iter() {}
++   |                  ^^^^^^^^^^^^^ help: to write this more concisely, try: `&t`
++
++error: it is more concise to loop over containers instead of using explicit iteration methods
++  --> $DIR/for_loop_fixable.rs:331:18
++   |
++LL |         for _ in r.into_iter() {}
++   |                  ^^^^^^^^^^^^^ help: to write this more concisely, try: `r`
++
++error: aborting due to 20 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6b207b26b6b7904a05c07bd84241beb05e0e1dc7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,59 @@@
++#![warn(clippy::for_loop_over_option, clippy::for_loop_over_result)]
++
++/// Tests for_loop_over_result and for_loop_over_option
++
++fn for_loop_over_option_and_result() {
++    let option = Some(1);
++    let result = option.ok_or("x not found");
++    let v = vec![0, 1, 2];
++
++    // check FOR_LOOP_OVER_OPTION lint
++    for x in option {
++        println!("{}", x);
++    }
++
++    // check FOR_LOOP_OVER_RESULT lint
++    for x in result {
++        println!("{}", x);
++    }
++
++    for x in option.ok_or("x not found") {
++        println!("{}", x);
++    }
++
++    // make sure LOOP_OVER_NEXT lint takes clippy::precedence when next() is the last call
++    // in the chain
++    for x in v.iter().next() {
++        println!("{}", x);
++    }
++
++    // make sure we lint when next() is not the last call in the chain
++    for x in v.iter().next().and(Some(0)) {
++        println!("{}", x);
++    }
++
++    for x in v.iter().next().ok_or("x not found") {
++        println!("{}", x);
++    }
++
++    // check for false positives
++
++    // for loop false positive
++    for x in v {
++        println!("{}", x);
++    }
++
++    // while let false positive for Option
++    while let Some(x) = option {
++        println!("{}", x);
++        break;
++    }
++
++    // while let false positive for Result
++    while let Ok(x) = result {
++        println!("{}", x);
++        break;
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..194a0bfec5b32ef9e9393c13c489f662ef06fe15
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,72 @@@
++error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement.
++  --> $DIR/for_loop_over_option_result.rs:11:14
++   |
++LL |     for x in option {
++   |              ^^^^^^
++   |
++   = note: `-D clippy::for-loop-over-option` implied by `-D warnings`
++   = help: consider replacing `for x in option` with `if let Some(x) = option`
++
++error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement.
++  --> $DIR/for_loop_over_option_result.rs:16:14
++   |
++LL |     for x in result {
++   |              ^^^^^^
++   |
++   = note: `-D clippy::for-loop-over-result` implied by `-D warnings`
++   = help: consider replacing `for x in result` with `if let Ok(x) = result`
++
++error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement.
++  --> $DIR/for_loop_over_option_result.rs:20:14
++   |
++LL |     for x in option.ok_or("x not found") {
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")`
++
++error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want
++  --> $DIR/for_loop_over_option_result.rs:26:14
++   |
++LL |     for x in v.iter().next() {
++   |              ^^^^^^^^^^^^^^^
++   |
++   = note: `#[deny(clippy::iter_next_loop)]` on by default
++
++error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement.
++  --> $DIR/for_loop_over_option_result.rs:31:14
++   |
++LL |     for x in v.iter().next().and(Some(0)) {
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))`
++
++error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement.
++  --> $DIR/for_loop_over_option_result.rs:35:14
++   |
++LL |     for x in v.iter().next().ok_or("x not found") {
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")`
++
++error: this loop never actually loops
++  --> $DIR/for_loop_over_option_result.rs:47:5
++   |
++LL | /     while let Some(x) = option {
++LL | |         println!("{}", x);
++LL | |         break;
++LL | |     }
++   | |_____^
++   |
++   = note: `#[deny(clippy::never_loop)]` on by default
++
++error: this loop never actually loops
++  --> $DIR/for_loop_over_option_result.rs:53:5
++   |
++LL | /     while let Ok(x) = result {
++LL | |         println!("{}", x);
++LL | |         break;
++LL | |     }
++   | |_____^
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..179b255e08ca77962bdb5a096333dd4d854b068c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++// Tests from for_loop.rs that don't have suggestions
++
++#[warn(
++    clippy::needless_range_loop,
++    clippy::explicit_iter_loop,
++    clippy::explicit_into_iter_loop,
++    clippy::iter_next_loop,
++    clippy::reverse_range_loop,
++    clippy::for_kv_map
++)]
++#[allow(
++    clippy::linkedlist,
++    clippy::shadow_unrelated,
++    clippy::unnecessary_mut_passed,
++    clippy::similar_names,
++    unused,
++    dead_code
++)]
++#[allow(clippy::many_single_char_names, unused_variables)]
++fn main() {
++    for i in 5..5 {
++        println!("{}", i);
++    }
++
++    let vec = vec![1, 2, 3, 4];
++
++    for _v in vec.iter().next() {}
++
++    for i in (5 + 2)..(8 - 1) {
++        println!("{}", i);
++    }
++
++    const ZERO: usize = 0;
++
++    for i in ZERO..vec.len() {
++        if f(&vec[i], &vec[i]) {
++            panic!("at the disco");
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1da8e0f3588d79fd3defd9429ff4bcd91055b61d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++error[E0425]: cannot find function `f` in this scope
++  --> $DIR/for_loop_unfixable.rs:36:12
++   |
++LL |         if f(&vec[i], &vec[i]) {
++   |            ^ help: a local variable with a similar name exists: `i`
++
++error: aborting due to previous error
++
++For more information about this error, try `rustc --explain E0425`.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..447fdbe1fac53ee1dbbb1217473b87237d41a467
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,48 @@@
++#![warn(clippy::forget_ref)]
++#![allow(clippy::toplevel_ref_arg)]
++
++use std::mem::forget;
++
++struct SomeStruct;
++
++fn main() {
++    forget(&SomeStruct);
++
++    let mut owned = SomeStruct;
++    forget(&owned);
++    forget(&&owned);
++    forget(&mut owned);
++    forget(owned); //OK
++
++    let reference1 = &SomeStruct;
++    forget(&*reference1);
++
++    let reference2 = &mut SomeStruct;
++    forget(reference2);
++
++    let ref reference3 = SomeStruct;
++    forget(reference3);
++}
++
++#[allow(dead_code)]
++fn test_generic_fn_forget<T>(val: T) {
++    forget(&val);
++    forget(val); //OK
++}
++
++#[allow(dead_code)]
++fn test_similarly_named_function() {
++    fn forget<T>(_val: T) {}
++    forget(&SomeStruct); //OK; call to unrelated function which happens to have the same name
++    std::mem::forget(&SomeStruct);
++}
++
++#[derive(Copy, Clone)]
++pub struct Error;
++fn produce_half_owl_error() -> Result<(), Error> {
++    Ok(())
++}
++
++fn produce_half_owl_ok() -> Result<bool, ()> {
++    Ok(true)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f90bcc2762cee2a92dcf73e96540011b68c0b422
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,111 @@@
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++  --> $DIR/forget_ref.rs:9:5
++   |
++LL |     forget(&SomeStruct);
++   |     ^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::forget-ref` implied by `-D warnings`
++note: argument has type `&SomeStruct`
++  --> $DIR/forget_ref.rs:9:12
++   |
++LL |     forget(&SomeStruct);
++   |            ^^^^^^^^^^^
++
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++  --> $DIR/forget_ref.rs:12:5
++   |
++LL |     forget(&owned);
++   |     ^^^^^^^^^^^^^^
++   |
++note: argument has type `&SomeStruct`
++  --> $DIR/forget_ref.rs:12:12
++   |
++LL |     forget(&owned);
++   |            ^^^^^^
++
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++  --> $DIR/forget_ref.rs:13:5
++   |
++LL |     forget(&&owned);
++   |     ^^^^^^^^^^^^^^^
++   |
++note: argument has type `&&SomeStruct`
++  --> $DIR/forget_ref.rs:13:12
++   |
++LL |     forget(&&owned);
++   |            ^^^^^^^
++
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++  --> $DIR/forget_ref.rs:14:5
++   |
++LL |     forget(&mut owned);
++   |     ^^^^^^^^^^^^^^^^^^
++   |
++note: argument has type `&mut SomeStruct`
++  --> $DIR/forget_ref.rs:14:12
++   |
++LL |     forget(&mut owned);
++   |            ^^^^^^^^^^
++
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++  --> $DIR/forget_ref.rs:18:5
++   |
++LL |     forget(&*reference1);
++   |     ^^^^^^^^^^^^^^^^^^^^
++   |
++note: argument has type `&SomeStruct`
++  --> $DIR/forget_ref.rs:18:12
++   |
++LL |     forget(&*reference1);
++   |            ^^^^^^^^^^^^
++
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++  --> $DIR/forget_ref.rs:21:5
++   |
++LL |     forget(reference2);
++   |     ^^^^^^^^^^^^^^^^^^
++   |
++note: argument has type `&mut SomeStruct`
++  --> $DIR/forget_ref.rs:21:12
++   |
++LL |     forget(reference2);
++   |            ^^^^^^^^^^
++
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++  --> $DIR/forget_ref.rs:24:5
++   |
++LL |     forget(reference3);
++   |     ^^^^^^^^^^^^^^^^^^
++   |
++note: argument has type `&SomeStruct`
++  --> $DIR/forget_ref.rs:24:12
++   |
++LL |     forget(reference3);
++   |            ^^^^^^^^^^
++
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++  --> $DIR/forget_ref.rs:29:5
++   |
++LL |     forget(&val);
++   |     ^^^^^^^^^^^^
++   |
++note: argument has type `&T`
++  --> $DIR/forget_ref.rs:29:12
++   |
++LL |     forget(&val);
++   |            ^^^^
++
++error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
++  --> $DIR/forget_ref.rs:37:5
++   |
++LL |     std::mem::forget(&SomeStruct);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++note: argument has type `&SomeStruct`
++  --> $DIR/forget_ref.rs:37:22
++   |
++LL |     std::mem::forget(&SomeStruct);
++   |                      ^^^^^^^^^^^
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..306514769990d82edde8ff07ebe1780c625cb061
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,67 @@@
++// run-rustfix
++
++#![allow(clippy::print_literal, clippy::redundant_clone)]
++#![warn(clippy::useless_format)]
++
++struct Foo(pub String);
++
++macro_rules! foo {
++    ($($t:tt)*) => (Foo(format!($($t)*)))
++}
++
++fn main() {
++    "foo".to_string();
++    "{}".to_string();
++    "{} abc {}".to_string();
++    "foo {}\n\" bar".to_string();
++
++    "foo".to_string();
++    format!("{:?}", "foo"); // Don't warn about `Debug`.
++    format!("{:8}", "foo");
++    format!("{:width$}", "foo", width = 8);
++    "foo".to_string(); // Warn when the format makes no difference.
++    "foo".to_string(); // Warn when the format makes no difference.
++    format!("foo {}", "bar");
++    format!("{} bar", "foo");
++
++    let arg: String = "".to_owned();
++    arg.to_string();
++    format!("{:?}", arg); // Don't warn about debug.
++    format!("{:8}", arg);
++    format!("{:width$}", arg, width = 8);
++    arg.to_string(); // Warn when the format makes no difference.
++    arg.to_string(); // Warn when the format makes no difference.
++    format!("foo {}", arg);
++    format!("{} bar", arg);
++
++    // We don’t want to warn for non-string args; see issue #697.
++    format!("{}", 42);
++    format!("{:?}", 42);
++    format!("{:+}", 42);
++    format!("foo {}", 42);
++    format!("{} bar", 42);
++
++    // We only want to warn about `format!` itself.
++    println!("foo");
++    println!("{}", "foo");
++    println!("foo {}", "foo");
++    println!("{}", 42);
++    println!("foo {}", 42);
++
++    // A `format!` inside a macro should not trigger a warning.
++    foo!("should not warn");
++
++    // Precision on string means slicing without panicking on size.
++    format!("{:.1}", "foo"); // Could be `"foo"[..1]`
++    format!("{:.10}", "foo"); // Could not be `"foo"[..10]`
++    format!("{:.prec$}", "foo", prec = 1);
++    format!("{:.prec$}", "foo", prec = 10);
++
++    42.to_string();
++    let x = std::path::PathBuf::from("/bar/foo/qux");
++    x.display().to_string();
++
++    // False positive
++    let a = "foo".to_string();
++    let _ = Some(a + "bar");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b604d79cca373119935232370d9cee658c540f5c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,70 @@@
++// run-rustfix
++
++#![allow(clippy::print_literal, clippy::redundant_clone)]
++#![warn(clippy::useless_format)]
++
++struct Foo(pub String);
++
++macro_rules! foo {
++    ($($t:tt)*) => (Foo(format!($($t)*)))
++}
++
++fn main() {
++    format!("foo");
++    format!("{{}}");
++    format!("{{}} abc {{}}");
++    format!(
++        r##"foo {{}}
++" bar"##
++    );
++
++    format!("{}", "foo");
++    format!("{:?}", "foo"); // Don't warn about `Debug`.
++    format!("{:8}", "foo");
++    format!("{:width$}", "foo", width = 8);
++    format!("{:+}", "foo"); // Warn when the format makes no difference.
++    format!("{:<}", "foo"); // Warn when the format makes no difference.
++    format!("foo {}", "bar");
++    format!("{} bar", "foo");
++
++    let arg: String = "".to_owned();
++    format!("{}", arg);
++    format!("{:?}", arg); // Don't warn about debug.
++    format!("{:8}", arg);
++    format!("{:width$}", arg, width = 8);
++    format!("{:+}", arg); // Warn when the format makes no difference.
++    format!("{:<}", arg); // Warn when the format makes no difference.
++    format!("foo {}", arg);
++    format!("{} bar", arg);
++
++    // We don’t want to warn for non-string args; see issue #697.
++    format!("{}", 42);
++    format!("{:?}", 42);
++    format!("{:+}", 42);
++    format!("foo {}", 42);
++    format!("{} bar", 42);
++
++    // We only want to warn about `format!` itself.
++    println!("foo");
++    println!("{}", "foo");
++    println!("foo {}", "foo");
++    println!("{}", 42);
++    println!("foo {}", 42);
++
++    // A `format!` inside a macro should not trigger a warning.
++    foo!("should not warn");
++
++    // Precision on string means slicing without panicking on size.
++    format!("{:.1}", "foo"); // Could be `"foo"[..1]`
++    format!("{:.10}", "foo"); // Could not be `"foo"[..10]`
++    format!("{:.prec$}", "foo", prec = 1);
++    format!("{:.prec$}", "foo", prec = 10);
++
++    format!("{}", 42.to_string());
++    let x = std::path::PathBuf::from("/bar/foo/qux");
++    format!("{}", x.display().to_string());
++
++    // False positive
++    let a = "foo".to_string();
++    let _ = Some(format!("{}", a + "bar"));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9734492154e8009fca62c7e5a2903cb649507c00
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,85 @@@
++error: useless use of `format!`
++  --> $DIR/format.rs:13:5
++   |
++LL |     format!("foo");
++   |     ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();`
++   |
++   = note: `-D clippy::useless-format` implied by `-D warnings`
++
++error: useless use of `format!`
++  --> $DIR/format.rs:14:5
++   |
++LL |     format!("{{}}");
++   |     ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string();`
++
++error: useless use of `format!`
++  --> $DIR/format.rs:15:5
++   |
++LL |     format!("{{}} abc {{}}");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string();`
++
++error: useless use of `format!`
++  --> $DIR/format.rs:16:5
++   |
++LL | /     format!(
++LL | |         r##"foo {{}}
++LL | | " bar"##
++LL | |     );
++   | |______^ help: consider using `.to_string()`: `"foo {}/n/" bar".to_string();`
++
++error: useless use of `format!`
++  --> $DIR/format.rs:21:5
++   |
++LL |     format!("{}", "foo");
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();`
++
++error: useless use of `format!`
++  --> $DIR/format.rs:25:5
++   |
++LL |     format!("{:+}", "foo"); // Warn when the format makes no difference.
++   |     ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();`
++
++error: useless use of `format!`
++  --> $DIR/format.rs:26:5
++   |
++LL |     format!("{:<}", "foo"); // Warn when the format makes no difference.
++   |     ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();`
++
++error: useless use of `format!`
++  --> $DIR/format.rs:31:5
++   |
++LL |     format!("{}", arg);
++   |     ^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string();`
++
++error: useless use of `format!`
++  --> $DIR/format.rs:35:5
++   |
++LL |     format!("{:+}", arg); // Warn when the format makes no difference.
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string();`
++
++error: useless use of `format!`
++  --> $DIR/format.rs:36:5
++   |
++LL |     format!("{:<}", arg); // Warn when the format makes no difference.
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string();`
++
++error: useless use of `format!`
++  --> $DIR/format.rs:63:5
++   |
++LL |     format!("{}", 42.to_string());
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string();`
++
++error: useless use of `format!`
++  --> $DIR/format.rs:65:5
++   |
++LL |     format!("{}", x.display().to_string());
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string();`
++
++error: useless use of `format!`
++  --> $DIR/format.rs:69:18
++   |
++LL |     let _ = Some(format!("{}", a + "bar"));
++   |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
++
++error: aborting due to 13 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..078811b8d882b8ba378b8f3b86409f82a8b8fbb5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,157 @@@
++#![warn(clippy::all)]
++#![allow(unused_variables)]
++#![allow(unused_assignments)]
++#![allow(clippy::if_same_then_else)]
++#![allow(clippy::deref_addrof)]
++
++fn foo() -> bool {
++    true
++}
++
++#[rustfmt::skip]
++fn main() {
++    // weird `else` formatting:
++    if foo() {
++    } {
++    }
++
++    if foo() {
++    } if foo() {
++    }
++
++    let _ = { // if as the last expression
++        let _ = 0;
++
++        if foo() {
++        } if foo() {
++        }
++        else {
++        }
++    };
++
++    let _ = { // if in the middle of a block
++        if foo() {
++        } if foo() {
++        }
++        else {
++        }
++
++        let _ = 0;
++    };
++
++    if foo() {
++    } else
++    {
++    }
++
++    if foo() {
++    }
++    else
++    {
++    }
++
++    if foo() {
++    } else
++    if foo() { // the span of the above error should continue here
++    }
++
++    if foo() {
++    }
++    else
++    if foo() { // the span of the above error should continue here
++    }
++
++    // those are ok:
++    if foo() {
++    }
++    {
++    }
++
++    if foo() {
++    } else {
++    }
++
++    if foo() {
++    }
++    else {
++    }
++
++    if foo() {
++    }
++    if foo() {
++    }
++
++    if foo() {
++    } else if foo() {
++    }
++
++    if foo() {
++    }
++    else if foo() {
++    }
++
++    if foo() {
++    }
++    else if
++    foo() {}
++
++    // weird op_eq formatting:
++    let mut a = 42;
++    a =- 35;
++    a =* &191;
++
++    let mut b = true;
++    b =! false;
++
++    // those are ok:
++    a = -35;
++    a = *&191;
++    b = !false;
++
++    // possible missing comma in an array
++    let _ = &[
++        -1, -2, -3 // <= no comma here
++        -4, -5, -6
++    ];
++    let _ = &[
++        -1, -2, -3 // <= no comma here
++        *4, -5, -6
++    ];
++
++    // those are ok:
++    let _ = &[
++        -1, -2, -3,
++        -4, -5, -6
++    ];
++    let _ = &[
++        -1, -2, -3,
++        -4, -5, -6,
++    ];
++    let _ = &[
++        1 + 2, 3 +
++        4, 5 + 6,
++    ];
++
++    // don't lint for bin op without unary equiv
++    // issue 3244
++    vec![
++        1
++        / 2,
++    ];
++    // issue 3396
++    vec![
++        true
++        | false,
++    ];
++
++    // don't lint if the indentation suggests not to
++    let _ = &[
++        1 + 2, 3 
++                - 4, 5
++    ];
++    // lint if it doesnt
++    let _ = &[
++        -1
++        -4,
++    ];
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e2095cc125bb7652db8fe7c147cf8f5362009b72
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,127 @@@
++error: this looks like an `else {..}` but the `else` is missing
++  --> $DIR/formatting.rs:15:6
++   |
++LL |     } {
++   |      ^
++   |
++   = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings`
++   = note: to remove this lint, add the missing `else` or add a new line before the next block
++
++error: this looks like an `else if` but the `else` is missing
++  --> $DIR/formatting.rs:19:6
++   |
++LL |     } if foo() {
++   |      ^
++   |
++   = note: to remove this lint, add the missing `else` or add a new line before the second `if`
++
++error: this looks like an `else if` but the `else` is missing
++  --> $DIR/formatting.rs:26:10
++   |
++LL |         } if foo() {
++   |          ^
++   |
++   = note: to remove this lint, add the missing `else` or add a new line before the second `if`
++
++error: this looks like an `else if` but the `else` is missing
++  --> $DIR/formatting.rs:34:10
++   |
++LL |         } if foo() {
++   |          ^
++   |
++   = note: to remove this lint, add the missing `else` or add a new line before the second `if`
++
++error: this is an `else {..}` but the formatting might hide it
++  --> $DIR/formatting.rs:43:6
++   |
++LL |       } else
++   |  ______^
++LL | |     {
++   | |____^
++   |
++   = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
++
++error: this is an `else {..}` but the formatting might hide it
++  --> $DIR/formatting.rs:48:6
++   |
++LL |       }
++   |  ______^
++LL | |     else
++LL | |     {
++   | |____^
++   |
++   = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
++
++error: this is an `else if` but the formatting might hide it
++  --> $DIR/formatting.rs:54:6
++   |
++LL |       } else
++   |  ______^
++LL | |     if foo() { // the span of the above error should continue here
++   | |____^
++   |
++   = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
++
++error: this is an `else if` but the formatting might hide it
++  --> $DIR/formatting.rs:59:6
++   |
++LL |       }
++   |  ______^
++LL | |     else
++LL | |     if foo() { // the span of the above error should continue here
++   | |____^
++   |
++   = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
++
++error: this looks like you are trying to use `.. -= ..`, but you really are doing `.. = (- ..)`
++  --> $DIR/formatting.rs:100:6
++   |
++LL |     a =- 35;
++   |      ^^^^
++   |
++   = note: `-D clippy::suspicious-assignment-formatting` implied by `-D warnings`
++   = note: to remove this lint, use either `-=` or `= -`
++
++error: this looks like you are trying to use `.. *= ..`, but you really are doing `.. = (* ..)`
++  --> $DIR/formatting.rs:101:6
++   |
++LL |     a =* &191;
++   |      ^^^^
++   |
++   = note: to remove this lint, use either `*=` or `= *`
++
++error: this looks like you are trying to use `.. != ..`, but you really are doing `.. = (! ..)`
++  --> $DIR/formatting.rs:104:6
++   |
++LL |     b =! false;
++   |      ^^^^
++   |
++   = note: to remove this lint, use either `!=` or `= !`
++
++error: possibly missing a comma here
++  --> $DIR/formatting.rs:113:19
++   |
++LL |         -1, -2, -3 // <= no comma here
++   |                   ^
++   |
++   = note: `-D clippy::possible-missing-comma` implied by `-D warnings`
++   = note: to remove this lint, add a comma or write the expr in a single line
++
++error: possibly missing a comma here
++  --> $DIR/formatting.rs:117:19
++   |
++LL |         -1, -2, -3 // <= no comma here
++   |                   ^
++   |
++   = note: to remove this lint, add a comma or write the expr in a single line
++
++error: possibly missing a comma here
++  --> $DIR/formatting.rs:154:11
++   |
++LL |         -1
++   |           ^
++   |
++   = note: to remove this lint, add a comma or write the expr in a single line
++
++error: aborting due to 14 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..271754cb06ee7eb7ffa4ac7a598d62b41a335a22
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,104 @@@
++#![warn(clippy::all)]
++#![allow(dead_code)]
++#![allow(unused_unsafe, clippy::missing_safety_doc)]
++
++// TOO_MANY_ARGUMENTS
++fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {}
++
++fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
++
++#[rustfmt::skip]
++fn bad_multiline(
++    one: u32,
++    two: u32,
++    three: &str,
++    four: bool,
++    five: f32,
++    six: f32,
++    seven: bool,
++    eight: ()
++) {
++    let _one = one;
++    let _two = two;
++    let _three = three;
++    let _four = four;
++    let _five = five;
++    let _six = six;
++    let _seven = seven;
++}
++
++// don't lint extern fns
++extern "C" fn extern_fn(
++    _one: u32,
++    _two: u32,
++    _three: *const u8,
++    _four: bool,
++    _five: f32,
++    _six: f32,
++    _seven: bool,
++    _eight: *const std::ffi::c_void,
++) {
++}
++
++pub trait Foo {
++    fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool);
++    fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ());
++
++    fn ptr(p: *const u8);
++}
++
++pub struct Bar;
++
++impl Bar {
++    fn good_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {}
++    fn bad_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
++}
++
++// ok, we don’t want to warn implementations
++impl Foo for Bar {
++    fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {}
++    fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
++
++    fn ptr(p: *const u8) {
++        println!("{}", unsafe { *p });
++        println!("{:?}", unsafe { p.as_ref() });
++        unsafe { std::ptr::read(p) };
++    }
++}
++
++// NOT_UNSAFE_PTR_ARG_DEREF
++
++fn private(p: *const u8) {
++    println!("{}", unsafe { *p });
++}
++
++pub fn public(p: *const u8) {
++    println!("{}", unsafe { *p });
++    println!("{:?}", unsafe { p.as_ref() });
++    unsafe { std::ptr::read(p) };
++}
++
++impl Bar {
++    fn private(self, p: *const u8) {
++        println!("{}", unsafe { *p });
++    }
++
++    pub fn public(self, p: *const u8) {
++        println!("{}", unsafe { *p });
++        println!("{:?}", unsafe { p.as_ref() });
++        unsafe { std::ptr::read(p) };
++    }
++
++    pub fn public_ok(self, p: *const u8) {
++        if !p.is_null() {
++            println!("{:p}", p);
++        }
++    }
++
++    pub unsafe fn public_unsafe(self, p: *const u8) {
++        println!("{}", unsafe { *p });
++        println!("{:?}", unsafe { p.as_ref() });
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0a86568b18de9f09016db3b3f27c18ea66bb8825
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,90 @@@
++error: this function has too many arguments (8/7)
++  --> $DIR/functions.rs:8:1
++   |
++LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::too-many-arguments` implied by `-D warnings`
++
++error: this function has too many arguments (8/7)
++  --> $DIR/functions.rs:11:1
++   |
++LL | / fn bad_multiline(
++LL | |     one: u32,
++LL | |     two: u32,
++LL | |     three: &str,
++...  |
++LL | |     eight: ()
++LL | | ) {
++   | |__^
++
++error: this function has too many arguments (8/7)
++  --> $DIR/functions.rs:45:5
++   |
++LL |     fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ());
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this function has too many arguments (8/7)
++  --> $DIR/functions.rs:54:5
++   |
++LL |     fn bad_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {}
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++  --> $DIR/functions.rs:63:34
++   |
++LL |         println!("{}", unsafe { *p });
++   |                                  ^
++   |
++   = note: `-D clippy::not-unsafe-ptr-arg-deref` implied by `-D warnings`
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++  --> $DIR/functions.rs:64:35
++   |
++LL |         println!("{:?}", unsafe { p.as_ref() });
++   |                                   ^
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++  --> $DIR/functions.rs:65:33
++   |
++LL |         unsafe { std::ptr::read(p) };
++   |                                 ^
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++  --> $DIR/functions.rs:76:30
++   |
++LL |     println!("{}", unsafe { *p });
++   |                              ^
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++  --> $DIR/functions.rs:77:31
++   |
++LL |     println!("{:?}", unsafe { p.as_ref() });
++   |                               ^
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++  --> $DIR/functions.rs:78:29
++   |
++LL |     unsafe { std::ptr::read(p) };
++   |                             ^
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++  --> $DIR/functions.rs:87:34
++   |
++LL |         println!("{}", unsafe { *p });
++   |                                  ^
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++  --> $DIR/functions.rs:88:35
++   |
++LL |         println!("{:?}", unsafe { p.as_ref() });
++   |                                   ^
++
++error: this public function dereferences a raw pointer but is not marked `unsafe`
++  --> $DIR/functions.rs:89:33
++   |
++LL |         unsafe { std::ptr::read(p) };
++   |                                 ^
++
++error: aborting due to 13 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5e1ee55e01040d2386906c5be85f53729483e808
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,163 @@@
++#![warn(clippy::too_many_lines)]
++
++fn good_lines() {
++    /* println!("This is good."); */
++    // println!("This is good.");
++    /* */ // println!("This is good.");
++    /* */ // println!("This is good.");
++    /* */ // println!("This is good.");
++    /* */ // println!("This is good.");
++    /* println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good."); */
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++    println!("This is good.");
++}
++
++fn bad_lines() {
++    println!("Dont get confused by braces: {{}}");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++    println!("This is bad.");
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9b0e7550cc314b774f400ffc095ef77921800430
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: This function has a large number of lines.
++  --> $DIR/functions_maxlines.rs:58:1
++   |
++LL | / fn bad_lines() {
++LL | |     println!("Dont get confused by braces: {{}}");
++LL | |     println!("This is bad.");
++LL | |     println!("This is bad.");
++...  |
++LL | |     println!("This is bad.");
++LL | | }
++   | |_^
++   |
++   = note: `-D clippy::too-many-lines` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6d09d71a630a28b6e85b089b6b74cf94a9558f45
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,79 @@@
++// edition:2018
++#![warn(clippy::future_not_send)]
++
++use std::cell::Cell;
++use std::rc::Rc;
++use std::sync::Arc;
++
++async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
++    async { true }.await
++}
++
++pub async fn public_future(rc: Rc<[u8]>) {
++    async { true }.await;
++}
++
++pub async fn public_send(arc: Arc<[u8]>) -> bool {
++    async { false }.await
++}
++
++async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
++    true
++}
++
++pub async fn public_future2(rc: Rc<[u8]>) {}
++
++pub async fn public_send2(arc: Arc<[u8]>) -> bool {
++    false
++}
++
++struct Dummy {
++    rc: Rc<[u8]>,
++}
++
++impl Dummy {
++    async fn private_future(&self) -> usize {
++        async { true }.await;
++        self.rc.len()
++    }
++
++    pub async fn public_future(&self) {
++        self.private_future().await;
++    }
++
++    pub fn public_send(&self) -> impl std::future::Future<Output = bool> {
++        async { false }
++    }
++}
++
++async fn generic_future<T>(t: T) -> T
++where
++    T: Send,
++{
++    let rt = &t;
++    async { true }.await;
++    t
++}
++
++async fn generic_future_send<T>(t: T)
++where
++    T: Send,
++{
++    async { true }.await;
++}
++
++async fn unclear_future<T>(t: T) {}
++
++fn main() {
++    let rc = Rc::new([1, 2, 3]);
++    private_future(rc.clone(), &Cell::new(42));
++    public_future(rc.clone());
++    let arc = Arc::new([4, 5, 6]);
++    public_send(arc);
++    generic_future(42);
++    generic_future_send(42);
++
++    let dummy = Dummy { rc };
++    dummy.public_future();
++    dummy.public_send();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3b4968ef0a63c528c8060966465c349c55dc6b94
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,125 @@@
++error: future cannot be sent between threads safely
++  --> $DIR/future_not_send.rs:8:62
++   |
++LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
++   |                                                              ^^^^ future returned by `private_future` is not `Send`
++   |
++   = note: `-D clippy::future-not-send` implied by `-D warnings`
++note: future is not `Send` as this value is used across an await
++  --> $DIR/future_not_send.rs:9:5
++   |
++LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
++   |                         -- has type `std::rc::Rc<[u8]>` which is not `Send`
++LL |     async { true }.await
++   |     ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rc` maybe used later
++LL | }
++   | - `rc` is later dropped here
++   = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
++note: future is not `Send` as this value is used across an await
++  --> $DIR/future_not_send.rs:9:5
++   |
++LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
++   |                                       ---- has type `&std::cell::Cell<usize>` which is not `Send`
++LL |     async { true }.await
++   |     ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `cell` maybe used later
++LL | }
++   | - `cell` is later dropped here
++   = note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync`
++
++error: future cannot be sent between threads safely
++  --> $DIR/future_not_send.rs:12:42
++   |
++LL | pub async fn public_future(rc: Rc<[u8]>) {
++   |                                          ^ future returned by `public_future` is not `Send`
++   |
++note: future is not `Send` as this value is used across an await
++  --> $DIR/future_not_send.rs:13:5
++   |
++LL | pub async fn public_future(rc: Rc<[u8]>) {
++   |                            -- has type `std::rc::Rc<[u8]>` which is not `Send`
++LL |     async { true }.await;
++   |     ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rc` maybe used later
++LL | }
++   | - `rc` is later dropped here
++   = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
++
++error: future cannot be sent between threads safely
++  --> $DIR/future_not_send.rs:20:63
++   |
++LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
++   |                                                               ^^^^
++   |
++   = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
++   = note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync`
++
++error: future cannot be sent between threads safely
++  --> $DIR/future_not_send.rs:24:43
++   |
++LL | pub async fn public_future2(rc: Rc<[u8]>) {}
++   |                                           ^
++   |
++   = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
++
++error: future cannot be sent between threads safely
++  --> $DIR/future_not_send.rs:35:39
++   |
++LL |     async fn private_future(&self) -> usize {
++   |                                       ^^^^^ future returned by `private_future` is not `Send`
++   |
++note: future is not `Send` as this value is used across an await
++  --> $DIR/future_not_send.rs:36:9
++   |
++LL |     async fn private_future(&self) -> usize {
++   |                             ----- has type `&Dummy` which is not `Send`
++LL |         async { true }.await;
++   |         ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `&self` maybe used later
++LL |         self.rc.len()
++LL |     }
++   |     - `&self` is later dropped here
++   = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
++
++error: future cannot be sent between threads safely
++  --> $DIR/future_not_send.rs:40:39
++   |
++LL |     pub async fn public_future(&self) {
++   |                                       ^ future returned by `public_future` is not `Send`
++   |
++note: future is not `Send` as this value is used across an await
++  --> $DIR/future_not_send.rs:41:9
++   |
++LL |     pub async fn public_future(&self) {
++   |                                ----- has type `&Dummy` which is not `Send`
++LL |         self.private_future().await;
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `&self` maybe used later
++LL |     }
++   |     - `&self` is later dropped here
++   = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
++
++error: future cannot be sent between threads safely
++  --> $DIR/future_not_send.rs:49:37
++   |
++LL | async fn generic_future<T>(t: T) -> T
++   |                                     ^ future returned by `generic_future` is not `Send`
++   |
++note: future is not `Send` as this value is used across an await
++  --> $DIR/future_not_send.rs:54:5
++   |
++LL |     let rt = &t;
++   |         -- has type `&T` which is not `Send`
++LL |     async { true }.await;
++   |     ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rt` maybe used later
++LL |     t
++LL | }
++   | - `rt` is later dropped here
++   = note: `T` doesn't implement `std::marker::Sync`
++
++error: future cannot be sent between threads safely
++  --> $DIR/future_not_send.rs:65:34
++   |
++LL | async fn unclear_future<T>(t: T) {}
++   |                                  ^
++   |
++   = note: `T` doesn't implement `std::marker::Send`
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c8b363f9c38e682eccc2f99ebd4a94ff98bf32e0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++// run-rustfix
++
++#![warn(clippy::get_last_with_len)]
++
++fn dont_use_last() {
++    let x = vec![2, 3, 5];
++    let _ = x.last(); // ~ERROR Use x.last()
++}
++
++fn indexing_two_from_end() {
++    let x = vec![2, 3, 5];
++    let _ = x.get(x.len() - 2);
++}
++
++fn index_into_last() {
++    let x = vec![2, 3, 5];
++    let _ = x[x.len() - 1];
++}
++
++fn use_last_with_different_vec_length() {
++    let x = vec![2, 3, 5];
++    let y = vec!['a', 'b', 'c'];
++    let _ = x.get(y.len() - 1);
++}
++
++fn main() {
++    dont_use_last();
++    indexing_two_from_end();
++    index_into_last();
++    use_last_with_different_vec_length();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bf9cb2d7e0ccd6e72d8f4c91402ab603d1faf8f1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++// run-rustfix
++
++#![warn(clippy::get_last_with_len)]
++
++fn dont_use_last() {
++    let x = vec![2, 3, 5];
++    let _ = x.get(x.len() - 1); // ~ERROR Use x.last()
++}
++
++fn indexing_two_from_end() {
++    let x = vec![2, 3, 5];
++    let _ = x.get(x.len() - 2);
++}
++
++fn index_into_last() {
++    let x = vec![2, 3, 5];
++    let _ = x[x.len() - 1];
++}
++
++fn use_last_with_different_vec_length() {
++    let x = vec![2, 3, 5];
++    let y = vec!['a', 'b', 'c'];
++    let _ = x.get(y.len() - 1);
++}
++
++fn main() {
++    dont_use_last();
++    indexing_two_from_end();
++    index_into_last();
++    use_last_with_different_vec_length();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..55baf87384a2499d8ddd5050564c7a03bfc8b534
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: accessing last element with `x.get(x.len() - 1)`
++  --> $DIR/get_last_with_len.rs:7:13
++   |
++LL |     let _ = x.get(x.len() - 1); // ~ERROR Use x.last()
++   |             ^^^^^^^^^^^^^^^^^^ help: try: `x.last()`
++   |
++   = note: `-D clippy::get-last-with-len` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..97e6b20f471fcf8bef96afdbeb493aa8f3deeee3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++// run-rustfix
++#![allow(unused_mut)]
++#![deny(clippy::get_unwrap)]
++
++use std::collections::BTreeMap;
++use std::collections::HashMap;
++use std::collections::VecDeque;
++use std::iter::FromIterator;
++
++struct GetFalsePositive {
++    arr: [u32; 3],
++}
++
++impl GetFalsePositive {
++    fn get(&self, pos: usize) -> Option<&u32> {
++        self.arr.get(pos)
++    }
++    fn get_mut(&mut self, pos: usize) -> Option<&mut u32> {
++        self.arr.get_mut(pos)
++    }
++}
++
++fn main() {
++    let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
++    let mut some_slice = &mut [0, 1, 2, 3];
++    let mut some_vec = vec![0, 1, 2, 3];
++    let mut some_vecdeque: VecDeque<_> = some_vec.iter().cloned().collect();
++    let mut some_hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(1, 'a'), (2, 'b')]);
++    let mut some_btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(1, 'a'), (2, 'b')]);
++    let mut false_positive = GetFalsePositive { arr: [0, 1, 2] };
++
++    {
++        // Test `get().unwrap()`
++        let _ = &boxed_slice[1];
++        let _ = &some_slice[0];
++        let _ = &some_vec[0];
++        let _ = &some_vecdeque[0];
++        let _ = &some_hashmap[&1];
++        let _ = &some_btreemap[&1];
++        let _ = false_positive.get(0).unwrap();
++        // Test with deref
++        let _: u8 = boxed_slice[1];
++    }
++
++    {
++        // Test `get_mut().unwrap()`
++        boxed_slice[0] = 1;
++        some_slice[0] = 1;
++        some_vec[0] = 1;
++        some_vecdeque[0] = 1;
++        // Check false positives
++        *some_hashmap.get_mut(&1).unwrap() = 'b';
++        *some_btreemap.get_mut(&1).unwrap() = 'b';
++        *false_positive.get_mut(0).unwrap() = 1;
++    }
++
++    {
++        // Test `get().unwrap().foo()` and `get_mut().unwrap().bar()`
++        let _ = some_vec[0..1].to_vec();
++        let _ = some_vec[0..1].to_vec();
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1c9a71c09699afda778d8dba35896571a672d997
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++// run-rustfix
++#![allow(unused_mut)]
++#![deny(clippy::get_unwrap)]
++
++use std::collections::BTreeMap;
++use std::collections::HashMap;
++use std::collections::VecDeque;
++use std::iter::FromIterator;
++
++struct GetFalsePositive {
++    arr: [u32; 3],
++}
++
++impl GetFalsePositive {
++    fn get(&self, pos: usize) -> Option<&u32> {
++        self.arr.get(pos)
++    }
++    fn get_mut(&mut self, pos: usize) -> Option<&mut u32> {
++        self.arr.get_mut(pos)
++    }
++}
++
++fn main() {
++    let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
++    let mut some_slice = &mut [0, 1, 2, 3];
++    let mut some_vec = vec![0, 1, 2, 3];
++    let mut some_vecdeque: VecDeque<_> = some_vec.iter().cloned().collect();
++    let mut some_hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(1, 'a'), (2, 'b')]);
++    let mut some_btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(1, 'a'), (2, 'b')]);
++    let mut false_positive = GetFalsePositive { arr: [0, 1, 2] };
++
++    {
++        // Test `get().unwrap()`
++        let _ = boxed_slice.get(1).unwrap();
++        let _ = some_slice.get(0).unwrap();
++        let _ = some_vec.get(0).unwrap();
++        let _ = some_vecdeque.get(0).unwrap();
++        let _ = some_hashmap.get(&1).unwrap();
++        let _ = some_btreemap.get(&1).unwrap();
++        let _ = false_positive.get(0).unwrap();
++        // Test with deref
++        let _: u8 = *boxed_slice.get(1).unwrap();
++    }
++
++    {
++        // Test `get_mut().unwrap()`
++        *boxed_slice.get_mut(0).unwrap() = 1;
++        *some_slice.get_mut(0).unwrap() = 1;
++        *some_vec.get_mut(0).unwrap() = 1;
++        *some_vecdeque.get_mut(0).unwrap() = 1;
++        // Check false positives
++        *some_hashmap.get_mut(&1).unwrap() = 'b';
++        *some_btreemap.get_mut(&1).unwrap() = 'b';
++        *false_positive.get_mut(0).unwrap() = 1;
++    }
++
++    {
++        // Test `get().unwrap().foo()` and `get_mut().unwrap().bar()`
++        let _ = some_vec.get(0..1).unwrap().to_vec();
++        let _ = some_vec.get_mut(0..1).unwrap().to_vec();
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..76a098df82aa199adb0186eb098b0e540fc6ba91
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,86 @@@
++error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
++  --> $DIR/get_unwrap.rs:34:17
++   |
++LL |         let _ = boxed_slice.get(1).unwrap();
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]`
++   |
++note: the lint level is defined here
++  --> $DIR/get_unwrap.rs:3:9
++   |
++LL | #![deny(clippy::get_unwrap)]
++   |         ^^^^^^^^^^^^^^^^^^
++
++error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
++  --> $DIR/get_unwrap.rs:35:17
++   |
++LL |         let _ = some_slice.get(0).unwrap();
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]`
++
++error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
++  --> $DIR/get_unwrap.rs:36:17
++   |
++LL |         let _ = some_vec.get(0).unwrap();
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]`
++
++error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
++  --> $DIR/get_unwrap.rs:37:17
++   |
++LL |         let _ = some_vecdeque.get(0).unwrap();
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]`
++
++error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise
++  --> $DIR/get_unwrap.rs:38:17
++   |
++LL |         let _ = some_hashmap.get(&1).unwrap();
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]`
++
++error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise
++  --> $DIR/get_unwrap.rs:39:17
++   |
++LL |         let _ = some_btreemap.get(&1).unwrap();
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]`
++
++error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
++  --> $DIR/get_unwrap.rs:42:21
++   |
++LL |         let _: u8 = *boxed_slice.get(1).unwrap();
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]`
++
++error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
++  --> $DIR/get_unwrap.rs:47:9
++   |
++LL |         *boxed_slice.get_mut(0).unwrap() = 1;
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]`
++
++error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
++  --> $DIR/get_unwrap.rs:48:9
++   |
++LL |         *some_slice.get_mut(0).unwrap() = 1;
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]`
++
++error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
++  --> $DIR/get_unwrap.rs:49:9
++   |
++LL |         *some_vec.get_mut(0).unwrap() = 1;
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]`
++
++error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
++  --> $DIR/get_unwrap.rs:50:9
++   |
++LL |         *some_vecdeque.get_mut(0).unwrap() = 1;
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]`
++
++error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
++  --> $DIR/get_unwrap.rs:59:17
++   |
++LL |         let _ = some_vec.get(0..1).unwrap().to_vec();
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
++
++error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
++  --> $DIR/get_unwrap.rs:60:17
++   |
++LL |         let _ = some_vec.get_mut(0..1).unwrap().to_vec();
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
++
++error: aborting due to 13 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dd3fc56e98bcf2b668a2c8fe372cedc7ef8f36b2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++// run-rustfix
++
++#![deny(clippy::identity_conversion)]
++
++fn test_generic<T: Copy>(val: T) -> T {
++    let _ = val;
++    val
++}
++
++fn test_generic2<T: Copy + Into<i32> + Into<U>, U: From<T>>(val: T) {
++    // ok
++    let _: i32 = val.into();
++    let _: U = val.into();
++    let _ = U::from(val);
++}
++
++fn test_questionmark() -> Result<(), ()> {
++    {
++        let _: i32 = 0i32;
++        Ok(Ok(()))
++    }??;
++    Ok(())
++}
++
++fn test_issue_3913() -> Result<(), std::io::Error> {
++    use std::fs;
++    use std::path::Path;
++
++    let path = Path::new(".");
++    for _ in fs::read_dir(path)? {}
++
++    Ok(())
++}
++
++fn main() {
++    test_generic(10i32);
++    test_generic2::<i32, i32>(10i32);
++    test_questionmark().unwrap();
++    test_issue_3913().unwrap();
++
++    let _: String = "foo".into();
++    let _: String = From::from("foo");
++    let _ = String::from("foo");
++    #[allow(clippy::identity_conversion)]
++    {
++        let _: String = "foo".into();
++        let _ = String::from("foo");
++        let _ = "".lines().into_iter();
++    }
++
++    let _: String = "foo".to_string();
++    let _: String = "foo".to_string();
++    let _ = "foo".to_string();
++    let _ = format!("A: {:04}", 123);
++    let _ = "".lines();
++    let _ = vec![1, 2, 3].into_iter();
++    let _: String = format!("Hello {}", "world");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..875ed7db373b6000d0ca4c14af50fcbe480aa084
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++// run-rustfix
++
++#![deny(clippy::identity_conversion)]
++
++fn test_generic<T: Copy>(val: T) -> T {
++    let _ = T::from(val);
++    val.into()
++}
++
++fn test_generic2<T: Copy + Into<i32> + Into<U>, U: From<T>>(val: T) {
++    // ok
++    let _: i32 = val.into();
++    let _: U = val.into();
++    let _ = U::from(val);
++}
++
++fn test_questionmark() -> Result<(), ()> {
++    {
++        let _: i32 = 0i32.into();
++        Ok(Ok(()))
++    }??;
++    Ok(())
++}
++
++fn test_issue_3913() -> Result<(), std::io::Error> {
++    use std::fs;
++    use std::path::Path;
++
++    let path = Path::new(".");
++    for _ in fs::read_dir(path)? {}
++
++    Ok(())
++}
++
++fn main() {
++    test_generic(10i32);
++    test_generic2::<i32, i32>(10i32);
++    test_questionmark().unwrap();
++    test_issue_3913().unwrap();
++
++    let _: String = "foo".into();
++    let _: String = From::from("foo");
++    let _ = String::from("foo");
++    #[allow(clippy::identity_conversion)]
++    {
++        let _: String = "foo".into();
++        let _ = String::from("foo");
++        let _ = "".lines().into_iter();
++    }
++
++    let _: String = "foo".to_string().into();
++    let _: String = From::from("foo".to_string());
++    let _ = String::from("foo".to_string());
++    let _ = String::from(format!("A: {:04}", 123));
++    let _ = "".lines().into_iter();
++    let _ = vec![1, 2, 3].into_iter().into_iter();
++    let _: String = format!("Hello {}", "world").into();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..57626b23795cf5ce4a0c6743998acddbc0e69491
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,68 @@@
++error: identical conversion
++  --> $DIR/identity_conversion.rs:6:13
++   |
++LL |     let _ = T::from(val);
++   |             ^^^^^^^^^^^^ help: consider removing `T::from()`: `val`
++   |
++note: the lint level is defined here
++  --> $DIR/identity_conversion.rs:3:9
++   |
++LL | #![deny(clippy::identity_conversion)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: identical conversion
++  --> $DIR/identity_conversion.rs:7:5
++   |
++LL |     val.into()
++   |     ^^^^^^^^^^ help: consider removing `.into()`: `val`
++
++error: identical conversion
++  --> $DIR/identity_conversion.rs:19:22
++   |
++LL |         let _: i32 = 0i32.into();
++   |                      ^^^^^^^^^^^ help: consider removing `.into()`: `0i32`
++
++error: identical conversion
++  --> $DIR/identity_conversion.rs:51:21
++   |
++LL |     let _: String = "foo".to_string().into();
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()`
++
++error: identical conversion
++  --> $DIR/identity_conversion.rs:52:21
++   |
++LL |     let _: String = From::from("foo".to_string());
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()`
++
++error: identical conversion
++  --> $DIR/identity_conversion.rs:53:13
++   |
++LL |     let _ = String::from("foo".to_string());
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()`
++
++error: identical conversion
++  --> $DIR/identity_conversion.rs:54:13
++   |
++LL |     let _ = String::from(format!("A: {:04}", 123));
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)`
++
++error: identical conversion
++  --> $DIR/identity_conversion.rs:55:13
++   |
++LL |     let _ = "".lines().into_iter();
++   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()`
++
++error: identical conversion
++  --> $DIR/identity_conversion.rs:56:13
++   |
++LL |     let _ = vec![1, 2, 3].into_iter().into_iter();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()`
++
++error: identical conversion
++  --> $DIR/identity_conversion.rs:57:21
++   |
++LL |     let _: String = format!("Hello {}", "world").into();
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")`
++
++error: aborting due to 10 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ae2815d345a94dd5233f3177ceb707bd5e47497a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++const ONE: i64 = 1;
++const NEG_ONE: i64 = -1;
++const ZERO: i64 = 0;
++
++#[allow(
++    clippy::eq_op,
++    clippy::no_effect,
++    clippy::unnecessary_operation,
++    clippy::double_parens
++)]
++#[warn(clippy::identity_op)]
++#[rustfmt::skip]
++fn main() {
++    let x = 0;
++
++    x + 0;
++    x + (1 - 1);
++    x + 1;
++    0 + x;
++    1 + x;
++    x - ZERO; //no error, as we skip lookups (for now)
++    x | (0);
++    ((ZERO)) | x; //no error, as we skip lookups (for now)
++
++    x * 1;
++    1 * x;
++    x / ONE; //no error, as we skip lookups (for now)
++
++    x / 2; //no false positive
++
++    x & NEG_ONE; //no error, as we skip lookups (for now)
++    -1 & x;
++
++    let u: u8 = 0;
++    u & 255;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4742877706acd6d360768c41dfbe4f0b8011d8c7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++error: the operation is ineffective. Consider reducing it to `x`
++  --> $DIR/identity_op.rs:16:5
++   |
++LL |     x + 0;
++   |     ^^^^^
++   |
++   = note: `-D clippy::identity-op` implied by `-D warnings`
++
++error: the operation is ineffective. Consider reducing it to `x`
++  --> $DIR/identity_op.rs:17:5
++   |
++LL |     x + (1 - 1);
++   |     ^^^^^^^^^^^
++
++error: the operation is ineffective. Consider reducing it to `x`
++  --> $DIR/identity_op.rs:19:5
++   |
++LL |     0 + x;
++   |     ^^^^^
++
++error: the operation is ineffective. Consider reducing it to `x`
++  --> $DIR/identity_op.rs:22:5
++   |
++LL |     x | (0);
++   |     ^^^^^^^
++
++error: the operation is ineffective. Consider reducing it to `x`
++  --> $DIR/identity_op.rs:25:5
++   |
++LL |     x * 1;
++   |     ^^^^^
++
++error: the operation is ineffective. Consider reducing it to `x`
++  --> $DIR/identity_op.rs:26:5
++   |
++LL |     1 * x;
++   |     ^^^^^
++
++error: the operation is ineffective. Consider reducing it to `x`
++  --> $DIR/identity_op.rs:32:5
++   |
++LL |     -1 & x;
++   |     ^^^^^^
++
++error: the operation is ineffective. Consider reducing it to `u`
++  --> $DIR/identity_op.rs:35:5
++   |
++LL |     u & 255;
++   |     ^^^^^^^
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..58feae422a3c121f3d89cc14278147037191a99a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++#![warn(clippy::if_let_mutex)]
++
++use std::ops::Deref;
++use std::sync::Mutex;
++
++fn do_stuff<T>(_: T) {}
++
++fn if_let() {
++    let m = Mutex::new(1_u8);
++    if let Err(locked) = m.lock() {
++        do_stuff(locked);
++    } else {
++        let lock = m.lock().unwrap();
++        do_stuff(lock);
++    };
++}
++
++// This is the most common case as the above case is pretty
++// contrived.
++fn if_let_option() {
++    let m = Mutex::new(Some(0_u8));
++    if let Some(locked) = m.lock().unwrap().deref() {
++        do_stuff(locked);
++    } else {
++        let lock = m.lock().unwrap();
++        do_stuff(lock);
++    };
++}
++
++// When mutexs are different don't warn
++fn if_let_different_mutex() {
++    let m = Mutex::new(Some(0_u8));
++    let other = Mutex::new(None::<u8>);
++    if let Some(locked) = m.lock().unwrap().deref() {
++        do_stuff(locked);
++    } else {
++        let lock = other.lock().unwrap();
++        do_stuff(lock);
++    };
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e9c4d9163328f3066e9c67f244a8ce233c35555a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
++  --> $DIR/if_let_mutex.rs:10:5
++   |
++LL | /     if let Err(locked) = m.lock() {
++LL | |         do_stuff(locked);
++LL | |     } else {
++LL | |         let lock = m.lock().unwrap();
++LL | |         do_stuff(lock);
++LL | |     };
++   | |_____^
++   |
++   = note: `-D clippy::if-let-mutex` implied by `-D warnings`
++   = help: move the lock call outside of the `if let ...` expression
++
++error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
++  --> $DIR/if_let_mutex.rs:22:5
++   |
++LL | /     if let Some(locked) = m.lock().unwrap().deref() {
++LL | |         do_stuff(locked);
++LL | |     } else {
++LL | |         let lock = m.lock().unwrap();
++LL | |         do_stuff(lock);
++LL | |     };
++   | |_____^
++   |
++   = help: move the lock call outside of the `if let ...` expression
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..80505fd997f42628919c4ceed0959b31411c2ebc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++// run-rustfix
++
++#![warn(clippy::if_let_some_result)]
++
++fn str_to_int(x: &str) -> i32 {
++    if let Ok(y) = x.parse() {
++        y
++    } else {
++        0
++    }
++}
++
++fn str_to_int_ok(x: &str) -> i32 {
++    if let Ok(y) = x.parse() {
++        y
++    } else {
++        0
++    }
++}
++
++#[rustfmt::skip]
++fn strange_some_no_else(x: &str) -> i32 {
++    {
++        if let Ok(y) = x   .   parse()       {
++            return y;
++        };
++        0
++    }
++}
++
++fn main() {
++    let _ = str_to_int("1");
++    let _ = str_to_int_ok("2");
++    let _ = strange_some_no_else("3");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ecac13574456ca8cae735b742108eb9f1671d087
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++// run-rustfix
++
++#![warn(clippy::if_let_some_result)]
++
++fn str_to_int(x: &str) -> i32 {
++    if let Some(y) = x.parse().ok() {
++        y
++    } else {
++        0
++    }
++}
++
++fn str_to_int_ok(x: &str) -> i32 {
++    if let Ok(y) = x.parse() {
++        y
++    } else {
++        0
++    }
++}
++
++#[rustfmt::skip]
++fn strange_some_no_else(x: &str) -> i32 {
++    {
++        if let Some(y) = x   .   parse()   .   ok   ()    {
++            return y;
++        };
++        0
++    }
++}
++
++fn main() {
++    let _ = str_to_int("1");
++    let _ = str_to_int_ok("2");
++    let _ = strange_some_no_else("3");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..334ccb0101678c6c05cbb0c7418c75c8a17489b2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++error: Matching on `Some` with `ok()` is redundant
++  --> $DIR/if_let_some_result.rs:6:5
++   |
++LL |     if let Some(y) = x.parse().ok() {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::if-let-some-result` implied by `-D warnings`
++help: Consider matching on `Ok(y)` and removing the call to `ok` instead
++   |
++LL |     if let Ok(y) = x.parse() {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: Matching on `Some` with `ok()` is redundant
++  --> $DIR/if_let_some_result.rs:24:9
++   |
++LL |         if let Some(y) = x   .   parse()   .   ok   ()    {
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: Consider matching on `Ok(y)` and removing the call to `ok` instead
++   |
++LL |         if let Ok(y) = x   .   parse()       {
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dc3fb1ceac9a44ba1dd69d063040b49f9feff534
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++#![warn(clippy::all)]
++#![warn(clippy::if_not_else)]
++
++fn bla() -> bool {
++    unimplemented!()
++}
++
++fn main() {
++    if !bla() {
++        println!("Bugs");
++    } else {
++        println!("Bunny");
++    }
++    if 4 != 5 {
++        println!("Bugs");
++    } else {
++        println!("Bunny");
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..78bc4d4bd20a30359e686ac4e891dec16c2108bf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++error: Unnecessary boolean `not` operation
++  --> $DIR/if_not_else.rs:9:5
++   |
++LL | /     if !bla() {
++LL | |         println!("Bugs");
++LL | |     } else {
++LL | |         println!("Bunny");
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::if-not-else` implied by `-D warnings`
++   = help: remove the `!` and swap the blocks of the `if`/`else`
++
++error: Unnecessary `!=` operation
++  --> $DIR/if_not_else.rs:14:5
++   |
++LL | /     if 4 != 5 {
++LL | |         println!("Bugs");
++LL | |     } else {
++LL | |         println!("Bunny");
++LL | |     }
++   | |_____^
++   |
++   = help: change to `==` and swap the blocks of the `if`/`else`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6bbf79edfcf700c75eb9444bb79ea12ef141af17
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,145 @@@
++#![warn(clippy::if_same_then_else)]
++#![allow(
++    clippy::blacklisted_name,
++    clippy::eq_op,
++    clippy::never_loop,
++    clippy::no_effect,
++    clippy::unused_unit,
++    clippy::zero_divided_by_zero
++)]
++
++struct Foo {
++    bar: u8,
++}
++
++fn foo() -> bool {
++    unimplemented!()
++}
++
++fn if_same_then_else() {
++    if true {
++        Foo { bar: 42 };
++        0..10;
++        ..;
++        0..;
++        ..10;
++        0..=10;
++        foo();
++    } else {
++        //~ ERROR same body as `if` block
++        Foo { bar: 42 };
++        0..10;
++        ..;
++        0..;
++        ..10;
++        0..=10;
++        foo();
++    }
++
++    if true {
++        Foo { bar: 42 };
++    } else {
++        Foo { bar: 43 };
++    }
++
++    if true {
++        ();
++    } else {
++        ()
++    }
++
++    if true {
++        0..10;
++    } else {
++        0..=10;
++    }
++
++    if true {
++        foo();
++        foo();
++    } else {
++        foo();
++    }
++
++    let _ = if true {
++        0.0
++    } else {
++        //~ ERROR same body as `if` block
++        0.0
++    };
++
++    let _ = if true {
++        -0.0
++    } else {
++        //~ ERROR same body as `if` block
++        -0.0
++    };
++
++    let _ = if true { 0.0 } else { -0.0 };
++
++    // Different NaNs
++    let _ = if true { 0.0 / 0.0 } else { f32::NAN };
++
++    if true {
++        foo();
++    }
++
++    let _ = if true {
++        42
++    } else {
++        //~ ERROR same body as `if` block
++        42
++    };
++
++    if true {
++        let bar = if true { 42 } else { 43 };
++
++        while foo() {
++            break;
++        }
++        bar + 1;
++    } else {
++        //~ ERROR same body as `if` block
++        let bar = if true { 42 } else { 43 };
++
++        while foo() {
++            break;
++        }
++        bar + 1;
++    }
++
++    if true {
++        let _ = match 42 {
++            42 => 1,
++            a if a > 0 => 2,
++            10..=15 => 3,
++            _ => 4,
++        };
++    } else if false {
++        foo();
++    } else if foo() {
++        let _ = match 42 {
++            42 => 1,
++            a if a > 0 => 2,
++            10..=15 => 3,
++            _ => 4,
++        };
++    }
++}
++
++// Issue #2423. This was causing an ICE.
++fn func() {
++    if true {
++        f(&[0; 62]);
++        f(&[0; 4]);
++        f(&[0; 3]);
++    } else {
++        f(&[0; 62]);
++        f(&[0; 6]);
++        f(&[0; 6]);
++    }
++}
++
++fn f(val: &[u8]) {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d9fdf06fa8b732b669f5d3c931b062f4196fcf4b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,112 @@@
++error: this `if` has identical blocks
++  --> $DIR/if_same_then_else.rs:28:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         //~ ERROR same body as `if` block
++LL | |         Foo { bar: 42 };
++LL | |         0..10;
++...  |
++LL | |         foo();
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::if-same-then-else` implied by `-D warnings`
++note: same as this
++  --> $DIR/if_same_then_else.rs:20:13
++   |
++LL |       if true {
++   |  _____________^
++LL | |         Foo { bar: 42 };
++LL | |         0..10;
++LL | |         ..;
++...  |
++LL | |         foo();
++LL | |     } else {
++   | |_____^
++
++error: this `if` has identical blocks
++  --> $DIR/if_same_then_else.rs:66:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         //~ ERROR same body as `if` block
++LL | |         0.0
++LL | |     };
++   | |_____^
++   |
++note: same as this
++  --> $DIR/if_same_then_else.rs:64:21
++   |
++LL |       let _ = if true {
++   |  _____________________^
++LL | |         0.0
++LL | |     } else {
++   | |_____^
++
++error: this `if` has identical blocks
++  --> $DIR/if_same_then_else.rs:73:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         //~ ERROR same body as `if` block
++LL | |         -0.0
++LL | |     };
++   | |_____^
++   |
++note: same as this
++  --> $DIR/if_same_then_else.rs:71:21
++   |
++LL |       let _ = if true {
++   |  _____________________^
++LL | |         -0.0
++LL | |     } else {
++   | |_____^
++
++error: this `if` has identical blocks
++  --> $DIR/if_same_then_else.rs:89:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         //~ ERROR same body as `if` block
++LL | |         42
++LL | |     };
++   | |_____^
++   |
++note: same as this
++  --> $DIR/if_same_then_else.rs:87:21
++   |
++LL |       let _ = if true {
++   |  _____________________^
++LL | |         42
++LL | |     } else {
++   | |_____^
++
++error: this `if` has identical blocks
++  --> $DIR/if_same_then_else.rs:101:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         //~ ERROR same body as `if` block
++LL | |         let bar = if true { 42 } else { 43 };
++LL | |
++...  |
++LL | |         bar + 1;
++LL | |     }
++   | |_____^
++   |
++note: same as this
++  --> $DIR/if_same_then_else.rs:94:13
++   |
++LL |       if true {
++   |  _____________^
++LL | |         let bar = if true { 42 } else { 43 };
++LL | |
++LL | |         while foo() {
++...  |
++LL | |         bar + 1;
++LL | |     } else {
++   | |_____^
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3cc21809264f55b94c7ec5ef2ef5e34c20097205
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,139 @@@
++#![warn(clippy::if_same_then_else)]
++#![allow(
++    clippy::blacklisted_name,
++    clippy::collapsible_if,
++    clippy::ifs_same_cond,
++    clippy::needless_return
++)]
++
++fn if_same_then_else2() -> Result<&'static str, ()> {
++    if true {
++        for _ in &[42] {
++            let foo: &Option<_> = &Some::<u8>(42);
++            if true {
++                break;
++            } else {
++                continue;
++            }
++        }
++    } else {
++        //~ ERROR same body as `if` block
++        for _ in &[42] {
++            let foo: &Option<_> = &Some::<u8>(42);
++            if true {
++                break;
++            } else {
++                continue;
++            }
++        }
++    }
++
++    if true {
++        if let Some(a) = Some(42) {}
++    } else {
++        //~ ERROR same body as `if` block
++        if let Some(a) = Some(42) {}
++    }
++
++    if true {
++        if let (1, .., 3) = (1, 2, 3) {}
++    } else {
++        //~ ERROR same body as `if` block
++        if let (1, .., 3) = (1, 2, 3) {}
++    }
++
++    if true {
++        if let (1, .., 3) = (1, 2, 3) {}
++    } else {
++        if let (.., 3) = (1, 2, 3) {}
++    }
++
++    if true {
++        if let (1, .., 3) = (1, 2, 3) {}
++    } else {
++        if let (.., 4) = (1, 2, 3) {}
++    }
++
++    if true {
++        if let (1, .., 3) = (1, 2, 3) {}
++    } else {
++        if let (.., 1, 3) = (1, 2, 3) {}
++    }
++
++    if true {
++        if let Some(42) = None {}
++    } else {
++        if let Option::Some(42) = None {}
++    }
++
++    if true {
++        if let Some(42) = None::<u8> {}
++    } else {
++        if let Some(42) = None {}
++    }
++
++    if true {
++        if let Some(42) = None::<u8> {}
++    } else {
++        if let Some(42) = None::<u32> {}
++    }
++
++    if true {
++        if let Some(a) = Some(42) {}
++    } else {
++        if let Some(a) = Some(43) {}
++    }
++
++    // Same NaNs
++    let _ = if true {
++        f32::NAN
++    } else {
++        //~ ERROR same body as `if` block
++        f32::NAN
++    };
++
++    if true {
++        Ok("foo")?;
++    } else {
++        //~ ERROR same body as `if` block
++        Ok("foo")?;
++    }
++
++    if true {
++        let foo = "";
++        return Ok(&foo[0..]);
++    } else if false {
++        let foo = "bar";
++        return Ok(&foo[0..]);
++    } else {
++        let foo = "";
++        return Ok(&foo[0..]);
++    }
++
++    if true {
++        let foo = "";
++        return Ok(&foo[0..]);
++    } else if false {
++        let foo = "bar";
++        return Ok(&foo[0..]);
++    } else if true {
++        let foo = "";
++        return Ok(&foo[0..]);
++    } else {
++        let foo = "";
++        return Ok(&foo[0..]);
++    }
++
++    // False positive `if_same_then_else`: `let (x, y)` vs. `let (y, x)`; see issue #3559.
++    if true {
++        let foo = "";
++        let (x, y) = (1, 2);
++        return Ok(&foo[x..y]);
++    } else {
++        let foo = "";
++        let (y, x) = (1, 2);
++        return Ok(&foo[x..y]);
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f5d087fe128386d6006706af594e780759110ee0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,125 @@@
++error: this `if` has identical blocks
++  --> $DIR/if_same_then_else2.rs:19:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         //~ ERROR same body as `if` block
++LL | |         for _ in &[42] {
++LL | |             let foo: &Option<_> = &Some::<u8>(42);
++...  |
++LL | |         }
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::if-same-then-else` implied by `-D warnings`
++note: same as this
++  --> $DIR/if_same_then_else2.rs:10:13
++   |
++LL |       if true {
++   |  _____________^
++LL | |         for _ in &[42] {
++LL | |             let foo: &Option<_> = &Some::<u8>(42);
++LL | |             if true {
++...  |
++LL | |         }
++LL | |     } else {
++   | |_____^
++
++error: this `if` has identical blocks
++  --> $DIR/if_same_then_else2.rs:33:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         //~ ERROR same body as `if` block
++LL | |         if let Some(a) = Some(42) {}
++LL | |     }
++   | |_____^
++   |
++note: same as this
++  --> $DIR/if_same_then_else2.rs:31:13
++   |
++LL |       if true {
++   |  _____________^
++LL | |         if let Some(a) = Some(42) {}
++LL | |     } else {
++   | |_____^
++
++error: this `if` has identical blocks
++  --> $DIR/if_same_then_else2.rs:40:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         //~ ERROR same body as `if` block
++LL | |         if let (1, .., 3) = (1, 2, 3) {}
++LL | |     }
++   | |_____^
++   |
++note: same as this
++  --> $DIR/if_same_then_else2.rs:38:13
++   |
++LL |       if true {
++   |  _____________^
++LL | |         if let (1, .., 3) = (1, 2, 3) {}
++LL | |     } else {
++   | |_____^
++
++error: this `if` has identical blocks
++  --> $DIR/if_same_then_else2.rs:90:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         //~ ERROR same body as `if` block
++LL | |         f32::NAN
++LL | |     };
++   | |_____^
++   |
++note: same as this
++  --> $DIR/if_same_then_else2.rs:88:21
++   |
++LL |       let _ = if true {
++   |  _____________________^
++LL | |         f32::NAN
++LL | |     } else {
++   | |_____^
++
++error: this `if` has identical blocks
++  --> $DIR/if_same_then_else2.rs:97:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         //~ ERROR same body as `if` block
++LL | |         Ok("foo")?;
++LL | |     }
++   | |_____^
++   |
++note: same as this
++  --> $DIR/if_same_then_else2.rs:95:13
++   |
++LL |       if true {
++   |  _____________^
++LL | |         Ok("foo")?;
++LL | |     } else {
++   | |_____^
++
++error: this `if` has identical blocks
++  --> $DIR/if_same_then_else2.rs:122:12
++   |
++LL |       } else {
++   |  ____________^
++LL | |         let foo = "";
++LL | |         return Ok(&foo[0..]);
++LL | |     }
++   | |_____^
++   |
++note: same as this
++  --> $DIR/if_same_then_else2.rs:119:20
++   |
++LL |       } else if true {
++   |  ____________________^
++LL | |         let foo = "";
++LL | |         return Ok(&foo[0..]);
++LL | |     } else {
++   | |_____^
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..80e9839ff40bd30df3bfdfaec456c1d44aa8154e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++#![warn(clippy::ifs_same_cond)]
++#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks
++
++fn ifs_same_cond() {
++    let a = 0;
++    let b = false;
++
++    if b {
++    } else if b {
++        //~ ERROR ifs same condition
++    }
++
++    if a == 1 {
++    } else if a == 1 {
++        //~ ERROR ifs same condition
++    }
++
++    if 2 * a == 1 {
++    } else if 2 * a == 2 {
++    } else if 2 * a == 1 {
++        //~ ERROR ifs same condition
++    } else if a == 1 {
++    }
++
++    // See #659
++    if cfg!(feature = "feature1-659") {
++        1
++    } else if cfg!(feature = "feature2-659") {
++        2
++    } else {
++        3
++    };
++
++    let mut v = vec![1];
++    if v.pop() == None {
++        // ok, functions
++    } else if v.pop() == None {
++    }
++
++    if v.len() == 42 {
++        // ok, functions
++    } else if v.len() == 42 {
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0c8f49b8687f0a2c639e0c4090de5aa7006269b0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++error: this `if` has the same condition as a previous `if`
++  --> $DIR/ifs_same_cond.rs:9:15
++   |
++LL |     } else if b {
++   |               ^
++   |
++   = note: `-D clippy::ifs-same-cond` implied by `-D warnings`
++note: same as this
++  --> $DIR/ifs_same_cond.rs:8:8
++   |
++LL |     if b {
++   |        ^
++
++error: this `if` has the same condition as a previous `if`
++  --> $DIR/ifs_same_cond.rs:14:15
++   |
++LL |     } else if a == 1 {
++   |               ^^^^^^
++   |
++note: same as this
++  --> $DIR/ifs_same_cond.rs:13:8
++   |
++LL |     if a == 1 {
++   |        ^^^^^^
++
++error: this `if` has the same condition as a previous `if`
++  --> $DIR/ifs_same_cond.rs:20:15
++   |
++LL |     } else if 2 * a == 1 {
++   |               ^^^^^^^^^^
++   |
++note: same as this
++  --> $DIR/ifs_same_cond.rs:18:8
++   |
++LL |     if 2 * a == 1 {
++   |        ^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1c46e3a5337828d3ca4a619cce8081d107f986ef
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++#![allow(dead_code)]
++#![warn(clippy::multiple_inherent_impl)]
++
++struct MyStruct;
++
++impl MyStruct {
++    fn first() {}
++}
++
++impl MyStruct {
++    fn second() {}
++}
++
++impl<'a> MyStruct {
++    fn lifetimed() {}
++}
++
++mod submod {
++    struct MyStruct;
++    impl MyStruct {
++        fn other() {}
++    }
++
++    impl super::MyStruct {
++        fn third() {}
++    }
++}
++
++use std::fmt;
++impl fmt::Debug for MyStruct {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        write!(f, "MyStruct {{ }}")
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..585d32845d290af4f8e66a3b1fd9a41b13332f5a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++error: Multiple implementations of this structure
++  --> $DIR/impl.rs:10:1
++   |
++LL | / impl MyStruct {
++LL | |     fn second() {}
++LL | | }
++   | |_^
++   |
++   = note: `-D clippy::multiple-inherent-impl` implied by `-D warnings`
++note: First implementation here
++  --> $DIR/impl.rs:6:1
++   |
++LL | / impl MyStruct {
++LL | |     fn first() {}
++LL | | }
++   | |_^
++
++error: Multiple implementations of this structure
++  --> $DIR/impl.rs:24:5
++   |
++LL | /     impl super::MyStruct {
++LL | |         fn third() {}
++LL | |     }
++   | |_____^
++   |
++note: First implementation here
++  --> $DIR/impl.rs:6:1
++   |
++LL | / impl MyStruct {
++LL | |     fn first() {}
++LL | | }
++   | |_^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fdcc9a33f55fe93e2d5989f1757b2233c43c3d78
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,99 @@@
++// aux-build:implicit_hasher_macros.rs
++#![deny(clippy::implicit_hasher)]
++#![allow(unused)]
++
++#[macro_use]
++extern crate implicit_hasher_macros;
++
++use std::cmp::Eq;
++use std::collections::{HashMap, HashSet};
++use std::hash::{BuildHasher, Hash};
++
++pub trait Foo<T>: Sized {
++    fn make() -> (Self, Self);
++}
++
++impl<K: Hash + Eq, V> Foo<i8> for HashMap<K, V> {
++    fn make() -> (Self, Self) {
++        // OK, don't suggest to modify these
++        let _: HashMap<i32, i32> = HashMap::new();
++        let _: HashSet<i32> = HashSet::new();
++
++        (HashMap::new(), HashMap::with_capacity(10))
++    }
++}
++impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) {
++    fn make() -> (Self, Self) {
++        ((HashMap::new(),), (HashMap::with_capacity(10),))
++    }
++}
++impl Foo<i16> for HashMap<String, String> {
++    fn make() -> (Self, Self) {
++        (HashMap::new(), HashMap::with_capacity(10))
++    }
++}
++
++impl<K: Hash + Eq, V, S: BuildHasher + Default> Foo<i32> for HashMap<K, V, S> {
++    fn make() -> (Self, Self) {
++        (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default()))
++    }
++}
++impl<S: BuildHasher + Default> Foo<i64> for HashMap<String, String, S> {
++    fn make() -> (Self, Self) {
++        (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default()))
++    }
++}
++
++impl<T: Hash + Eq> Foo<i8> for HashSet<T> {
++    fn make() -> (Self, Self) {
++        (HashSet::new(), HashSet::with_capacity(10))
++    }
++}
++impl Foo<i16> for HashSet<String> {
++    fn make() -> (Self, Self) {
++        (HashSet::new(), HashSet::with_capacity(10))
++    }
++}
++
++impl<T: Hash + Eq, S: BuildHasher + Default> Foo<i32> for HashSet<T, S> {
++    fn make() -> (Self, Self) {
++        (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default()))
++    }
++}
++impl<S: BuildHasher + Default> Foo<i64> for HashSet<String, S> {
++    fn make() -> (Self, Self) {
++        (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default()))
++    }
++}
++
++pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
++
++macro_rules! gen {
++    (impl) => {
++        impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> {
++            fn make() -> (Self, Self) {
++                (HashMap::new(), HashMap::with_capacity(10))
++            }
++        }
++    };
++
++    (fn $name:ident) => {
++        pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
++    };
++}
++#[rustfmt::skip]
++gen!(impl);
++gen!(fn bar);
++
++// When the macro is in a different file, the suggestion spans can't be combined properly
++// and should not cause an ICE
++// See #2707
++#[macro_use]
++#[path = "../auxiliary/test_macro.rs"]
++pub mod test_macro;
++__implicit_hasher_test_macro!(impl<K, V> for HashMap<K, V> where V: test_macro::A);
++
++// #4260
++implicit_hasher_fn!();
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2b06d661772d28083e71f35e4abe9f7f056fe338
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,153 @@@
++error: impl for `HashMap` should be generalized over different hashers
++  --> $DIR/implicit_hasher.rs:16:35
++   |
++LL | impl<K: Hash + Eq, V> Foo<i8> for HashMap<K, V> {
++   |                                   ^^^^^^^^^^^^^
++   |
++note: the lint level is defined here
++  --> $DIR/implicit_hasher.rs:2:9
++   |
++LL | #![deny(clippy::implicit_hasher)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^
++help: consider adding a type parameter
++   |
++LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashMap<K, V, S> {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^             ^^^^^^^^^^^^^^^^
++help: ...and use generic constructor
++   |
++LL |         (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
++   |          ^^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: impl for `HashMap` should be generalized over different hashers
++  --> $DIR/implicit_hasher.rs:25:36
++   |
++LL | impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) {
++   |                                    ^^^^^^^^^^^^^
++   |
++help: consider adding a type parameter
++   |
++LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for (HashMap<K, V, S>,) {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^              ^^^^^^^^^^^^^^^^
++help: ...and use generic constructor
++   |
++LL |         ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Default::default()),))
++   |           ^^^^^^^^^^^^^^^^^^     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: impl for `HashMap` should be generalized over different hashers
++  --> $DIR/implicit_hasher.rs:30:19
++   |
++LL | impl Foo<i16> for HashMap<String, String> {
++   |                   ^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: consider adding a type parameter
++   |
++LL | impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashMap<String, String, S> {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^              ^^^^^^^^^^^^^^^^^^^^^^^^^^
++help: ...and use generic constructor
++   |
++LL |         (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
++   |          ^^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: impl for `HashSet` should be generalized over different hashers
++  --> $DIR/implicit_hasher.rs:47:32
++   |
++LL | impl<T: Hash + Eq> Foo<i8> for HashSet<T> {
++   |                                ^^^^^^^^^^
++   |
++help: consider adding a type parameter
++   |
++LL | impl<T: Hash + Eq, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashSet<T, S> {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^             ^^^^^^^^^^^^^
++help: ...and use generic constructor
++   |
++LL |         (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
++   |          ^^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: impl for `HashSet` should be generalized over different hashers
++  --> $DIR/implicit_hasher.rs:52:19
++   |
++LL | impl Foo<i16> for HashSet<String> {
++   |                   ^^^^^^^^^^^^^^^
++   |
++help: consider adding a type parameter
++   |
++LL | impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashSet<String, S> {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^              ^^^^^^^^^^^^^^^^^^
++help: ...and use generic constructor
++   |
++LL |         (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
++   |          ^^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: parameter of type `HashMap` should be generalized over different hashers
++  --> $DIR/implicit_hasher.rs:69:23
++   |
++LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
++   |                       ^^^^^^^^^^^^^^^^^
++   |
++help: consider adding a type parameter
++   |
++LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32, S>, _set: &mut HashSet<i32>) {}
++   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^            ^^^^^^^^^^^^^^^^^^^^
++
++error: parameter of type `HashSet` should be generalized over different hashers
++  --> $DIR/implicit_hasher.rs:69:53
++   |
++LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
++   |                                                     ^^^^^^^^^^^^
++   |
++help: consider adding a type parameter
++   |
++LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32, S>) {}
++   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                          ^^^^^^^^^^^^^^^
++
++error: impl for `HashMap` should be generalized over different hashers
++  --> $DIR/implicit_hasher.rs:73:43
++   |
++LL |         impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> {
++   |                                           ^^^^^^^^^^^^^
++...
++LL | gen!(impl);
++   | ----------- in this macro invocation
++   |
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++help: consider adding a type parameter
++   |
++LL |         impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<u8> for HashMap<K, V, S> {
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^             ^^^^^^^^^^^^^^^^
++help: ...and use generic constructor
++   |
++LL |                 (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
++   |                  ^^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: parameter of type `HashMap` should be generalized over different hashers
++  --> $DIR/implicit_hasher.rs:81:33
++   |
++LL |         pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
++   |                                 ^^^^^^^^^^^^^^^^^
++...
++LL | gen!(fn bar);
++   | ------------- in this macro invocation
++   |
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++help: consider adding a type parameter
++   |
++LL |         pub fn $name<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32, S>, _set: &mut HashSet<i32>) {}
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^            ^^^^^^^^^^^^^^^^^^^^
++
++error: parameter of type `HashSet` should be generalized over different hashers
++  --> $DIR/implicit_hasher.rs:81:63
++   |
++LL |         pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
++   |                                                               ^^^^^^^^^^^^
++...
++LL | gen!(fn bar);
++   | ------------- in this macro invocation
++   |
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++help: consider adding a type parameter
++   |
++LL |         pub fn $name<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32, S>) {}
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                          ^^^^^^^^^^^^^^^
++
++error: aborting due to 10 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9066dc3fedfd60fc5377adbfa78d2df368c2ed3a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,101 @@@
++// run-rustfix
++
++#![warn(clippy::implicit_return)]
++#![allow(clippy::needless_return, unused)]
++
++fn test_end_of_fn() -> bool {
++    if true {
++        // no error!
++        return true;
++    }
++
++    return true
++}
++
++#[allow(clippy::needless_bool)]
++fn test_if_block() -> bool {
++    if true {
++        return true
++    } else {
++        return false
++    }
++}
++
++#[rustfmt::skip]
++fn test_match(x: bool) -> bool {
++    match x {
++        true => return false,
++        false => { return true },
++    }
++}
++
++#[allow(clippy::needless_return)]
++fn test_match_with_unreachable(x: bool) -> bool {
++    match x {
++        true => return false,
++        false => unreachable!(),
++    }
++}
++
++#[allow(clippy::never_loop)]
++fn test_loop() -> bool {
++    loop {
++        return true;
++    }
++}
++
++#[allow(clippy::never_loop)]
++fn test_loop_with_block() -> bool {
++    loop {
++        {
++            return true;
++        }
++    }
++}
++
++#[allow(clippy::never_loop)]
++fn test_loop_with_nests() -> bool {
++    loop {
++        if true {
++            return true;
++        } else {
++            let _ = true;
++        }
++    }
++}
++
++#[allow(clippy::redundant_pattern_matching)]
++fn test_loop_with_if_let() -> bool {
++    loop {
++        if let Some(x) = Some(true) {
++            return x;
++        }
++    }
++}
++
++fn test_closure() {
++    #[rustfmt::skip]
++    let _ = || { return true };
++    let _ = || return true;
++}
++
++fn test_panic() -> bool {
++    panic!()
++}
++
++fn test_return_macro() -> String {
++    return format!("test {}", "test")
++}
++
++fn main() {
++    let _ = test_end_of_fn();
++    let _ = test_if_block();
++    let _ = test_match(true);
++    let _ = test_match_with_unreachable(true);
++    let _ = test_loop();
++    let _ = test_loop_with_block();
++    let _ = test_loop_with_nests();
++    let _ = test_loop_with_if_let();
++    test_closure();
++    let _ = test_return_macro();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c0d70ecf502ed6167fe7205866045f24d66c6ceb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,101 @@@
++// run-rustfix
++
++#![warn(clippy::implicit_return)]
++#![allow(clippy::needless_return, unused)]
++
++fn test_end_of_fn() -> bool {
++    if true {
++        // no error!
++        return true;
++    }
++
++    true
++}
++
++#[allow(clippy::needless_bool)]
++fn test_if_block() -> bool {
++    if true {
++        true
++    } else {
++        false
++    }
++}
++
++#[rustfmt::skip]
++fn test_match(x: bool) -> bool {
++    match x {
++        true => false,
++        false => { true },
++    }
++}
++
++#[allow(clippy::needless_return)]
++fn test_match_with_unreachable(x: bool) -> bool {
++    match x {
++        true => return false,
++        false => unreachable!(),
++    }
++}
++
++#[allow(clippy::never_loop)]
++fn test_loop() -> bool {
++    loop {
++        break true;
++    }
++}
++
++#[allow(clippy::never_loop)]
++fn test_loop_with_block() -> bool {
++    loop {
++        {
++            break true;
++        }
++    }
++}
++
++#[allow(clippy::never_loop)]
++fn test_loop_with_nests() -> bool {
++    loop {
++        if true {
++            break true;
++        } else {
++            let _ = true;
++        }
++    }
++}
++
++#[allow(clippy::redundant_pattern_matching)]
++fn test_loop_with_if_let() -> bool {
++    loop {
++        if let Some(x) = Some(true) {
++            return x;
++        }
++    }
++}
++
++fn test_closure() {
++    #[rustfmt::skip]
++    let _ = || { true };
++    let _ = || true;
++}
++
++fn test_panic() -> bool {
++    panic!()
++}
++
++fn test_return_macro() -> String {
++    format!("test {}", "test")
++}
++
++fn main() {
++    let _ = test_end_of_fn();
++    let _ = test_if_block();
++    let _ = test_match(true);
++    let _ = test_match_with_unreachable(true);
++    let _ = test_loop();
++    let _ = test_loop_with_block();
++    let _ = test_loop_with_nests();
++    let _ = test_loop_with_if_let();
++    test_closure();
++    let _ = test_return_macro();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fb2ec9027645459c8ae221a1962e61e49df03ce9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,70 @@@
++error: missing `return` statement
++  --> $DIR/implicit_return.rs:12:5
++   |
++LL |     true
++   |     ^^^^ help: add `return` as shown: `return true`
++   |
++   = note: `-D clippy::implicit-return` implied by `-D warnings`
++
++error: missing `return` statement
++  --> $DIR/implicit_return.rs:18:9
++   |
++LL |         true
++   |         ^^^^ help: add `return` as shown: `return true`
++
++error: missing `return` statement
++  --> $DIR/implicit_return.rs:20:9
++   |
++LL |         false
++   |         ^^^^^ help: add `return` as shown: `return false`
++
++error: missing `return` statement
++  --> $DIR/implicit_return.rs:27:17
++   |
++LL |         true => false,
++   |                 ^^^^^ help: add `return` as shown: `return false`
++
++error: missing `return` statement
++  --> $DIR/implicit_return.rs:28:20
++   |
++LL |         false => { true },
++   |                    ^^^^ help: add `return` as shown: `return true`
++
++error: missing `return` statement
++  --> $DIR/implicit_return.rs:43:9
++   |
++LL |         break true;
++   |         ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
++
++error: missing `return` statement
++  --> $DIR/implicit_return.rs:51:13
++   |
++LL |             break true;
++   |             ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
++
++error: missing `return` statement
++  --> $DIR/implicit_return.rs:60:13
++   |
++LL |             break true;
++   |             ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
++
++error: missing `return` statement
++  --> $DIR/implicit_return.rs:78:18
++   |
++LL |     let _ = || { true };
++   |                  ^^^^ help: add `return` as shown: `return true`
++
++error: missing `return` statement
++  --> $DIR/implicit_return.rs:79:16
++   |
++LL |     let _ = || true;
++   |                ^^^^ help: add `return` as shown: `return true`
++
++error: missing `return` statement
++  --> $DIR/implicit_return.rs:87:5
++   |
++LL |     format!("test {}", "test")
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")`
++
++error: aborting due to 11 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e0b5b31a00c7c4aef6e064173d51449f98e34196
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,160 @@@
++// run-rustfix
++#![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)]
++#![warn(clippy::implicit_saturating_sub)]
++
++fn main() {
++    // Tests for unsigned integers
++
++    let end_8: u8 = 10;
++    let start_8: u8 = 5;
++    let mut u_8: u8 = end_8 - start_8;
++
++    // Lint
++    u_8 = u_8.saturating_sub(1);
++
++    match end_8 {
++        10 => {
++            // Lint
++            u_8 = u_8.saturating_sub(1);
++        },
++        11 => u_8 += 1,
++        _ => u_8 = 0,
++    }
++
++    let end_16: u16 = 40;
++    let start_16: u16 = 35;
++
++    let mut u_16: u16 = end_16 - start_16;
++
++    // Lint
++    u_16 = u_16.saturating_sub(1);
++
++    let mut end_32: u32 = 7000;
++    let mut start_32: u32 = 7010;
++
++    let mut u_32: u32 = end_32 - start_32;
++
++    // Lint
++    u_32 = u_32.saturating_sub(1);
++
++    // No Lint
++    if u_32 > 0 {
++        u_16 += 1;
++    }
++
++    // No Lint
++    if u_32 != 0 {
++        end_32 -= 1;
++        start_32 += 1;
++    }
++
++    let mut end_64: u64 = 75001;
++    let mut start_64: u64 = 75000;
++
++    let mut u_64: u64 = end_64 - start_64;
++
++    // Lint
++    u_64 = u_64.saturating_sub(1);
++
++    // Lint
++    u_64 = u_64.saturating_sub(1);
++
++    // Lint
++    u_64 = u_64.saturating_sub(1);
++
++    // No Lint
++    if u_64 >= 1 {
++        u_64 -= 1;
++    }
++
++    // No Lint
++    if u_64 > 0 {
++        end_64 -= 1;
++    }
++
++    // Tests for usize
++    let end_usize: usize = 8054;
++    let start_usize: usize = 8050;
++
++    let mut u_usize: usize = end_usize - start_usize;
++
++    // Lint
++    u_usize = u_usize.saturating_sub(1);
++
++    // Tests for signed integers
++
++    let endi_8: i8 = 10;
++    let starti_8: i8 = 50;
++
++    let mut i_8: i8 = endi_8 - starti_8;
++
++    // Lint
++    i_8 = i_8.saturating_sub(1);
++
++    // Lint
++    i_8 = i_8.saturating_sub(1);
++
++    // Lint
++    i_8 = i_8.saturating_sub(1);
++
++    // Lint
++    i_8 = i_8.saturating_sub(1);
++
++    let endi_16: i16 = 45;
++    let starti_16: i16 = 44;
++
++    let mut i_16: i16 = endi_16 - starti_16;
++
++    // Lint
++    i_16 = i_16.saturating_sub(1);
++
++    // Lint
++    i_16 = i_16.saturating_sub(1);
++
++    // Lint
++    i_16 = i_16.saturating_sub(1);
++
++    // Lint
++    i_16 = i_16.saturating_sub(1);
++
++    let endi_32: i32 = 45;
++    let starti_32: i32 = 44;
++
++    let mut i_32: i32 = endi_32 - starti_32;
++
++    // Lint
++    i_32 = i_32.saturating_sub(1);
++
++    // Lint
++    i_32 = i_32.saturating_sub(1);
++
++    // Lint
++    i_32 = i_32.saturating_sub(1);
++
++    // Lint
++    i_32 = i_32.saturating_sub(1);
++
++    let endi_64: i64 = 45;
++    let starti_64: i64 = 44;
++
++    let mut i_64: i64 = endi_64 - starti_64;
++
++    // Lint
++    i_64 = i_64.saturating_sub(1);
++
++    // Lint
++    i_64 = i_64.saturating_sub(1);
++
++    // Lint
++    i_64 = i_64.saturating_sub(1);
++
++    // No Lint
++    if i_64 > 0 {
++        i_64 -= 1;
++    }
++
++    // No Lint
++    if i_64 != 0 {
++        i_64 -= 1;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..39d81608922975e74fbb105eb8432ae159b1eff5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,206 @@@
++// run-rustfix
++#![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)]
++#![warn(clippy::implicit_saturating_sub)]
++
++fn main() {
++    // Tests for unsigned integers
++
++    let end_8: u8 = 10;
++    let start_8: u8 = 5;
++    let mut u_8: u8 = end_8 - start_8;
++
++    // Lint
++    if u_8 > 0 {
++        u_8 = u_8 - 1;
++    }
++
++    match end_8 {
++        10 => {
++            // Lint
++            if u_8 > 0 {
++                u_8 -= 1;
++            }
++        },
++        11 => u_8 += 1,
++        _ => u_8 = 0,
++    }
++
++    let end_16: u16 = 40;
++    let start_16: u16 = 35;
++
++    let mut u_16: u16 = end_16 - start_16;
++
++    // Lint
++    if u_16 > 0 {
++        u_16 -= 1;
++    }
++
++    let mut end_32: u32 = 7000;
++    let mut start_32: u32 = 7010;
++
++    let mut u_32: u32 = end_32 - start_32;
++
++    // Lint
++    if u_32 != 0 {
++        u_32 -= 1;
++    }
++
++    // No Lint
++    if u_32 > 0 {
++        u_16 += 1;
++    }
++
++    // No Lint
++    if u_32 != 0 {
++        end_32 -= 1;
++        start_32 += 1;
++    }
++
++    let mut end_64: u64 = 75001;
++    let mut start_64: u64 = 75000;
++
++    let mut u_64: u64 = end_64 - start_64;
++
++    // Lint
++    if u_64 > 0 {
++        u_64 -= 1;
++    }
++
++    // Lint
++    if 0 < u_64 {
++        u_64 -= 1;
++    }
++
++    // Lint
++    if 0 != u_64 {
++        u_64 -= 1;
++    }
++
++    // No Lint
++    if u_64 >= 1 {
++        u_64 -= 1;
++    }
++
++    // No Lint
++    if u_64 > 0 {
++        end_64 -= 1;
++    }
++
++    // Tests for usize
++    let end_usize: usize = 8054;
++    let start_usize: usize = 8050;
++
++    let mut u_usize: usize = end_usize - start_usize;
++
++    // Lint
++    if u_usize > 0 {
++        u_usize -= 1;
++    }
++
++    // Tests for signed integers
++
++    let endi_8: i8 = 10;
++    let starti_8: i8 = 50;
++
++    let mut i_8: i8 = endi_8 - starti_8;
++
++    // Lint
++    if i_8 > i8::MIN {
++        i_8 -= 1;
++    }
++
++    // Lint
++    if i_8 > i8::min_value() {
++        i_8 -= 1;
++    }
++
++    // Lint
++    if i_8 != i8::MIN {
++        i_8 -= 1;
++    }
++
++    // Lint
++    if i_8 != i8::min_value() {
++        i_8 -= 1;
++    }
++
++    let endi_16: i16 = 45;
++    let starti_16: i16 = 44;
++
++    let mut i_16: i16 = endi_16 - starti_16;
++
++    // Lint
++    if i_16 > i16::MIN {
++        i_16 -= 1;
++    }
++
++    // Lint
++    if i_16 > i16::min_value() {
++        i_16 -= 1;
++    }
++
++    // Lint
++    if i_16 != i16::MIN {
++        i_16 -= 1;
++    }
++
++    // Lint
++    if i_16 != i16::min_value() {
++        i_16 -= 1;
++    }
++
++    let endi_32: i32 = 45;
++    let starti_32: i32 = 44;
++
++    let mut i_32: i32 = endi_32 - starti_32;
++
++    // Lint
++    if i_32 > i32::MIN {
++        i_32 -= 1;
++    }
++
++    // Lint
++    if i_32 > i32::min_value() {
++        i_32 -= 1;
++    }
++
++    // Lint
++    if i_32 != i32::MIN {
++        i_32 -= 1;
++    }
++
++    // Lint
++    if i_32 != i32::min_value() {
++        i_32 -= 1;
++    }
++
++    let endi_64: i64 = 45;
++    let starti_64: i64 = 44;
++
++    let mut i_64: i64 = endi_64 - starti_64;
++
++    // Lint
++    if i64::min_value() < i_64 {
++        i_64 -= 1;
++    }
++
++    // Lint
++    if i64::MIN != i_64 {
++        i_64 -= 1;
++    }
++
++    // Lint
++    if i64::MIN < i_64 {
++        i_64 -= 1;
++    }
++
++    // No Lint
++    if i_64 > 0 {
++        i_64 -= 1;
++    }
++
++    // No Lint
++    if i_64 != 0 {
++        i_64 -= 1;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a8ba870b1dda6b03e76b533851d0c5a671458f7d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,188 @@@
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:13:5
++   |
++LL | /     if u_8 > 0 {
++LL | |         u_8 = u_8 - 1;
++LL | |     }
++   | |_____^ help: try: `u_8 = u_8.saturating_sub(1);`
++   |
++   = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:20:13
++   |
++LL | /             if u_8 > 0 {
++LL | |                 u_8 -= 1;
++LL | |             }
++   | |_____________^ help: try: `u_8 = u_8.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:34:5
++   |
++LL | /     if u_16 > 0 {
++LL | |         u_16 -= 1;
++LL | |     }
++   | |_____^ help: try: `u_16 = u_16.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:44:5
++   |
++LL | /     if u_32 != 0 {
++LL | |         u_32 -= 1;
++LL | |     }
++   | |_____^ help: try: `u_32 = u_32.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:65:5
++   |
++LL | /     if u_64 > 0 {
++LL | |         u_64 -= 1;
++LL | |     }
++   | |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:70:5
++   |
++LL | /     if 0 < u_64 {
++LL | |         u_64 -= 1;
++LL | |     }
++   | |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:75:5
++   |
++LL | /     if 0 != u_64 {
++LL | |         u_64 -= 1;
++LL | |     }
++   | |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:96:5
++   |
++LL | /     if u_usize > 0 {
++LL | |         u_usize -= 1;
++LL | |     }
++   | |_____^ help: try: `u_usize = u_usize.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:108:5
++   |
++LL | /     if i_8 > i8::MIN {
++LL | |         i_8 -= 1;
++LL | |     }
++   | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:113:5
++   |
++LL | /     if i_8 > i8::min_value() {
++LL | |         i_8 -= 1;
++LL | |     }
++   | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:118:5
++   |
++LL | /     if i_8 != i8::MIN {
++LL | |         i_8 -= 1;
++LL | |     }
++   | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:123:5
++   |
++LL | /     if i_8 != i8::min_value() {
++LL | |         i_8 -= 1;
++LL | |     }
++   | |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:133:5
++   |
++LL | /     if i_16 > i16::MIN {
++LL | |         i_16 -= 1;
++LL | |     }
++   | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:138:5
++   |
++LL | /     if i_16 > i16::min_value() {
++LL | |         i_16 -= 1;
++LL | |     }
++   | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:143:5
++   |
++LL | /     if i_16 != i16::MIN {
++LL | |         i_16 -= 1;
++LL | |     }
++   | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:148:5
++   |
++LL | /     if i_16 != i16::min_value() {
++LL | |         i_16 -= 1;
++LL | |     }
++   | |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:158:5
++   |
++LL | /     if i_32 > i32::MIN {
++LL | |         i_32 -= 1;
++LL | |     }
++   | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:163:5
++   |
++LL | /     if i_32 > i32::min_value() {
++LL | |         i_32 -= 1;
++LL | |     }
++   | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:168:5
++   |
++LL | /     if i_32 != i32::MIN {
++LL | |         i_32 -= 1;
++LL | |     }
++   | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:173:5
++   |
++LL | /     if i_32 != i32::min_value() {
++LL | |         i_32 -= 1;
++LL | |     }
++   | |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:183:5
++   |
++LL | /     if i64::min_value() < i_64 {
++LL | |         i_64 -= 1;
++LL | |     }
++   | |_____^ help: try: `i_64 = i_64.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:188:5
++   |
++LL | /     if i64::MIN != i_64 {
++LL | |         i_64 -= 1;
++LL | |     }
++   | |_____^ help: try: `i_64 = i_64.saturating_sub(1);`
++
++error: Implicitly performing saturating subtraction
++  --> $DIR/implicit_saturating_sub.rs:193:5
++   |
++LL | /     if i64::MIN < i_64 {
++LL | |         i_64 -= 1;
++LL | |     }
++   | |_____^ help: try: `i_64 = i_64.saturating_sub(1);`
++
++error: aborting due to 23 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b75f10917df18b0a8a5968364172d63ebe9766d2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++// run-rustfix
++#[warn(clippy::inconsistent_digit_grouping)]
++#[deny(clippy::unreadable_literal)]
++#[allow(unused_variables, clippy::excessive_precision)]
++fn main() {
++    macro_rules! mac1 {
++        () => {
++            1_23_456
++        };
++    }
++    macro_rules! mac2 {
++        () => {
++            1_234.5678_f32
++        };
++    }
++
++    let good = (
++        123,
++        1_234,
++        1_2345_6789,
++        123_f32,
++        1_234.12_f32,
++        1_234.123_4_f32,
++        1.123_456_7_f32,
++    );
++    let bad = (123_456, 12_345_678, 1_234_567, 1_234.567_8_f32, 1.234_567_8_f32);
++
++    // Test padding
++    let _ = 0x0010_0000;
++    let _ = 0x0100_0000;
++    let _ = 0x1000_0000;
++    let _ = 0x0001_0000_0000_u64;
++
++    // Test suggestion when fraction has no digits
++    let _: f32 = 123_456.;
++
++    // Test UUID formatted literal
++    let _: u128 = 0x12345678_1234_1234_1234_123456789012;
++
++    // Ignore literals in macros
++    let _ = mac1!();
++    let _ = mac2!();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..79ce38be19bd3e877935a034929cfab0c69dc270
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++// run-rustfix
++#[warn(clippy::inconsistent_digit_grouping)]
++#[deny(clippy::unreadable_literal)]
++#[allow(unused_variables, clippy::excessive_precision)]
++fn main() {
++    macro_rules! mac1 {
++        () => {
++            1_23_456
++        };
++    }
++    macro_rules! mac2 {
++        () => {
++            1_234.5678_f32
++        };
++    }
++
++    let good = (
++        123,
++        1_234,
++        1_2345_6789,
++        123_f32,
++        1_234.12_f32,
++        1_234.123_4_f32,
++        1.123_456_7_f32,
++    );
++    let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
++
++    // Test padding
++    let _ = 0x100000;
++    let _ = 0x1000000;
++    let _ = 0x10000000;
++    let _ = 0x100000000_u64;
++
++    // Test suggestion when fraction has no digits
++    let _: f32 = 1_23_456.;
++
++    // Test UUID formatted literal
++    let _: u128 = 0x12345678_1234_1234_1234_123456789012;
++
++    // Ignore literals in macros
++    let _ = mac1!();
++    let _ = mac2!();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b8ac915546200718aba29271f5776a380980efe7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,70 @@@
++error: digits grouped inconsistently by underscores
++  --> $DIR/inconsistent_digit_grouping.rs:26:16
++   |
++LL |     let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
++   |                ^^^^^^^^ help: consider: `123_456`
++   |
++   = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings`
++
++error: digits grouped inconsistently by underscores
++  --> $DIR/inconsistent_digit_grouping.rs:26:26
++   |
++LL |     let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
++   |                          ^^^^^^^^^^ help: consider: `12_345_678`
++
++error: digits grouped inconsistently by underscores
++  --> $DIR/inconsistent_digit_grouping.rs:26:38
++   |
++LL |     let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
++   |                                      ^^^^^^^^ help: consider: `1_234_567`
++
++error: digits grouped inconsistently by underscores
++  --> $DIR/inconsistent_digit_grouping.rs:26:48
++   |
++LL |     let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
++   |                                                ^^^^^^^^^^^^^^ help: consider: `1_234.567_8_f32`
++
++error: digits grouped inconsistently by underscores
++  --> $DIR/inconsistent_digit_grouping.rs:26:64
++   |
++LL |     let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32);
++   |                                                                ^^^^^^^^^^^^^^ help: consider: `1.234_567_8_f32`
++
++error: long literal lacking separators
++  --> $DIR/inconsistent_digit_grouping.rs:29:13
++   |
++LL |     let _ = 0x100000;
++   |             ^^^^^^^^ help: consider: `0x0010_0000`
++   |
++note: the lint level is defined here
++  --> $DIR/inconsistent_digit_grouping.rs:3:8
++   |
++LL | #[deny(clippy::unreadable_literal)]
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: long literal lacking separators
++  --> $DIR/inconsistent_digit_grouping.rs:30:13
++   |
++LL |     let _ = 0x1000000;
++   |             ^^^^^^^^^ help: consider: `0x0100_0000`
++
++error: long literal lacking separators
++  --> $DIR/inconsistent_digit_grouping.rs:31:13
++   |
++LL |     let _ = 0x10000000;
++   |             ^^^^^^^^^^ help: consider: `0x1000_0000`
++
++error: long literal lacking separators
++  --> $DIR/inconsistent_digit_grouping.rs:32:13
++   |
++LL |     let _ = 0x100000000_u64;
++   |             ^^^^^^^^^^^^^^^ help: consider: `0x0001_0000_0000_u64`
++
++error: digits grouped inconsistently by underscores
++  --> $DIR/inconsistent_digit_grouping.rs:35:18
++   |
++LL |     let _: f32 = 1_23_456.;
++   |                  ^^^^^^^^^ help: consider: `123_456.`
++
++error: aborting due to 10 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..000d5269930ba8f29a69a8c496c7cded649a14dc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++#![warn(clippy::indexing_slicing)]
++// We also check the out_of_bounds_indexing lint here, because it lints similar things and
++// we want to avoid false positives.
++#![warn(clippy::out_of_bounds_indexing)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation)]
++
++fn main() {
++    let x = [1, 2, 3, 4];
++    let index: usize = 1;
++    x[index];
++    x[4]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
++    x[1 << 3]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
++
++    x[0]; // Ok, should not produce stderr.
++    x[3]; // Ok, should not produce stderr.
++
++    let y = &x;
++    y[0];
++
++    let v = vec![0; 5];
++    v[0];
++    v[10];
++    v[1 << 3];
++
++    const N: usize = 15; // Out of bounds
++    const M: usize = 3; // In bounds
++    x[N]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
++    x[M]; // Ok, should not produce stderr.
++    v[N];
++    v[M];
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ac5f0d0a39e89aa7f57b2be61e34bb307a29eb91
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,79 @@@
++error: this operation will panic at runtime
++  --> $DIR/indexing_slicing_index.rs:11:5
++   |
++LL |     x[4]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
++   |     ^^^^ index out of bounds: the len is 4 but the index is 4
++   |
++   = note: `#[deny(unconditional_panic)]` on by default
++
++error: this operation will panic at runtime
++  --> $DIR/indexing_slicing_index.rs:12:5
++   |
++LL |     x[1 << 3]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
++   |     ^^^^^^^^^ index out of bounds: the len is 4 but the index is 8
++
++error: this operation will panic at runtime
++  --> $DIR/indexing_slicing_index.rs:27:5
++   |
++LL |     x[N]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
++   |     ^^^^ index out of bounds: the len is 4 but the index is 15
++
++error: indexing may panic.
++  --> $DIR/indexing_slicing_index.rs:10:5
++   |
++LL |     x[index];
++   |     ^^^^^^^^
++   |
++   = note: `-D clippy::indexing-slicing` implied by `-D warnings`
++   = help: Consider using `.get(n)` or `.get_mut(n)` instead
++
++error: indexing may panic.
++  --> $DIR/indexing_slicing_index.rs:18:5
++   |
++LL |     y[0];
++   |     ^^^^
++   |
++   = help: Consider using `.get(n)` or `.get_mut(n)` instead
++
++error: indexing may panic.
++  --> $DIR/indexing_slicing_index.rs:21:5
++   |
++LL |     v[0];
++   |     ^^^^
++   |
++   = help: Consider using `.get(n)` or `.get_mut(n)` instead
++
++error: indexing may panic.
++  --> $DIR/indexing_slicing_index.rs:22:5
++   |
++LL |     v[10];
++   |     ^^^^^
++   |
++   = help: Consider using `.get(n)` or `.get_mut(n)` instead
++
++error: indexing may panic.
++  --> $DIR/indexing_slicing_index.rs:23:5
++   |
++LL |     v[1 << 3];
++   |     ^^^^^^^^^
++   |
++   = help: Consider using `.get(n)` or `.get_mut(n)` instead
++
++error: indexing may panic.
++  --> $DIR/indexing_slicing_index.rs:29:5
++   |
++LL |     v[N];
++   |     ^^^^
++   |
++   = help: Consider using `.get(n)` or `.get_mut(n)` instead
++
++error: indexing may panic.
++  --> $DIR/indexing_slicing_index.rs:30:5
++   |
++LL |     v[M];
++   |     ^^^^
++   |
++   = help: Consider using `.get(n)` or `.get_mut(n)` instead
++
++error: aborting due to 10 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7b107db39f0221a1fc1d3a4295c463f53e549e8e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++#![warn(clippy::indexing_slicing)]
++// We also check the out_of_bounds_indexing lint here, because it lints similar things and
++// we want to avoid false positives.
++#![warn(clippy::out_of_bounds_indexing)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation)]
++
++fn main() {
++    let x = [1, 2, 3, 4];
++    let index: usize = 1;
++    let index_from: usize = 2;
++    let index_to: usize = 3;
++    &x[index..];
++    &x[..index];
++    &x[index_from..index_to];
++    &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to].
++    &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10].
++    &x[0..][..3];
++    &x[1..][..5];
++
++    &x[0..].get(..3); // Ok, should not produce stderr.
++    &x[0..3]; // Ok, should not produce stderr.
++
++    let y = &x;
++    &y[1..2];
++    &y[0..=4];
++    &y[..=4];
++
++    &y[..]; // Ok, should not produce stderr.
++
++    let v = vec![0; 5];
++    &v[10..100];
++    &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100].
++    &v[10..];
++    &v[..100];
++
++    &v[..]; // Ok, should not produce stderr.
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ec6c157ac1a2659510a80d6f36355d5a41b90041
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,137 @@@
++error: slicing may panic.
++  --> $DIR/indexing_slicing_slice.rs:12:6
++   |
++LL |     &x[index..];
++   |      ^^^^^^^^^^
++   |
++   = note: `-D clippy::indexing-slicing` implied by `-D warnings`
++   = help: Consider using `.get(n..)` or .get_mut(n..)` instead
++
++error: slicing may panic.
++  --> $DIR/indexing_slicing_slice.rs:13:6
++   |
++LL |     &x[..index];
++   |      ^^^^^^^^^^
++   |
++   = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
++
++error: slicing may panic.
++  --> $DIR/indexing_slicing_slice.rs:14:6
++   |
++LL |     &x[index_from..index_to];
++   |      ^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead
++
++error: slicing may panic.
++  --> $DIR/indexing_slicing_slice.rs:15:6
++   |
++LL |     &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to].
++   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
++
++error: slicing may panic.
++  --> $DIR/indexing_slicing_slice.rs:15:6
++   |
++LL |     &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to].
++   |      ^^^^^^^^^^^^^^^
++   |
++   = help: Consider using `.get(n..)` or .get_mut(n..)` instead
++
++error: slicing may panic.
++  --> $DIR/indexing_slicing_slice.rs:16:6
++   |
++LL |     &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10].
++   |      ^^^^^^^^^^^^
++   |
++   = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
++
++error: range is out of bounds
++  --> $DIR/indexing_slicing_slice.rs:16:8
++   |
++LL |     &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10].
++   |        ^
++   |
++   = note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings`
++
++error: slicing may panic.
++  --> $DIR/indexing_slicing_slice.rs:17:6
++   |
++LL |     &x[0..][..3];
++   |      ^^^^^^^^^^^
++   |
++   = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
++
++error: slicing may panic.
++  --> $DIR/indexing_slicing_slice.rs:18:6
++   |
++LL |     &x[1..][..5];
++   |      ^^^^^^^^^^^
++   |
++   = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
++
++error: slicing may panic.
++  --> $DIR/indexing_slicing_slice.rs:24:6
++   |
++LL |     &y[1..2];
++   |      ^^^^^^^
++   |
++   = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead
++
++error: slicing may panic.
++  --> $DIR/indexing_slicing_slice.rs:25:6
++   |
++LL |     &y[0..=4];
++   |      ^^^^^^^^
++   |
++   = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead
++
++error: slicing may panic.
++  --> $DIR/indexing_slicing_slice.rs:26:6
++   |
++LL |     &y[..=4];
++   |      ^^^^^^^
++   |
++   = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
++
++error: slicing may panic.
++  --> $DIR/indexing_slicing_slice.rs:31:6
++   |
++LL |     &v[10..100];
++   |      ^^^^^^^^^^
++   |
++   = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead
++
++error: slicing may panic.
++  --> $DIR/indexing_slicing_slice.rs:32:6
++   |
++LL |     &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100].
++   |      ^^^^^^^^^^^^^^
++   |
++   = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
++
++error: range is out of bounds
++  --> $DIR/indexing_slicing_slice.rs:32:8
++   |
++LL |     &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100].
++   |        ^^
++
++error: slicing may panic.
++  --> $DIR/indexing_slicing_slice.rs:33:6
++   |
++LL |     &v[10..];
++   |      ^^^^^^^
++   |
++   = help: Consider using `.get(n..)` or .get_mut(n..)` instead
++
++error: slicing may panic.
++  --> $DIR/indexing_slicing_slice.rs:34:6
++   |
++LL |     &v[..100];
++   |      ^^^^^^^^
++   |
++   = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
++
++error: aborting due to 17 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c972b9419ef76adaf2aa18168ada844245be25a5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++// run-rustfix
++#![deny(clippy::inefficient_to_string)]
++
++use std::borrow::Cow;
++
++fn main() {
++    let rstr: &str = "hello";
++    let rrstr: &&str = &rstr;
++    let rrrstr: &&&str = &rrstr;
++    let _: String = rstr.to_string();
++    let _: String = (*rrstr).to_string();
++    let _: String = (**rrrstr).to_string();
++
++    let string: String = String::from("hello");
++    let rstring: &String = &string;
++    let rrstring: &&String = &rstring;
++    let rrrstring: &&&String = &rrstring;
++    let _: String = string.to_string();
++    let _: String = rstring.to_string();
++    let _: String = (*rrstring).to_string();
++    let _: String = (**rrrstring).to_string();
++
++    let cow: Cow<'_, str> = Cow::Borrowed("hello");
++    let rcow: &Cow<'_, str> = &cow;
++    let rrcow: &&Cow<'_, str> = &rcow;
++    let rrrcow: &&&Cow<'_, str> = &rrcow;
++    let _: String = cow.to_string();
++    let _: String = rcow.to_string();
++    let _: String = (*rrcow).to_string();
++    let _: String = (**rrrcow).to_string();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..acdc55aa0d69d65002753cd11c92fe6300ff60b4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++// run-rustfix
++#![deny(clippy::inefficient_to_string)]
++
++use std::borrow::Cow;
++
++fn main() {
++    let rstr: &str = "hello";
++    let rrstr: &&str = &rstr;
++    let rrrstr: &&&str = &rrstr;
++    let _: String = rstr.to_string();
++    let _: String = rrstr.to_string();
++    let _: String = rrrstr.to_string();
++
++    let string: String = String::from("hello");
++    let rstring: &String = &string;
++    let rrstring: &&String = &rstring;
++    let rrrstring: &&&String = &rrstring;
++    let _: String = string.to_string();
++    let _: String = rstring.to_string();
++    let _: String = rrstring.to_string();
++    let _: String = rrrstring.to_string();
++
++    let cow: Cow<'_, str> = Cow::Borrowed("hello");
++    let rcow: &Cow<'_, str> = &cow;
++    let rrcow: &&Cow<'_, str> = &rcow;
++    let rrrcow: &&&Cow<'_, str> = &rrcow;
++    let _: String = cow.to_string();
++    let _: String = rcow.to_string();
++    let _: String = rrcow.to_string();
++    let _: String = rrrcow.to_string();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4be46161e8b74f2cb88978d5761b1c61509ab377
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++error: calling `to_string` on `&&str`
++  --> $DIR/inefficient_to_string.rs:11:21
++   |
++LL |     let _: String = rrstr.to_string();
++   |                     ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstr).to_string()`
++   |
++note: the lint level is defined here
++  --> $DIR/inefficient_to_string.rs:2:9
++   |
++LL | #![deny(clippy::inefficient_to_string)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   = help: `&str` implements `ToString` through a slower blanket impl, but `str` has a fast specialization of `ToString`
++
++error: calling `to_string` on `&&&str`
++  --> $DIR/inefficient_to_string.rs:12:21
++   |
++LL |     let _: String = rrrstr.to_string();
++   |                     ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstr).to_string()`
++   |
++   = help: `&&str` implements `ToString` through a slower blanket impl, but `str` has a fast specialization of `ToString`
++
++error: calling `to_string` on `&&std::string::String`
++  --> $DIR/inefficient_to_string.rs:20:21
++   |
++LL |     let _: String = rrstring.to_string();
++   |                     ^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstring).to_string()`
++   |
++   = help: `&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString`
++
++error: calling `to_string` on `&&&std::string::String`
++  --> $DIR/inefficient_to_string.rs:21:21
++   |
++LL |     let _: String = rrrstring.to_string();
++   |                     ^^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstring).to_string()`
++   |
++   = help: `&&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString`
++
++error: calling `to_string` on `&&std::borrow::Cow<str>`
++  --> $DIR/inefficient_to_string.rs:29:21
++   |
++LL |     let _: String = rrcow.to_string();
++   |                     ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrcow).to_string()`
++   |
++   = help: `&std::borrow::Cow<str>` implements `ToString` through a slower blanket impl, but `std::borrow::Cow<str>` has a fast specialization of `ToString`
++
++error: calling `to_string` on `&&&std::borrow::Cow<str>`
++  --> $DIR/inefficient_to_string.rs:30:21
++   |
++LL |     let _: String = rrrcow.to_string();
++   |                     ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrcow).to_string()`
++   |
++   = help: `&&std::borrow::Cow<str>` implements `ToString` through a slower blanket impl, but `std::borrow::Cow<str>` has a fast specialization of `ToString`
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b8e40d995531aeb43c3c021716336450cf3d44d7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,112 @@@
++// run-rustfix
++#![feature(exhaustive_patterns, never_type)]
++#![allow(dead_code, unreachable_code, unused_variables)]
++#![allow(clippy::let_and_return)]
++
++enum SingleVariantEnum {
++    Variant(i32),
++}
++
++struct TupleStruct(i32);
++
++enum EmptyEnum {}
++
++macro_rules! match_enum {
++    ($param:expr) => {
++        let data = match $param {
++            SingleVariantEnum::Variant(i) => i,
++        };
++    };
++}
++
++fn infallible_destructuring_match_enum() {
++    let wrapper = SingleVariantEnum::Variant(0);
++
++    // This should lint!
++    let SingleVariantEnum::Variant(data) = wrapper;
++
++    // This shouldn't (inside macro)
++    match_enum!(wrapper);
++
++    // This shouldn't!
++    let data = match wrapper {
++        SingleVariantEnum::Variant(_) => -1,
++    };
++
++    // Neither should this!
++    let data = match wrapper {
++        SingleVariantEnum::Variant(i) => -1,
++    };
++
++    let SingleVariantEnum::Variant(data) = wrapper;
++}
++
++macro_rules! match_struct {
++    ($param:expr) => {
++        let data = match $param {
++            TupleStruct(i) => i,
++        };
++    };
++}
++
++fn infallible_destructuring_match_struct() {
++    let wrapper = TupleStruct(0);
++
++    // This should lint!
++    let TupleStruct(data) = wrapper;
++
++    // This shouldn't (inside macro)
++    match_struct!(wrapper);
++
++    // This shouldn't!
++    let data = match wrapper {
++        TupleStruct(_) => -1,
++    };
++
++    // Neither should this!
++    let data = match wrapper {
++        TupleStruct(i) => -1,
++    };
++
++    let TupleStruct(data) = wrapper;
++}
++
++macro_rules! match_never_enum {
++    ($param:expr) => {
++        let data = match $param {
++            Ok(i) => i,
++        };
++    };
++}
++
++fn never_enum() {
++    let wrapper: Result<i32, !> = Ok(23);
++
++    // This should lint!
++    let Ok(data) = wrapper;
++
++    // This shouldn't (inside macro)
++    match_never_enum!(wrapper);
++
++    // This shouldn't!
++    let data = match wrapper {
++        Ok(_) => -1,
++    };
++
++    // Neither should this!
++    let data = match wrapper {
++        Ok(i) => -1,
++    };
++
++    let Ok(data) = wrapper;
++}
++
++impl EmptyEnum {
++    fn match_on(&self) -> ! {
++        // The lint shouldn't pick this up, as `let` won't work here!
++        let data = match *self {};
++        data
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..106cd438b90e79e1b0dd28c41c8b0bf3cc1e576d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,118 @@@
++// run-rustfix
++#![feature(exhaustive_patterns, never_type)]
++#![allow(dead_code, unreachable_code, unused_variables)]
++#![allow(clippy::let_and_return)]
++
++enum SingleVariantEnum {
++    Variant(i32),
++}
++
++struct TupleStruct(i32);
++
++enum EmptyEnum {}
++
++macro_rules! match_enum {
++    ($param:expr) => {
++        let data = match $param {
++            SingleVariantEnum::Variant(i) => i,
++        };
++    };
++}
++
++fn infallible_destructuring_match_enum() {
++    let wrapper = SingleVariantEnum::Variant(0);
++
++    // This should lint!
++    let data = match wrapper {
++        SingleVariantEnum::Variant(i) => i,
++    };
++
++    // This shouldn't (inside macro)
++    match_enum!(wrapper);
++
++    // This shouldn't!
++    let data = match wrapper {
++        SingleVariantEnum::Variant(_) => -1,
++    };
++
++    // Neither should this!
++    let data = match wrapper {
++        SingleVariantEnum::Variant(i) => -1,
++    };
++
++    let SingleVariantEnum::Variant(data) = wrapper;
++}
++
++macro_rules! match_struct {
++    ($param:expr) => {
++        let data = match $param {
++            TupleStruct(i) => i,
++        };
++    };
++}
++
++fn infallible_destructuring_match_struct() {
++    let wrapper = TupleStruct(0);
++
++    // This should lint!
++    let data = match wrapper {
++        TupleStruct(i) => i,
++    };
++
++    // This shouldn't (inside macro)
++    match_struct!(wrapper);
++
++    // This shouldn't!
++    let data = match wrapper {
++        TupleStruct(_) => -1,
++    };
++
++    // Neither should this!
++    let data = match wrapper {
++        TupleStruct(i) => -1,
++    };
++
++    let TupleStruct(data) = wrapper;
++}
++
++macro_rules! match_never_enum {
++    ($param:expr) => {
++        let data = match $param {
++            Ok(i) => i,
++        };
++    };
++}
++
++fn never_enum() {
++    let wrapper: Result<i32, !> = Ok(23);
++
++    // This should lint!
++    let data = match wrapper {
++        Ok(i) => i,
++    };
++
++    // This shouldn't (inside macro)
++    match_never_enum!(wrapper);
++
++    // This shouldn't!
++    let data = match wrapper {
++        Ok(_) => -1,
++    };
++
++    // Neither should this!
++    let data = match wrapper {
++        Ok(i) => -1,
++    };
++
++    let Ok(data) = wrapper;
++}
++
++impl EmptyEnum {
++    fn match_on(&self) -> ! {
++        // The lint shouldn't pick this up, as `let` won't work here!
++        let data = match *self {};
++        data
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1b78db42014a2c5a3f493955138680a614780b5c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let`
++  --> $DIR/infallible_destructuring_match.rs:26:5
++   |
++LL | /     let data = match wrapper {
++LL | |         SingleVariantEnum::Variant(i) => i,
++LL | |     };
++   | |______^ help: try this: `let SingleVariantEnum::Variant(data) = wrapper;`
++   |
++   = note: `-D clippy::infallible-destructuring-match` implied by `-D warnings`
++
++error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let`
++  --> $DIR/infallible_destructuring_match.rs:58:5
++   |
++LL | /     let data = match wrapper {
++LL | |         TupleStruct(i) => i,
++LL | |     };
++   | |______^ help: try this: `let TupleStruct(data) = wrapper;`
++
++error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let`
++  --> $DIR/infallible_destructuring_match.rs:90:5
++   |
++LL | /     let data = match wrapper {
++LL | |         Ok(i) => i,
++LL | |     };
++   | |______^ help: try this: `let Ok(data) = wrapper;`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1fe688977659d962c5535b4127df8cf546c4852a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,69 @@@
++use std::iter::repeat;
++fn square_is_lower_64(x: &u32) -> bool {
++    x * x < 64
++}
++
++#[allow(clippy::maybe_infinite_iter)]
++#[deny(clippy::infinite_iter)]
++fn infinite_iters() {
++    repeat(0_u8).collect::<Vec<_>>(); // infinite iter
++    (0..8_u32).take_while(square_is_lower_64).cycle().count(); // infinite iter
++    (0..8_u64).chain(0..).max(); // infinite iter
++    (0_usize..)
++        .chain([0usize, 1, 2].iter().cloned())
++        .skip_while(|x| *x != 42)
++        .min(); // infinite iter
++    (0..8_u32)
++        .rev()
++        .cycle()
++        .map(|x| x + 1_u32)
++        .for_each(|x| println!("{}", x)); // infinite iter
++    (0..3_u32).flat_map(|x| x..).sum::<u32>(); // infinite iter
++    (0_usize..).flat_map(|x| 0..x).product::<usize>(); // infinite iter
++    (0_u64..).filter(|x| x % 2 == 0).last(); // infinite iter
++    (0..42_u64).by_ref().last(); // not an infinite, because ranges are double-ended
++    (0..).next(); // iterator is not exhausted
++}
++
++#[deny(clippy::maybe_infinite_iter)]
++fn potential_infinite_iters() {
++    (0..).zip((0..).take_while(square_is_lower_64)).count(); // maybe infinite iter
++    repeat(42).take_while(|x| *x == 42).chain(0..42).max(); // maybe infinite iter
++    (1..)
++        .scan(0, |state, x| {
++            *state += x;
++            Some(*state)
++        })
++        .min(); // maybe infinite iter
++    (0..).find(|x| *x == 24); // maybe infinite iter
++    (0..).position(|x| x == 24); // maybe infinite iter
++    (0..).any(|x| x == 24); // maybe infinite iter
++    (0..).all(|x| x == 24); // maybe infinite iter
++
++    (0..).zip(0..42).take_while(|&(x, _)| x != 42).count(); // not infinite
++    repeat(42).take_while(|x| *x == 42).next(); // iterator is not exhausted
++}
++
++fn main() {
++    infinite_iters();
++    potential_infinite_iters();
++}
++
++mod finite_collect {
++    use std::collections::HashSet;
++    use std::iter::FromIterator;
++
++    struct C;
++    impl FromIterator<i32> for C {
++        fn from_iter<I: IntoIterator<Item = i32>>(iter: I) -> Self {
++            C
++        }
++    }
++
++    fn check_collect() {
++        let _: HashSet<i32> = (0..).collect(); // Infinite iter
++
++        // Some data structures don't collect infinitely, such as `ArrayVec`
++        let _: C = (0..).collect();
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5f5e7ac9f253a0e0589dec0a662495f5747d2bb4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,109 @@@
++error: infinite iteration detected
++  --> $DIR/infinite_iter.rs:9:5
++   |
++LL |     repeat(0_u8).collect::<Vec<_>>(); // infinite iter
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++note: the lint level is defined here
++  --> $DIR/infinite_iter.rs:7:8
++   |
++LL | #[deny(clippy::infinite_iter)]
++   |        ^^^^^^^^^^^^^^^^^^^^^
++
++error: infinite iteration detected
++  --> $DIR/infinite_iter.rs:10:5
++   |
++LL |     (0..8_u32).take_while(square_is_lower_64).cycle().count(); // infinite iter
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: infinite iteration detected
++  --> $DIR/infinite_iter.rs:11:5
++   |
++LL |     (0..8_u64).chain(0..).max(); // infinite iter
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: infinite iteration detected
++  --> $DIR/infinite_iter.rs:16:5
++   |
++LL | /     (0..8_u32)
++LL | |         .rev()
++LL | |         .cycle()
++LL | |         .map(|x| x + 1_u32)
++LL | |         .for_each(|x| println!("{}", x)); // infinite iter
++   | |________________________________________^
++
++error: infinite iteration detected
++  --> $DIR/infinite_iter.rs:22:5
++   |
++LL |     (0_usize..).flat_map(|x| 0..x).product::<usize>(); // infinite iter
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: infinite iteration detected
++  --> $DIR/infinite_iter.rs:23:5
++   |
++LL |     (0_u64..).filter(|x| x % 2 == 0).last(); // infinite iter
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: possible infinite iteration detected
++  --> $DIR/infinite_iter.rs:30:5
++   |
++LL |     (0..).zip((0..).take_while(square_is_lower_64)).count(); // maybe infinite iter
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++note: the lint level is defined here
++  --> $DIR/infinite_iter.rs:28:8
++   |
++LL | #[deny(clippy::maybe_infinite_iter)]
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: possible infinite iteration detected
++  --> $DIR/infinite_iter.rs:31:5
++   |
++LL |     repeat(42).take_while(|x| *x == 42).chain(0..42).max(); // maybe infinite iter
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: possible infinite iteration detected
++  --> $DIR/infinite_iter.rs:32:5
++   |
++LL | /     (1..)
++LL | |         .scan(0, |state, x| {
++LL | |             *state += x;
++LL | |             Some(*state)
++LL | |         })
++LL | |         .min(); // maybe infinite iter
++   | |______________^
++
++error: possible infinite iteration detected
++  --> $DIR/infinite_iter.rs:38:5
++   |
++LL |     (0..).find(|x| *x == 24); // maybe infinite iter
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: possible infinite iteration detected
++  --> $DIR/infinite_iter.rs:39:5
++   |
++LL |     (0..).position(|x| x == 24); // maybe infinite iter
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: possible infinite iteration detected
++  --> $DIR/infinite_iter.rs:40:5
++   |
++LL |     (0..).any(|x| x == 24); // maybe infinite iter
++   |     ^^^^^^^^^^^^^^^^^^^^^^
++
++error: possible infinite iteration detected
++  --> $DIR/infinite_iter.rs:41:5
++   |
++LL |     (0..).all(|x| x == 24); // maybe infinite iter
++   |     ^^^^^^^^^^^^^^^^^^^^^^
++
++error: infinite iteration detected
++  --> $DIR/infinite_iter.rs:64:31
++   |
++LL |         let _: HashSet<i32> = (0..).collect(); // Infinite iter
++   |                               ^^^^^^^^^^^^^^^
++   |
++   = note: `#[deny(clippy::infinite_iter)]` on by default
++
++error: aborting due to 14 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..72591f12baf852811efbc511a1ee7cf965929fe7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,206 @@@
++fn fn_val(i: i32) -> i32 {
++    unimplemented!()
++}
++fn fn_constref(i: &i32) -> i32 {
++    unimplemented!()
++}
++fn fn_mutref(i: &mut i32) {
++    unimplemented!()
++}
++fn fooi() -> i32 {
++    unimplemented!()
++}
++fn foob() -> bool {
++    unimplemented!()
++}
++
++#[allow(clippy::many_single_char_names)]
++fn immutable_condition() {
++    // Should warn when all vars mentioned are immutable
++    let y = 0;
++    while y < 10 {
++        println!("KO - y is immutable");
++    }
++
++    let x = 0;
++    while y < 10 && x < 3 {
++        let mut k = 1;
++        k += 2;
++        println!("KO - x and y immutable");
++    }
++
++    let cond = false;
++    while !cond {
++        println!("KO - cond immutable");
++    }
++
++    let mut i = 0;
++    while y < 10 && i < 3 {
++        i += 1;
++        println!("OK - i is mutable");
++    }
++
++    let mut mut_cond = false;
++    while !mut_cond || cond {
++        mut_cond = true;
++        println!("OK - mut_cond is mutable");
++    }
++
++    while fooi() < x {
++        println!("OK - Fn call results may vary");
++    }
++
++    while foob() {
++        println!("OK - Fn call results may vary");
++    }
++
++    let mut a = 0;
++    let mut c = move || {
++        while a < 5 {
++            a += 1;
++            println!("OK - a is mutable");
++        }
++    };
++    c();
++
++    let mut tup = (0, 0);
++    while tup.0 < 5 {
++        tup.0 += 1;
++        println!("OK - tup.0 gets mutated")
++    }
++}
++
++fn unused_var() {
++    // Should warn when a (mutable) var is not used in while body
++    let (mut i, mut j) = (0, 0);
++
++    while i < 3 {
++        j = 3;
++        println!("KO - i not mentioned");
++    }
++
++    while i < 3 && j > 0 {
++        println!("KO - i and j not mentioned");
++    }
++
++    while i < 3 {
++        let mut i = 5;
++        fn_mutref(&mut i);
++        println!("KO - shadowed");
++    }
++
++    while i < 3 && j > 0 {
++        i = 5;
++        println!("OK - i in cond and mentioned");
++    }
++}
++
++fn used_immutable() {
++    let mut i = 0;
++
++    while i < 3 {
++        fn_constref(&i);
++        println!("KO - const reference");
++    }
++
++    while i < 3 {
++        fn_val(i);
++        println!("KO - passed by value");
++    }
++
++    while i < 3 {
++        println!("OK - passed by mutable reference");
++        fn_mutref(&mut i)
++    }
++
++    while i < 3 {
++        fn_mutref(&mut i);
++        println!("OK - passed by mutable reference");
++    }
++}
++
++const N: i32 = 5;
++const B: bool = false;
++
++fn consts() {
++    while false {
++        println!("Constants are not linted");
++    }
++
++    while B {
++        println!("Constants are not linted");
++    }
++
++    while N > 0 {
++        println!("Constants are not linted");
++    }
++}
++
++use std::cell::Cell;
++
++fn maybe_i_mutate(i: &Cell<bool>) {
++    unimplemented!()
++}
++
++fn internally_mutable() {
++    let b = Cell::new(true);
++
++    while b.get() {
++        // b cannot be silently coerced to `bool`
++        maybe_i_mutate(&b);
++        println!("OK - Method call within condition");
++    }
++}
++
++struct Counter {
++    count: usize,
++}
++
++impl Counter {
++    fn inc(&mut self) {
++        self.count += 1;
++    }
++
++    fn inc_n(&mut self, n: usize) {
++        while self.count < n {
++            self.inc();
++        }
++        println!("OK - self borrowed mutably");
++    }
++
++    fn print_n(&self, n: usize) {
++        while self.count < n {
++            println!("KO - {} is not mutated", self.count);
++        }
++    }
++}
++
++fn while_loop_with_break_and_return() {
++    let y = 0;
++    while y < 10 {
++        if y == 0 {
++            break;
++        }
++        println!("KO - loop contains break");
++    }
++
++    while y < 10 {
++        if y == 0 {
++            return;
++        }
++        println!("KO - loop contains return");
++    }
++}
++
++fn main() {
++    immutable_condition();
++    unused_var();
++    used_immutable();
++    internally_mutable();
++
++    let mut c = Counter { count: 0 };
++    c.inc_n(5);
++    c.print_n(2);
++
++    while_loop_with_break_and_return();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1fcb29eff18e41c05d34873d57bfe2ab77dd7f48
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,95 @@@
++error: variables in the condition are not mutated in the loop body
++  --> $DIR/infinite_loop.rs:21:11
++   |
++LL |     while y < 10 {
++   |           ^^^^^^
++   |
++   = note: `#[deny(clippy::while_immutable_condition)]` on by default
++   = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++  --> $DIR/infinite_loop.rs:26:11
++   |
++LL |     while y < 10 && x < 3 {
++   |           ^^^^^^^^^^^^^^^
++   |
++   = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++  --> $DIR/infinite_loop.rs:33:11
++   |
++LL |     while !cond {
++   |           ^^^^^
++   |
++   = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++  --> $DIR/infinite_loop.rs:77:11
++   |
++LL |     while i < 3 {
++   |           ^^^^^
++   |
++   = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++  --> $DIR/infinite_loop.rs:82:11
++   |
++LL |     while i < 3 && j > 0 {
++   |           ^^^^^^^^^^^^^^
++   |
++   = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++  --> $DIR/infinite_loop.rs:86:11
++   |
++LL |     while i < 3 {
++   |           ^^^^^
++   |
++   = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++  --> $DIR/infinite_loop.rs:101:11
++   |
++LL |     while i < 3 {
++   |           ^^^^^
++   |
++   = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++  --> $DIR/infinite_loop.rs:106:11
++   |
++LL |     while i < 3 {
++   |           ^^^^^
++   |
++   = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++  --> $DIR/infinite_loop.rs:172:15
++   |
++LL |         while self.count < n {
++   |               ^^^^^^^^^^^^^^
++   |
++   = note: this may lead to an infinite or to a never running loop
++
++error: variables in the condition are not mutated in the loop body
++  --> $DIR/infinite_loop.rs:180:11
++   |
++LL |     while y < 10 {
++   |           ^^^^^^
++   |
++   = note: this may lead to an infinite or to a never running loop
++   = note: this loop contains `return`s or `break`s
++   = help: rewrite it as `if cond { loop { } }`
++
++error: variables in the condition are not mutated in the loop body
++  --> $DIR/infinite_loop.rs:187:11
++   |
++LL |     while y < 10 {
++   |           ^^^^^^
++   |
++   = note: this may lead to an infinite or to a never running loop
++   = note: this loop contains `return`s or `break`s
++   = help: rewrite it as `if cond { loop { } }`
++
++error: aborting due to 11 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e6cf337d1bb1baa932b13f3665cdc8d7764395ee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,96 @@@
++#![warn(clippy::inherent_to_string)]
++#![deny(clippy::inherent_to_string_shadow_display)]
++#![allow(clippy::many_single_char_names)]
++
++use std::fmt;
++
++trait FalsePositive {
++    fn to_string(&self) -> String;
++}
++
++struct A;
++struct B;
++struct C;
++struct D;
++struct E;
++struct F;
++
++impl A {
++    // Should be detected; emit warning
++    fn to_string(&self) -> String {
++        "A.to_string()".to_string()
++    }
++
++    // Should not be detected as it does not match the function signature
++    fn to_str(&self) -> String {
++        "A.to_str()".to_string()
++    }
++}
++
++// Should not be detected as it is a free function
++fn to_string() -> String {
++    "free to_string()".to_string()
++}
++
++impl B {
++    // Should not be detected, wrong return type
++    fn to_string(&self) -> i32 {
++        42
++    }
++}
++
++impl C {
++    // Should be detected and emit error as C also implements Display
++    fn to_string(&self) -> String {
++        "C.to_string()".to_string()
++    }
++}
++
++impl fmt::Display for C {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        write!(f, "impl Display for C")
++    }
++}
++
++impl FalsePositive for D {
++    // Should not be detected, as it is a trait function
++    fn to_string(&self) -> String {
++        "impl FalsePositive for D".to_string()
++    }
++}
++
++impl E {
++    // Should not be detected, as it is not bound to an instance
++    fn to_string() -> String {
++        "E::to_string()".to_string()
++    }
++}
++
++impl F {
++    // Should not be detected, as it does not match the function signature
++    fn to_string(&self, _i: i32) -> String {
++        "F.to_string()".to_string()
++    }
++}
++
++fn main() {
++    let a = A;
++    a.to_string();
++    a.to_str();
++
++    to_string();
++
++    let b = B;
++    b.to_string();
++
++    let c = C;
++    C.to_string();
++
++    let d = D;
++    d.to_string();
++
++    E::to_string();
++
++    let f = F;
++    f.to_string(1);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4f331f5bec9e6fe87073d1cdb63991862db4bf28
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: implementation of inherent method `to_string(&self) -> String` for type `A`
++  --> $DIR/inherent_to_string.rs:20:5
++   |
++LL | /     fn to_string(&self) -> String {
++LL | |         "A.to_string()".to_string()
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::inherent-to-string` implied by `-D warnings`
++   = help: implement trait `Display` for type `A` instead
++
++error: type `C` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`
++  --> $DIR/inherent_to_string.rs:44:5
++   |
++LL | /     fn to_string(&self) -> String {
++LL | |         "C.to_string()".to_string()
++LL | |     }
++   | |_____^
++   |
++note: the lint level is defined here
++  --> $DIR/inherent_to_string.rs:2:9
++   |
++LL | #![deny(clippy::inherent_to_string_shadow_display)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   = help: remove the inherent method from type `C`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fe21a71a42c2648f650bf612b37210e3b7e8f59c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++// run-rustfix
++
++#![warn(clippy::inline_fn_without_body)]
++#![allow(clippy::inline_always)]
++
++trait Foo {
++    fn default_inline();
++
++    fn always_inline();
++
++    fn never_inline();
++
++    #[inline]
++    fn has_body() {}
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..50746989466504118e3633aaf79c06fa3dd9ee4e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++// run-rustfix
++
++#![warn(clippy::inline_fn_without_body)]
++#![allow(clippy::inline_always)]
++
++trait Foo {
++    #[inline]
++    fn default_inline();
++
++    #[inline(always)]
++    fn always_inline();
++
++    #[inline(never)]
++    fn never_inline();
++
++    #[inline]
++    fn has_body() {}
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..32d35e209b01baf52ac56968a94ef8019d98bb4c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: use of `#[inline]` on trait method `default_inline` which has no body
++  --> $DIR/inline_fn_without_body.rs:7:5
++   |
++LL |       #[inline]
++   |  _____-^^^^^^^^
++LL | |     fn default_inline();
++   | |____- help: remove
++   |
++   = note: `-D clippy::inline-fn-without-body` implied by `-D warnings`
++
++error: use of `#[inline]` on trait method `always_inline` which has no body
++  --> $DIR/inline_fn_without_body.rs:10:5
++   |
++LL |       #[inline(always)]
++   |  _____-^^^^^^^^^^^^^^^^
++LL | |     fn always_inline();
++   | |____- help: remove
++
++error: use of `#[inline]` on trait method `never_inline` which has no body
++  --> $DIR/inline_fn_without_body.rs:13:5
++   |
++LL |       #[inline(never)]
++   |  _____-^^^^^^^^^^^^^^^
++LL | |     fn never_inline();
++   | |____- help: remove
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..642830f24f58adbb4effca6d619bce3c6f2298c0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++// run-rustfix
++
++#[allow(clippy::no_effect, clippy::unnecessary_operation)]
++#[warn(clippy::int_plus_one)]
++fn main() {
++    let x = 1i32;
++    let y = 0i32;
++
++    let _ = x > y;
++    let _ = y < x;
++
++    let _ = x > y;
++    let _ = y < x;
++
++    let _ = x > y; // should be ok
++    let _ = y < x; // should be ok
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0755a0c79d2801eb50e1f372fd4ff1aaf71d8f69
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++// run-rustfix
++
++#[allow(clippy::no_effect, clippy::unnecessary_operation)]
++#[warn(clippy::int_plus_one)]
++fn main() {
++    let x = 1i32;
++    let y = 0i32;
++
++    let _ = x >= y + 1;
++    let _ = y + 1 <= x;
++
++    let _ = x - 1 >= y;
++    let _ = y <= x - 1;
++
++    let _ = x > y; // should be ok
++    let _ = y < x; // should be ok
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..29a6914761c9f92c319918bbee76403312ad2724
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: Unnecessary `>= y + 1` or `x - 1 >=`
++  --> $DIR/int_plus_one.rs:9:13
++   |
++LL |     let _ = x >= y + 1;
++   |             ^^^^^^^^^^ help: change it to: `x > y`
++   |
++   = note: `-D clippy::int-plus-one` implied by `-D warnings`
++
++error: Unnecessary `>= y + 1` or `x - 1 >=`
++  --> $DIR/int_plus_one.rs:10:13
++   |
++LL |     let _ = y + 1 <= x;
++   |             ^^^^^^^^^^ help: change it to: `y < x`
++
++error: Unnecessary `>= y + 1` or `x - 1 >=`
++  --> $DIR/int_plus_one.rs:12:13
++   |
++LL |     let _ = x - 1 >= y;
++   |             ^^^^^^^^^^ help: change it to: `x > y`
++
++error: Unnecessary `>= y + 1` or `x - 1 >=`
++  --> $DIR/int_plus_one.rs:13:13
++   |
++LL |     let _ = y <= x - 1;
++   |             ^^^^^^^^^^ help: change it to: `y < x`
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7b1b64f390a5aa25a5f4039761dab8b8dc832e54
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,99 @@@
++#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)]
++#![allow(
++    unused,
++    clippy::shadow_reuse,
++    clippy::shadow_unrelated,
++    clippy::no_effect,
++    clippy::unnecessary_operation,
++    clippy::op_ref
++)]
++
++#[rustfmt::skip]
++fn main() {
++    let mut i = 1i32;
++    1 + i;
++    i * 2;
++    1 %
++    i / 2; // no error, this is part of the expression in the preceding line
++    i - 2 + 2 - i;
++    -i;
++    i >> 1;
++    i << 1;
++
++    // no error, overflows are checked by `overflowing_literals`
++    -1;
++    -(-1);
++
++    i & 1; // no wrapping
++    i | 1;
++    i ^ 1;
++
++    i += 1;
++    i -= 1;
++    i *= 2;
++    i /= 2;
++    i %= 2;
++    i <<= 3;
++    i >>= 2;
++
++    // no errors
++    i |= 1;
++    i &= 1;
++    i ^= i;
++
++    // No errors for the following items because they are constant expressions
++    enum Foo {
++        Bar = -2,
++    }
++    struct Baz([i32; 1 + 1]);
++    union Qux {
++        field: [i32; 1 + 1],
++    }
++    type Alias = [i32; 1 + 1];
++
++    const FOO: i32 = -2;
++    static BAR: i32 = -2;
++
++    let _: [i32; 1 + 1] = [0, 0];
++
++    let _: [i32; 1 + 1] = {
++        let a: [i32; 1 + 1] = [0, 0];
++        a
++    };
++
++    trait Trait {
++        const ASSOC: i32 = 1 + 1;
++    }
++
++    impl Trait for Foo {
++        const ASSOC: i32 = {
++            let _: [i32; 1 + 1];
++            fn foo() {}
++            1 + 1
++        };
++    }
++}
++
++// warn on references as well! (#5328)
++pub fn int_arith_ref() {
++    3 + &1;
++    &3 + 1;
++    &3 + &1;
++}
++
++pub fn foo(x: &i32) -> i32 {
++    let a = 5;
++    a + x
++}
++
++pub fn bar(x: &i32, y: &i32) -> i32 {
++    x + y
++}
++
++pub fn baz(x: i32, y: &i32) -> i32 {
++    x + y
++}
++
++pub fn qux(x: i32, y: i32) -> i32 {
++    (&x + &y)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..83e8a9cde3ff11873f6cbb715af32565f9461ed5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,131 @@@
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:14:5
++   |
++LL |     1 + i;
++   |     ^^^^^
++   |
++   = note: `-D clippy::integer-arithmetic` implied by `-D warnings`
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:15:5
++   |
++LL |     i * 2;
++   |     ^^^^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:16:5
++   |
++LL | /     1 %
++LL | |     i / 2; // no error, this is part of the expression in the preceding line
++   | |_________^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:18:5
++   |
++LL |     i - 2 + 2 - i;
++   |     ^^^^^^^^^^^^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:19:5
++   |
++LL |     -i;
++   |     ^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:20:5
++   |
++LL |     i >> 1;
++   |     ^^^^^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:21:5
++   |
++LL |     i << 1;
++   |     ^^^^^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:31:5
++   |
++LL |     i += 1;
++   |     ^^^^^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:32:5
++   |
++LL |     i -= 1;
++   |     ^^^^^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:33:5
++   |
++LL |     i *= 2;
++   |     ^^^^^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:34:5
++   |
++LL |     i /= 2;
++   |     ^^^^^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:35:5
++   |
++LL |     i %= 2;
++   |     ^^^^^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:36:5
++   |
++LL |     i <<= 3;
++   |     ^^^^^^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:37:5
++   |
++LL |     i >>= 2;
++   |     ^^^^^^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:79:5
++   |
++LL |     3 + &1;
++   |     ^^^^^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:80:5
++   |
++LL |     &3 + 1;
++   |     ^^^^^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:81:5
++   |
++LL |     &3 + &1;
++   |     ^^^^^^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:86:5
++   |
++LL |     a + x
++   |     ^^^^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:90:5
++   |
++LL |     x + y
++   |     ^^^^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:94:5
++   |
++LL |     x + y
++   |     ^^^^^
++
++error: integer arithmetic detected
++  --> $DIR/integer_arithmetic.rs:98:5
++   |
++LL |     (&x + &y)
++   |     ^^^^^^^^^
++
++error: aborting due to 21 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..800c75257524a542d014192b94c74496cea4f322
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++#![warn(clippy::integer_division)]
++
++fn main() {
++    let two = 2;
++    let n = 1 / 2;
++    let o = 1 / two;
++    let p = two / 4;
++    let x = 1. / 2.0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..72a232ef3d75085ecddb056be72f645f21d67d3f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++error: integer division
++  --> $DIR/integer_division.rs:5:13
++   |
++LL |     let n = 1 / 2;
++   |             ^^^^^
++   |
++   = note: `-D clippy::integer-division` implied by `-D warnings`
++   = help: division of integers may cause loss of precision. consider using floats.
++
++error: integer division
++  --> $DIR/integer_division.rs:6:13
++   |
++LL |     let o = 1 / two;
++   |             ^^^^^^^
++   |
++   = help: division of integers may cause loss of precision. consider using floats.
++
++error: integer division
++  --> $DIR/integer_division.rs:7:13
++   |
++LL |     let p = two / 4;
++   |             ^^^^^^^
++   |
++   = help: division of integers may cause loss of precision. consider using floats.
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c30d23de3f86921a891dbce97af00cea427cb71d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++// run-rustfix
++#![allow(clippy::useless_vec)]
++#![warn(clippy::into_iter_on_ref)]
++
++struct X;
++use std::collections::*;
++
++fn main() {
++    for _ in &[1, 2, 3] {}
++    for _ in vec![X, X] {}
++    for _ in &vec![X, X] {}
++
++    let _ = vec![1, 2, 3].into_iter();
++    let _ = (&vec![1, 2, 3]).iter(); //~ WARN equivalent to .iter()
++    let _ = vec![1, 2, 3].into_boxed_slice().iter(); //~ WARN equivalent to .iter()
++    let _ = std::rc::Rc::from(&[X][..]).iter(); //~ WARN equivalent to .iter()
++    let _ = std::sync::Arc::from(&[X][..]).iter(); //~ WARN equivalent to .iter()
++
++    let _ = (&&&&&&&[1, 2, 3]).iter(); //~ ERROR equivalent to .iter()
++    let _ = (&&&&mut &&&[1, 2, 3]).iter(); //~ ERROR equivalent to .iter()
++    let _ = (&mut &mut &mut [1, 2, 3]).iter_mut(); //~ ERROR equivalent to .iter_mut()
++
++    let _ = (&Some(4)).iter(); //~ WARN equivalent to .iter()
++    let _ = (&mut Some(5)).iter_mut(); //~ WARN equivalent to .iter_mut()
++    let _ = (&Ok::<_, i32>(6)).iter(); //~ WARN equivalent to .iter()
++    let _ = (&mut Err::<i32, _>(7)).iter_mut(); //~ WARN equivalent to .iter_mut()
++    let _ = (&Vec::<i32>::new()).iter(); //~ WARN equivalent to .iter()
++    let _ = (&mut Vec::<i32>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
++    let _ = (&BTreeMap::<i32, u64>::new()).iter(); //~ WARN equivalent to .iter()
++    let _ = (&mut BTreeMap::<i32, u64>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
++    let _ = (&VecDeque::<i32>::new()).iter(); //~ WARN equivalent to .iter()
++    let _ = (&mut VecDeque::<i32>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
++    let _ = (&LinkedList::<i32>::new()).iter(); //~ WARN equivalent to .iter()
++    let _ = (&mut LinkedList::<i32>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
++    let _ = (&HashMap::<i32, u64>::new()).iter(); //~ WARN equivalent to .iter()
++    let _ = (&mut HashMap::<i32, u64>::new()).iter_mut(); //~ WARN equivalent to .iter_mut()
++
++    let _ = (&BTreeSet::<i32>::new()).iter(); //~ WARN equivalent to .iter()
++    let _ = (&BinaryHeap::<i32>::new()).iter(); //~ WARN equivalent to .iter()
++    let _ = (&HashSet::<i32>::new()).iter(); //~ WARN equivalent to .iter()
++    let _ = std::path::Path::new("12/34").iter(); //~ WARN equivalent to .iter()
++    let _ = std::path::PathBuf::from("12/34").iter(); //~ ERROR equivalent to .iter()
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..94bc1689619a297b84116d0efebcc14c7e74f5e1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++// run-rustfix
++#![allow(clippy::useless_vec)]
++#![warn(clippy::into_iter_on_ref)]
++
++struct X;
++use std::collections::*;
++
++fn main() {
++    for _ in &[1, 2, 3] {}
++    for _ in vec![X, X] {}
++    for _ in &vec![X, X] {}
++
++    let _ = vec![1, 2, 3].into_iter();
++    let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter()
++    let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter()
++    let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
++    let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
++
++    let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
++    let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
++    let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter_mut()
++
++    let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter()
++    let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut()
++    let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter()
++    let _ = (&mut Err::<i32, _>(7)).into_iter(); //~ WARN equivalent to .iter_mut()
++    let _ = (&Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++    let _ = (&mut Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++    let _ = (&BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
++    let _ = (&mut BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++    let _ = (&VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++    let _ = (&mut VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++    let _ = (&LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++    let _ = (&mut LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++    let _ = (&HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
++    let _ = (&mut HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++
++    let _ = (&BTreeSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++    let _ = (&BinaryHeap::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++    let _ = (&HashSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++    let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter()
++    let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter()
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..80e2d104f824f440461fd5f3b039bc42eeae2aca
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,160 @@@
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec`
++  --> $DIR/into_iter_on_ref.rs:14:30
++   |
++LL |     let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter()
++   |                              ^^^^^^^^^ help: call directly: `iter`
++   |
++   = note: `-D clippy::into-iter-on-ref` implied by `-D warnings`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice`
++  --> $DIR/into_iter_on_ref.rs:15:46
++   |
++LL |     let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter()
++   |                                              ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice`
++  --> $DIR/into_iter_on_ref.rs:16:41
++   |
++LL |     let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
++   |                                         ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice`
++  --> $DIR/into_iter_on_ref.rs:17:44
++   |
++LL |     let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
++   |                                            ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array`
++  --> $DIR/into_iter_on_ref.rs:19:32
++   |
++LL |     let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
++   |                                ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array`
++  --> $DIR/into_iter_on_ref.rs:20:36
++   |
++LL |     let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
++   |                                    ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `array`
++  --> $DIR/into_iter_on_ref.rs:21:40
++   |
++LL |     let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter_mut()
++   |                                        ^^^^^^^^^ help: call directly: `iter_mut`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Option`
++  --> $DIR/into_iter_on_ref.rs:23:24
++   |
++LL |     let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter()
++   |                        ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Option`
++  --> $DIR/into_iter_on_ref.rs:24:28
++   |
++LL |     let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut()
++   |                            ^^^^^^^^^ help: call directly: `iter_mut`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Result`
++  --> $DIR/into_iter_on_ref.rs:25:32
++   |
++LL |     let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter()
++   |                                ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Result`
++  --> $DIR/into_iter_on_ref.rs:26:37
++   |
++LL |     let _ = (&mut Err::<i32, _>(7)).into_iter(); //~ WARN equivalent to .iter_mut()
++   |                                     ^^^^^^^^^ help: call directly: `iter_mut`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec`
++  --> $DIR/into_iter_on_ref.rs:27:34
++   |
++LL |     let _ = (&Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++   |                                  ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Vec`
++  --> $DIR/into_iter_on_ref.rs:28:38
++   |
++LL |     let _ = (&mut Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++   |                                      ^^^^^^^^^ help: call directly: `iter_mut`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeMap`
++  --> $DIR/into_iter_on_ref.rs:29:44
++   |
++LL |     let _ = (&BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
++   |                                            ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `BTreeMap`
++  --> $DIR/into_iter_on_ref.rs:30:48
++   |
++LL |     let _ = (&mut BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++   |                                                ^^^^^^^^^ help: call directly: `iter_mut`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `VecDeque`
++  --> $DIR/into_iter_on_ref.rs:31:39
++   |
++LL |     let _ = (&VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++   |                                       ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `VecDeque`
++  --> $DIR/into_iter_on_ref.rs:32:43
++   |
++LL |     let _ = (&mut VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++   |                                           ^^^^^^^^^ help: call directly: `iter_mut`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `LinkedList`
++  --> $DIR/into_iter_on_ref.rs:33:41
++   |
++LL |     let _ = (&LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++   |                                         ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `LinkedList`
++  --> $DIR/into_iter_on_ref.rs:34:45
++   |
++LL |     let _ = (&mut LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++   |                                             ^^^^^^^^^ help: call directly: `iter_mut`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashMap`
++  --> $DIR/into_iter_on_ref.rs:35:43
++   |
++LL |     let _ = (&HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
++   |                                           ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `HashMap`
++  --> $DIR/into_iter_on_ref.rs:36:47
++   |
++LL |     let _ = (&mut HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
++   |                                               ^^^^^^^^^ help: call directly: `iter_mut`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeSet`
++  --> $DIR/into_iter_on_ref.rs:38:39
++   |
++LL |     let _ = (&BTreeSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++   |                                       ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BinaryHeap`
++  --> $DIR/into_iter_on_ref.rs:39:41
++   |
++LL |     let _ = (&BinaryHeap::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++   |                                         ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashSet`
++  --> $DIR/into_iter_on_ref.rs:40:38
++   |
++LL |     let _ = (&HashSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
++   |                                      ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Path`
++  --> $DIR/into_iter_on_ref.rs:41:43
++   |
++LL |     let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter()
++   |                                           ^^^^^^^^^ help: call directly: `iter`
++
++error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `PathBuf`
++  --> $DIR/into_iter_on_ref.rs:42:47
++   |
++LL |     let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter()
++   |                                               ^^^^^^^^^ help: call directly: `iter`
++
++error: aborting due to 26 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..697416dcee83188c28c87a18489ba261b551c39c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,85 @@@
++#![warn(clippy::invalid_upcast_comparisons)]
++#![allow(
++    unused,
++    clippy::eq_op,
++    clippy::no_effect,
++    clippy::unnecessary_operation,
++    clippy::cast_lossless
++)]
++
++fn mk_value<T>() -> T {
++    unimplemented!()
++}
++
++fn main() {
++    let u32: u32 = mk_value();
++    let u8: u8 = mk_value();
++    let i32: i32 = mk_value();
++    let i8: i8 = mk_value();
++
++    // always false, since no u8 can be > 300
++    (u8 as u32) > 300;
++    (u8 as i32) > 300;
++    (u8 as u32) == 300;
++    (u8 as i32) == 300;
++    300 < (u8 as u32);
++    300 < (u8 as i32);
++    300 == (u8 as u32);
++    300 == (u8 as i32);
++    // inverted of the above
++    (u8 as u32) <= 300;
++    (u8 as i32) <= 300;
++    (u8 as u32) != 300;
++    (u8 as i32) != 300;
++    300 >= (u8 as u32);
++    300 >= (u8 as i32);
++    300 != (u8 as u32);
++    300 != (u8 as i32);
++
++    // always false, since u8 -> i32 doesn't wrap
++    (u8 as i32) < 0;
++    -5 != (u8 as i32);
++    // inverted of the above
++    (u8 as i32) >= 0;
++    -5 == (u8 as i32);
++
++    // always false, since no u8 can be 1337
++    1337 == (u8 as i32);
++    1337 == (u8 as u32);
++    // inverted of the above
++    1337 != (u8 as i32);
++    1337 != (u8 as u32);
++
++    // Those are Ok:
++    (u8 as u32) > 20;
++    42 == (u8 as i32);
++    42 != (u8 as i32);
++    42 > (u8 as i32);
++    (u8 as i32) == 42;
++    (u8 as i32) != 42;
++    (u8 as i32) > 42;
++    (u8 as i32) < 42;
++
++    (u8 as i8) == -1;
++    (u8 as i8) != -1;
++    (u8 as i32) > -1;
++    (u8 as i32) < -1;
++    (u32 as i32) < -5;
++    (u32 as i32) < 10;
++
++    (i8 as u8) == 1;
++    (i8 as u8) != 1;
++    (i8 as u8) < 1;
++    (i8 as u8) > 1;
++    (i32 as u32) < 5;
++    (i32 as u32) < 10;
++
++    -5 < (u32 as i32);
++    0 <= (u32 as i32);
++    0 < (u32 as i32);
++
++    -5 > (u32 as i32);
++    -5 >= (u8 as i32);
++
++    -5 == (u32 as i32);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..03c3fb80aaabcc1f5adbee634ad8bf256de77b4e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,166 @@@
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++  --> $DIR/invalid_upcast_comparisons.rs:21:5
++   |
++LL |     (u8 as u32) > 300;
++   |     ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::invalid-upcast-comparisons` implied by `-D warnings`
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++  --> $DIR/invalid_upcast_comparisons.rs:22:5
++   |
++LL |     (u8 as i32) > 300;
++   |     ^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++  --> $DIR/invalid_upcast_comparisons.rs:23:5
++   |
++LL |     (u8 as u32) == 300;
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++  --> $DIR/invalid_upcast_comparisons.rs:24:5
++   |
++LL |     (u8 as i32) == 300;
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++  --> $DIR/invalid_upcast_comparisons.rs:25:5
++   |
++LL |     300 < (u8 as u32);
++   |     ^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++  --> $DIR/invalid_upcast_comparisons.rs:26:5
++   |
++LL |     300 < (u8 as i32);
++   |     ^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++  --> $DIR/invalid_upcast_comparisons.rs:27:5
++   |
++LL |     300 == (u8 as u32);
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++  --> $DIR/invalid_upcast_comparisons.rs:28:5
++   |
++LL |     300 == (u8 as i32);
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++  --> $DIR/invalid_upcast_comparisons.rs:30:5
++   |
++LL |     (u8 as u32) <= 300;
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++  --> $DIR/invalid_upcast_comparisons.rs:31:5
++   |
++LL |     (u8 as i32) <= 300;
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++  --> $DIR/invalid_upcast_comparisons.rs:32:5
++   |
++LL |     (u8 as u32) != 300;
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++  --> $DIR/invalid_upcast_comparisons.rs:33:5
++   |
++LL |     (u8 as i32) != 300;
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++  --> $DIR/invalid_upcast_comparisons.rs:34:5
++   |
++LL |     300 >= (u8 as u32);
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++  --> $DIR/invalid_upcast_comparisons.rs:35:5
++   |
++LL |     300 >= (u8 as i32);
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++  --> $DIR/invalid_upcast_comparisons.rs:36:5
++   |
++LL |     300 != (u8 as u32);
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++  --> $DIR/invalid_upcast_comparisons.rs:37:5
++   |
++LL |     300 != (u8 as i32);
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++  --> $DIR/invalid_upcast_comparisons.rs:40:5
++   |
++LL |     (u8 as i32) < 0;
++   |     ^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++  --> $DIR/invalid_upcast_comparisons.rs:41:5
++   |
++LL |     -5 != (u8 as i32);
++   |     ^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++  --> $DIR/invalid_upcast_comparisons.rs:43:5
++   |
++LL |     (u8 as i32) >= 0;
++   |     ^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++  --> $DIR/invalid_upcast_comparisons.rs:44:5
++   |
++LL |     -5 == (u8 as i32);
++   |     ^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++  --> $DIR/invalid_upcast_comparisons.rs:47:5
++   |
++LL |     1337 == (u8 as i32);
++   |     ^^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++  --> $DIR/invalid_upcast_comparisons.rs:48:5
++   |
++LL |     1337 == (u8 as u32);
++   |     ^^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++  --> $DIR/invalid_upcast_comparisons.rs:50:5
++   |
++LL |     1337 != (u8 as i32);
++   |     ^^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++  --> $DIR/invalid_upcast_comparisons.rs:51:5
++   |
++LL |     1337 != (u8 as u32);
++   |     ^^^^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always true
++  --> $DIR/invalid_upcast_comparisons.rs:65:5
++   |
++LL |     (u8 as i32) > -1;
++   |     ^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++  --> $DIR/invalid_upcast_comparisons.rs:66:5
++   |
++LL |     (u8 as i32) < -1;
++   |     ^^^^^^^^^^^^^^^^
++
++error: because of the numeric bounds on `u8` prior to casting, this expression is always false
++  --> $DIR/invalid_upcast_comparisons.rs:82:5
++   |
++LL |     -5 >= (u8 as i32);
++   |     ^^^^^^^^^^^^^^^^^
++
++error: aborting due to 27 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f497d5550af5f9cb9e6fdcafb18557db5a022b2a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++fn main() {
++    println!("{}" a); //~ERROR expected token: `,`
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cb0d95f5e2643abab2cce9fb77323c2acd0c3778
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++error: expected token: `,`
++  --> $DIR/issue-3145.rs:2:19
++   |
++LL |     println!("{}" a); //~ERROR expected token: `,`
++   |                   ^ expected `,`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..879d1d5d916e439bf219fef4e343ab0320c13d51
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++// ignore-macos
++// ignore-windows
++
++#![warn(clippy::empty_loop)]
++#![feature(lang_items, link_args, start, libc)]
++#![link_args = "-nostartfiles"]
++#![no_std]
++
++use core::panic::PanicInfo;
++
++#[start]
++fn main(argc: isize, argv: *const *const u8) -> isize {
++    loop {}
++}
++
++#[panic_handler]
++fn panic(_info: &PanicInfo) -> ! {
++    loop {}
++}
++
++#[lang = "eh_personality"]
++extern "C" fn eh_personality() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..da580a1839a176b4bf05325302a26c928a16e732
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++#![deny(clippy::while_let_on_iterator)]
++
++use std::iter::Iterator;
++
++struct Foo;
++
++impl Foo {
++    fn foo1<I: Iterator<Item = usize>>(mut it: I) {
++        while let Some(_) = it.next() {
++            println!("{:?}", it.size_hint());
++        }
++    }
++
++    fn foo2<I: Iterator<Item = usize>>(mut it: I) {
++        while let Some(e) = it.next() {
++            println!("{:?}", e);
++        }
++    }
++}
++
++fn main() {
++    Foo::foo1(vec![].into_iter());
++    Foo::foo2(vec![].into_iter());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..51b872e21c08582afd3e9bea7943c2d64d0af4b0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++error: this loop could be written as a `for` loop
++  --> $DIR/issue_2356.rs:15:9
++   |
++LL |         while let Some(e) = it.next() {
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for e in it`
++   |
++note: the lint level is defined here
++  --> $DIR/issue_2356.rs:1:9
++   |
++LL | #![deny(clippy::while_let_on_iterator)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8a9d5a3d1d5696afe426d685f3314916e020c067
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++// edition:2018
++#![allow(dead_code)]
++
++async fn sink1<'a>(_: &'a str) {} // lint
++async fn sink1_elided(_: &str) {} // ok
++
++// lint
++async fn one_to_one<'a>(s: &'a str) -> &'a str {
++    s
++}
++
++// ok
++async fn one_to_one_elided(s: &str) -> &str {
++    s
++}
++
++// ok
++async fn all_to_one<'a>(a: &'a str, _b: &'a str) -> &'a str {
++    a
++}
++
++// async fn unrelated(_: &str, _: &str) {} // Not allowed in async fn
++
++// #3988
++struct Foo;
++impl Foo {
++    // ok
++    pub async fn foo(&mut self) {}
++}
++
++// rust-lang/rust#61115
++// ok
++async fn print(s: &str) {
++    println!("{}", s);
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0426508e622f8fd471a61e0464e1ba53ccf537a3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/issue_4266.rs:4:1
++   |
++LL | async fn sink1<'a>(_: &'a str) {} // lint
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::needless-lifetimes` implied by `-D warnings`
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/issue_4266.rs:8:1
++   |
++LL | async fn one_to_one<'a>(s: &'a str) -> &'a str {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c17a7cbc8d905a7f7484f8491e854404b382188f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++#![warn(clippy::items_after_statements)]
++
++fn ok() {
++    fn foo() {
++        println!("foo");
++    }
++    foo();
++}
++
++fn last() {
++    foo();
++    fn foo() {
++        println!("foo");
++    }
++}
++
++fn main() {
++    foo();
++    fn foo() {
++        println!("foo");
++    }
++    foo();
++}
++
++fn mac() {
++    let mut a = 5;
++    println!("{}", a);
++    // do not lint this, because it needs to be after `a`
++    macro_rules! b {
++        () => {{
++            a = 6
++        }};
++    }
++    b!();
++    println!("{}", a);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f8f010b5e5c1fb4c4cdbed207a6f2312f157cb42
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++error: adding items after statements is confusing, since items exist from the start of the scope
++  --> $DIR/item_after_statement.rs:12:5
++   |
++LL | /     fn foo() {
++LL | |         println!("foo");
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::items-after-statements` implied by `-D warnings`
++
++error: adding items after statements is confusing, since items exist from the start of the scope
++  --> $DIR/item_after_statement.rs:19:5
++   |
++LL | /     fn foo() {
++LL | |         println!("foo");
++LL | |     }
++   | |_____^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2773227e26bcaa0be55b82eb4abbedcdb8816829
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++// run-rustfix
++
++#![allow(unused)]
++
++use std::collections::HashSet;
++use std::collections::VecDeque;
++
++fn main() {
++    let v = [1, 2, 3, 4, 5];
++    let v2: Vec<isize> = v.to_vec();
++    let v3: HashSet<isize> = v.iter().cloned().collect();
++    let v4: VecDeque<isize> = v.iter().cloned().collect();
++
++    // Handle macro expansion in suggestion
++    let _: Vec<isize> = vec![1, 2, 3].to_vec();
++
++    // Issue #3704
++    unsafe {
++        let _: Vec<u8> = std::ffi::CStr::from_ptr(std::ptr::null())
++            .to_bytes().to_vec();
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..60a4eac23c79f61650593f77063d69d40a3605c3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++// run-rustfix
++
++#![allow(unused)]
++
++use std::collections::HashSet;
++use std::collections::VecDeque;
++
++fn main() {
++    let v = [1, 2, 3, 4, 5];
++    let v2: Vec<isize> = v.iter().cloned().collect();
++    let v3: HashSet<isize> = v.iter().cloned().collect();
++    let v4: VecDeque<isize> = v.iter().cloned().collect();
++
++    // Handle macro expansion in suggestion
++    let _: Vec<isize> = vec![1, 2, 3].iter().cloned().collect();
++
++    // Issue #3704
++    unsafe {
++        let _: Vec<u8> = std::ffi::CStr::from_ptr(std::ptr::null())
++            .to_bytes()
++            .iter()
++            .cloned()
++            .collect();
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b90a1e6c9196733db6f63a23f2e48a2e0dfe136e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
++  --> $DIR/iter_cloned_collect.rs:10:27
++   |
++LL |     let v2: Vec<isize> = v.iter().cloned().collect();
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
++   |
++   = note: `-D clippy::iter-cloned-collect` implied by `-D warnings`
++
++error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
++  --> $DIR/iter_cloned_collect.rs:15:38
++   |
++LL |     let _: Vec<isize> = vec![1, 2, 3].iter().cloned().collect();
++   |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
++
++error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
++  --> $DIR/iter_cloned_collect.rs:20:24
++   |
++LL |               .to_bytes()
++   |  ________________________^
++LL | |             .iter()
++LL | |             .cloned()
++LL | |             .collect();
++   | |______________________^ help: try: `.to_vec()`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9c21dd82ee45efe0a70c1f0b08b2b0688502fa13
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++// aux-build:option_helpers.rs
++
++#![warn(clippy::iter_nth)]
++
++#[macro_use]
++extern crate option_helpers;
++
++use option_helpers::IteratorFalsePositives;
++use std::collections::VecDeque;
++
++/// Struct to generate false positives for things with `.iter()`.
++#[derive(Copy, Clone)]
++struct HasIter;
++
++impl HasIter {
++    fn iter(self) -> IteratorFalsePositives {
++        IteratorFalsePositives { foo: 0 }
++    }
++
++    fn iter_mut(self) -> IteratorFalsePositives {
++        IteratorFalsePositives { foo: 0 }
++    }
++}
++
++/// Checks implementation of `ITER_NTH` lint.
++fn iter_nth() {
++    let mut some_vec = vec![0, 1, 2, 3];
++    let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
++    let mut some_vec_deque: VecDeque<_> = some_vec.iter().cloned().collect();
++
++    {
++        // Make sure we lint `.iter()` for relevant types.
++        let bad_vec = some_vec.iter().nth(3);
++        let bad_slice = &some_vec[..].iter().nth(3);
++        let bad_boxed_slice = boxed_slice.iter().nth(3);
++        let bad_vec_deque = some_vec_deque.iter().nth(3);
++    }
++
++    {
++        // Make sure we lint `.iter_mut()` for relevant types.
++        let bad_vec = some_vec.iter_mut().nth(3);
++    }
++    {
++        let bad_slice = &some_vec[..].iter_mut().nth(3);
++    }
++    {
++        let bad_vec_deque = some_vec_deque.iter_mut().nth(3);
++    }
++
++    // Make sure we don't lint for non-relevant types.
++    let false_positive = HasIter;
++    let ok = false_positive.iter().nth(3);
++    let ok_mut = false_positive.iter_mut().nth(3);
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d00b2fb672bb6ea9abb6160bcb09e331b32c8074
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,59 @@@
++error: called `.iter().nth()` on a Vec
++  --> $DIR/iter_nth.rs:33:23
++   |
++LL |         let bad_vec = some_vec.iter().nth(3);
++   |                       ^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::iter-nth` implied by `-D warnings`
++   = help: calling `.get()` is both faster and more readable
++
++error: called `.iter().nth()` on a slice
++  --> $DIR/iter_nth.rs:34:26
++   |
++LL |         let bad_slice = &some_vec[..].iter().nth(3);
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: calling `.get()` is both faster and more readable
++
++error: called `.iter().nth()` on a slice
++  --> $DIR/iter_nth.rs:35:31
++   |
++LL |         let bad_boxed_slice = boxed_slice.iter().nth(3);
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: calling `.get()` is both faster and more readable
++
++error: called `.iter().nth()` on a VecDeque
++  --> $DIR/iter_nth.rs:36:29
++   |
++LL |         let bad_vec_deque = some_vec_deque.iter().nth(3);
++   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: calling `.get()` is both faster and more readable
++
++error: called `.iter_mut().nth()` on a Vec
++  --> $DIR/iter_nth.rs:41:23
++   |
++LL |         let bad_vec = some_vec.iter_mut().nth(3);
++   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: calling `.get_mut()` is both faster and more readable
++
++error: called `.iter_mut().nth()` on a slice
++  --> $DIR/iter_nth.rs:44:26
++   |
++LL |         let bad_slice = &some_vec[..].iter_mut().nth(3);
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: calling `.get_mut()` is both faster and more readable
++
++error: called `.iter_mut().nth()` on a VecDeque
++  --> $DIR/iter_nth.rs:47:29
++   |
++LL |         let bad_vec_deque = some_vec_deque.iter_mut().nth(3);
++   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: calling `.get_mut()` is both faster and more readable
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b54147c94d192bea1c3ec58030aebd824b4f9bf2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++// run-rustfix
++
++#![warn(clippy::iter_nth_zero)]
++use std::collections::HashSet;
++
++struct Foo {}
++
++impl Foo {
++    fn nth(&self, index: usize) -> usize {
++        index + 1
++    }
++}
++
++fn main() {
++    let f = Foo {};
++    f.nth(0); // lint does not apply here
++
++    let mut s = HashSet::new();
++    s.insert(1);
++    let _x = s.iter().next();
++
++    let mut s2 = HashSet::new();
++    s2.insert(2);
++    let mut iter = s2.iter();
++    let _y = iter.next();
++
++    let mut s3 = HashSet::new();
++    s3.insert(3);
++    let mut iter2 = s3.iter();
++    let _unwrapped = iter2.next().unwrap();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b92c7d18adb4fb658484c29a640999e23907946b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++// run-rustfix
++
++#![warn(clippy::iter_nth_zero)]
++use std::collections::HashSet;
++
++struct Foo {}
++
++impl Foo {
++    fn nth(&self, index: usize) -> usize {
++        index + 1
++    }
++}
++
++fn main() {
++    let f = Foo {};
++    f.nth(0); // lint does not apply here
++
++    let mut s = HashSet::new();
++    s.insert(1);
++    let _x = s.iter().nth(0);
++
++    let mut s2 = HashSet::new();
++    s2.insert(2);
++    let mut iter = s2.iter();
++    let _y = iter.nth(0);
++
++    let mut s3 = HashSet::new();
++    s3.insert(3);
++    let mut iter2 = s3.iter();
++    let _unwrapped = iter2.nth(0).unwrap();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2b20a4ceb4ab8d9ffcc3b658348920cbdfe53ad5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: called `.nth(0)` on a `std::iter::Iterator`
++  --> $DIR/iter_nth_zero.rs:20:14
++   |
++LL |     let _x = s.iter().nth(0);
++   |              ^^^^^^^^^^^^^^^ help: try calling: `s.iter().next()`
++   |
++   = note: `-D clippy::iter-nth-zero` implied by `-D warnings`
++
++error: called `.nth(0)` on a `std::iter::Iterator`
++  --> $DIR/iter_nth_zero.rs:25:14
++   |
++LL |     let _y = iter.nth(0);
++   |              ^^^^^^^^^^^ help: try calling: `iter.next()`
++
++error: called `.nth(0)` on a `std::iter::Iterator`
++  --> $DIR/iter_nth_zero.rs:30:22
++   |
++LL |     let _unwrapped = iter2.nth(0).unwrap();
++   |                      ^^^^^^^^^^^^ help: try calling: `iter2.next()`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a65ca3bbb131b149d4328612bcef41931c18a773
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++// aux-build:option_helpers.rs
++
++#![warn(clippy::iter_skip_next)]
++#![allow(clippy::blacklisted_name)]
++
++extern crate option_helpers;
++
++use option_helpers::IteratorFalsePositives;
++
++/// Checks implementation of `ITER_SKIP_NEXT` lint
++fn iter_skip_next() {
++    let mut some_vec = vec![0, 1, 2, 3];
++    let _ = some_vec.iter().skip(42).next();
++    let _ = some_vec.iter().cycle().skip(42).next();
++    let _ = (1..10).skip(10).next();
++    let _ = &some_vec[..].iter().skip(3).next();
++    let foo = IteratorFalsePositives { foo: 0 };
++    let _ = foo.skip(42).next();
++    let _ = foo.filter().skip(42).next();
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5709f3355298bb396337aa7a259dc6040c3d5da4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++error: called `skip(x).next()` on an iterator
++  --> $DIR/iter_skip_next.rs:13:13
++   |
++LL |     let _ = some_vec.iter().skip(42).next();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::iter-skip-next` implied by `-D warnings`
++   = help: this is more succinctly expressed by calling `nth(x)`
++
++error: called `skip(x).next()` on an iterator
++  --> $DIR/iter_skip_next.rs:14:13
++   |
++LL |     let _ = some_vec.iter().cycle().skip(42).next();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: this is more succinctly expressed by calling `nth(x)`
++
++error: called `skip(x).next()` on an iterator
++  --> $DIR/iter_skip_next.rs:15:13
++   |
++LL |     let _ = (1..10).skip(10).next();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: this is more succinctly expressed by calling `nth(x)`
++
++error: called `skip(x).next()` on an iterator
++  --> $DIR/iter_skip_next.rs:16:14
++   |
++LL |     let _ = &some_vec[..].iter().skip(3).next();
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: this is more succinctly expressed by calling `nth(x)`
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..13d1cfd428185b83479abd7b939b55e5137f357d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++#[warn(clippy::iterator_step_by_zero)]
++fn main() {
++    let _ = vec!["A", "B", "B"].iter().step_by(0);
++    let _ = "XXX".chars().step_by(0);
++    let _ = (0..1).step_by(0);
++
++    // No error, not an iterator.
++    let y = NotIterator;
++    y.step_by(0);
++
++    // No warning for non-zero step
++    let _ = (0..1).step_by(1);
++
++    let _ = (1..).step_by(0);
++    let _ = (1..=2).step_by(0);
++
++    let x = 0..1;
++    let _ = x.step_by(0);
++
++    // check const eval
++    let v1 = vec![1, 2, 3];
++    let _ = v1.iter().step_by(2 / 3);
++}
++
++struct NotIterator;
++impl NotIterator {
++    fn step_by(&self, _: u32) {}
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c2c6803b3e6e7cf0f768bfd263f51a9794ce431d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++error: Iterator::step_by(0) will panic at runtime
++  --> $DIR/iterator_step_by_zero.rs:3:13
++   |
++LL |     let _ = vec!["A", "B", "B"].iter().step_by(0);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::iterator-step-by-zero` implied by `-D warnings`
++
++error: Iterator::step_by(0) will panic at runtime
++  --> $DIR/iterator_step_by_zero.rs:4:13
++   |
++LL |     let _ = "XXX".chars().step_by(0);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: Iterator::step_by(0) will panic at runtime
++  --> $DIR/iterator_step_by_zero.rs:5:13
++   |
++LL |     let _ = (0..1).step_by(0);
++   |             ^^^^^^^^^^^^^^^^^
++
++error: Iterator::step_by(0) will panic at runtime
++  --> $DIR/iterator_step_by_zero.rs:14:13
++   |
++LL |     let _ = (1..).step_by(0);
++   |             ^^^^^^^^^^^^^^^^
++
++error: Iterator::step_by(0) will panic at runtime
++  --> $DIR/iterator_step_by_zero.rs:15:13
++   |
++LL |     let _ = (1..=2).step_by(0);
++   |             ^^^^^^^^^^^^^^^^^^
++
++error: Iterator::step_by(0) will panic at runtime
++  --> $DIR/iterator_step_by_zero.rs:18:13
++   |
++LL |     let _ = x.step_by(0);
++   |             ^^^^^^^^^^^^
++
++error: Iterator::step_by(0) will panic at runtime
++  --> $DIR/iterator_step_by_zero.rs:22:13
++   |
++LL |     let _ = v1.iter().step_by(2 / 3);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c5af07c8a1728bc671c4837ccb7d7bec70150ae5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++// run-rustfix
++
++#![warn(clippy::large_const_arrays)]
++#![allow(dead_code)]
++
++#[derive(Clone, Copy)]
++pub struct S {
++    pub data: [u64; 32],
++}
++
++// Should lint
++pub(crate) static FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000];
++pub static FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
++static FOO: [u32; 1_000_000] = [0u32; 1_000_000];
++
++// Good
++pub(crate) const G_FOO_PUB_CRATE: [u32; 1_000] = [0u32; 1_000];
++pub const G_FOO_PUB: [u32; 1_000] = [0u32; 1_000];
++const G_FOO: [u32; 1_000] = [0u32; 1_000];
++
++fn main() {
++    // Should lint
++    pub static BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
++    static BAR: [u32; 1_000_000] = [0u32; 1_000_000];
++    pub static BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000];
++    static BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000];
++    pub static BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000];
++    static BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000];
++
++    // Good
++    pub const G_BAR_PUB: [u32; 1_000] = [0u32; 1_000];
++    const G_BAR: [u32; 1_000] = [0u32; 1_000];
++    pub const G_BAR_STRUCT_PUB: [S; 500] = [S { data: [0; 32] }; 500];
++    const G_BAR_STRUCT: [S; 500] = [S { data: [0; 32] }; 500];
++    pub const G_BAR_S_PUB: [Option<&str>; 200] = [Some("str"); 200];
++    const G_BAR_S: [Option<&str>; 200] = [Some("str"); 200];
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a160b9f8ad5b0249adeb8099f4dbcfa40c8abe51
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++// run-rustfix
++
++#![warn(clippy::large_const_arrays)]
++#![allow(dead_code)]
++
++#[derive(Clone, Copy)]
++pub struct S {
++    pub data: [u64; 32],
++}
++
++// Should lint
++pub(crate) const FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000];
++pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
++const FOO: [u32; 1_000_000] = [0u32; 1_000_000];
++
++// Good
++pub(crate) const G_FOO_PUB_CRATE: [u32; 1_000] = [0u32; 1_000];
++pub const G_FOO_PUB: [u32; 1_000] = [0u32; 1_000];
++const G_FOO: [u32; 1_000] = [0u32; 1_000];
++
++fn main() {
++    // Should lint
++    pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
++    const BAR: [u32; 1_000_000] = [0u32; 1_000_000];
++    pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000];
++    const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000];
++    pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000];
++    const BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000];
++
++    // Good
++    pub const G_BAR_PUB: [u32; 1_000] = [0u32; 1_000];
++    const G_BAR: [u32; 1_000] = [0u32; 1_000];
++    pub const G_BAR_STRUCT_PUB: [S; 500] = [S { data: [0; 32] }; 500];
++    const G_BAR_STRUCT: [S; 500] = [S { data: [0; 32] }; 500];
++    pub const G_BAR_S_PUB: [Option<&str>; 200] = [Some("str"); 200];
++    const G_BAR_S: [Option<&str>; 200] = [Some("str"); 200];
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3fb0acbca67de8225e1df60c6ba873cedd6fdc82
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,76 @@@
++error: large array defined as const
++  --> $DIR/large_const_arrays.rs:12:1
++   |
++LL | pub(crate) const FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000];
++   | ^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |            |
++   |            help: make this a static item: `static`
++   |
++   = note: `-D clippy::large-const-arrays` implied by `-D warnings`
++
++error: large array defined as const
++  --> $DIR/large_const_arrays.rs:13:1
++   |
++LL | pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
++   | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |     |
++   |     help: make this a static item: `static`
++
++error: large array defined as const
++  --> $DIR/large_const_arrays.rs:14:1
++   |
++LL | const FOO: [u32; 1_000_000] = [0u32; 1_000_000];
++   | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   | |
++   | help: make this a static item: `static`
++
++error: large array defined as const
++  --> $DIR/large_const_arrays.rs:23:5
++   |
++LL |     pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
++   |     ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |         |
++   |         help: make this a static item: `static`
++
++error: large array defined as const
++  --> $DIR/large_const_arrays.rs:24:5
++   |
++LL |     const BAR: [u32; 1_000_000] = [0u32; 1_000_000];
++   |     -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |     |
++   |     help: make this a static item: `static`
++
++error: large array defined as const
++  --> $DIR/large_const_arrays.rs:25:5
++   |
++LL |     pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000];
++   |     ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |         |
++   |         help: make this a static item: `static`
++
++error: large array defined as const
++  --> $DIR/large_const_arrays.rs:26:5
++   |
++LL |     const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000];
++   |     -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |     |
++   |     help: make this a static item: `static`
++
++error: large array defined as const
++  --> $DIR/large_const_arrays.rs:27:5
++   |
++LL |     pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000];
++   |     ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |         |
++   |         help: make this a static item: `static`
++
++error: large array defined as const
++  --> $DIR/large_const_arrays.rs:28:5
++   |
++LL |     const BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000];
++   |     -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |     |
++   |     help: make this a static item: `static`
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..859fad2f54d9d82bb5b7d664c3fb12ca271f2efe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++// run-rustfix
++#![warn(clippy::large_digit_groups)]
++
++fn main() {
++    macro_rules! mac {
++        () => {
++            0b1_10110_i64
++        };
++    }
++
++    let _good = (
++        0b1011_i64,
++        0o1_234_u32,
++        0x1_234_567,
++        1_2345_6789,
++        1234_f32,
++        1_234.12_f32,
++        1_234.123_f32,
++        1.123_4_f32,
++    );
++    let _bad = (
++        0b11_0110_i64,
++        0xdead_beef_usize,
++        123_456_f32,
++        123_456.12_f32,
++        123_456.123_45_f64,
++        123_456.123_456_f64,
++    );
++    // Ignore literals in macros
++    let _ = mac!();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ac116d5dbda15c953c6e23fce0baf8cdfb56a333
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++// run-rustfix
++#![warn(clippy::large_digit_groups)]
++
++fn main() {
++    macro_rules! mac {
++        () => {
++            0b1_10110_i64
++        };
++    }
++
++    let _good = (
++        0b1011_i64,
++        0o1_234_u32,
++        0x1_234_567,
++        1_2345_6789,
++        1234_f32,
++        1_234.12_f32,
++        1_234.123_f32,
++        1.123_4_f32,
++    );
++    let _bad = (
++        0b1_10110_i64,
++        0xd_e_adbee_f_usize,
++        1_23456_f32,
++        1_23456.12_f32,
++        1_23456.12345_f64,
++        1_23456.12345_6_f64,
++    );
++    // Ignore literals in macros
++    let _ = mac!();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b6d9672a78e2191c3475d1449a0196c907c8b0eb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++error: digit groups should be smaller
++  --> $DIR/large_digit_groups.rs:22:9
++   |
++LL |         0b1_10110_i64,
++   |         ^^^^^^^^^^^^^ help: consider: `0b11_0110_i64`
++   |
++   = note: `-D clippy::large-digit-groups` implied by `-D warnings`
++
++error: digits grouped inconsistently by underscores
++  --> $DIR/large_digit_groups.rs:23:9
++   |
++LL |         0xd_e_adbee_f_usize,
++   |         ^^^^^^^^^^^^^^^^^^^ help: consider: `0xdead_beef_usize`
++   |
++   = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings`
++
++error: digit groups should be smaller
++  --> $DIR/large_digit_groups.rs:24:9
++   |
++LL |         1_23456_f32,
++   |         ^^^^^^^^^^^ help: consider: `123_456_f32`
++
++error: digit groups should be smaller
++  --> $DIR/large_digit_groups.rs:25:9
++   |
++LL |         1_23456.12_f32,
++   |         ^^^^^^^^^^^^^^ help: consider: `123_456.12_f32`
++
++error: digit groups should be smaller
++  --> $DIR/large_digit_groups.rs:26:9
++   |
++LL |         1_23456.12345_f64,
++   |         ^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_45_f64`
++
++error: digit groups should be smaller
++  --> $DIR/large_digit_groups.rs:27:9
++   |
++LL |         1_23456.12345_6_f64,
++   |         ^^^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_456_f64`
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..852ef5fec0e7b060332bca56b8fb96e29d555e72
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,54 @@@
++#![allow(dead_code)]
++#![allow(unused_variables)]
++#![warn(clippy::large_enum_variant)]
++
++enum LargeEnum {
++    A(i32),
++    B([i32; 8000]),
++}
++
++enum GenericEnumOk<T> {
++    A(i32),
++    B([T; 8000]),
++}
++
++enum GenericEnum2<T> {
++    A(i32),
++    B([i32; 8000]),
++    C(T, [i32; 8000]),
++}
++
++trait SomeTrait {
++    type Item;
++}
++
++enum LargeEnumGeneric<A: SomeTrait> {
++    Var(A::Item),
++}
++
++enum LargeEnum2 {
++    VariantOk(i32, u32),
++    ContainingLargeEnum(LargeEnum),
++}
++enum LargeEnum3 {
++    ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]),
++    VoidVariant,
++    StructLikeLittle { x: i32, y: i32 },
++}
++
++enum LargeEnum4 {
++    VariantOk(i32, u32),
++    StructLikeLarge { x: [i32; 8000], y: i32 },
++}
++
++enum LargeEnum5 {
++    VariantOk(i32, u32),
++    StructLikeLarge2 { x: [i32; 8000] },
++}
++
++enum LargeEnumOk {
++    LargeA([i32; 8000]),
++    LargeB([i32; 8001]),
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8ce641a81f29746289c9b750b11cad95ba133cf2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,68 @@@
++error: large size difference between variants
++  --> $DIR/large_enum_variant.rs:7:5
++   |
++LL |     B([i32; 8000]),
++   |     ^^^^^^^^^^^^^^ this variant is 32000 bytes
++   |
++   = note: `-D clippy::large-enum-variant` implied by `-D warnings`
++note: and the second-largest variant is 4 bytes:
++  --> $DIR/large_enum_variant.rs:6:5
++   |
++LL |     A(i32),
++   |     ^^^^^^
++help: consider boxing the large fields to reduce the total size of the enum
++   |
++LL |     B(Box<[i32; 8000]>),
++   |       ^^^^^^^^^^^^^^^^
++
++error: large size difference between variants
++  --> $DIR/large_enum_variant.rs:31:5
++   |
++LL |     ContainingLargeEnum(LargeEnum),
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes
++   |
++note: and the second-largest variant is 8 bytes:
++  --> $DIR/large_enum_variant.rs:30:5
++   |
++LL |     VariantOk(i32, u32),
++   |     ^^^^^^^^^^^^^^^^^^^
++help: consider boxing the large fields to reduce the total size of the enum
++   |
++LL |     ContainingLargeEnum(Box<LargeEnum>),
++   |                         ^^^^^^^^^^^^^^
++
++error: large size difference between variants
++  --> $DIR/large_enum_variant.rs:41:5
++   |
++LL |     StructLikeLarge { x: [i32; 8000], y: i32 },
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes
++   |
++note: and the second-largest variant is 8 bytes:
++  --> $DIR/large_enum_variant.rs:40:5
++   |
++LL |     VariantOk(i32, u32),
++   |     ^^^^^^^^^^^^^^^^^^^
++help: consider boxing the large fields to reduce the total size of the enum
++  --> $DIR/large_enum_variant.rs:41:5
++   |
++LL |     StructLikeLarge { x: [i32; 8000], y: i32 },
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: large size difference between variants
++  --> $DIR/large_enum_variant.rs:46:5
++   |
++LL |     StructLikeLarge2 { x: [i32; 8000] },
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32000 bytes
++   |
++note: and the second-largest variant is 8 bytes:
++  --> $DIR/large_enum_variant.rs:45:5
++   |
++LL |     VariantOk(i32, u32),
++   |     ^^^^^^^^^^^^^^^^^^^
++help: consider boxing the large fields to reduce the total size of the enum
++   |
++LL |     StructLikeLarge2 { x: Box<[i32; 8000]> },
++   |                           ^^^^^^^^^^^^^^^^
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d9161bfcf15433f491bb5c58125f9b327b6736c5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++#![warn(clippy::large_stack_arrays)]
++#![allow(clippy::large_enum_variant)]
++
++#[derive(Clone, Copy)]
++struct S {
++    pub data: [u64; 32],
++}
++
++#[derive(Clone, Copy)]
++enum E {
++    S(S),
++    T(u32),
++}
++
++fn main() {
++    let bad = (
++        [0u32; 20_000_000],
++        [S { data: [0; 32] }; 5000],
++        [Some(""); 20_000_000],
++        [E::T(0); 5000],
++    );
++
++    let good = (
++        [0u32; 1000],
++        [S { data: [0; 32] }; 1000],
++        [Some(""); 1000],
++        [E::T(0); 1000],
++        [(); 20_000_000],
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..58c0a77c1c84136c6205232aa03ef54be3498cf9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++error: allocating a local array larger than 512000 bytes
++  --> $DIR/large_stack_arrays.rs:17:9
++   |
++LL |         [0u32; 20_000_000],
++   |         ^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::large-stack-arrays` implied by `-D warnings`
++   = help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()`
++
++error: allocating a local array larger than 512000 bytes
++  --> $DIR/large_stack_arrays.rs:18:9
++   |
++LL |         [S { data: [0; 32] }; 5000],
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()`
++
++error: allocating a local array larger than 512000 bytes
++  --> $DIR/large_stack_arrays.rs:19:9
++   |
++LL |         [Some(""); 20_000_000],
++   |         ^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()`
++
++error: allocating a local array larger than 512000 bytes
++  --> $DIR/large_stack_arrays.rs:20:9
++   |
++LL |         [E::T(0); 5000],
++   |         ^^^^^^^^^^^^^^^
++   |
++   = help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()`
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3ef29dd63880b7f62757d82ab6a2f6bdf2dfc341
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,145 @@@
++#![warn(clippy::len_without_is_empty)]
++#![allow(dead_code, unused)]
++
++pub struct PubOne;
++
++impl PubOne {
++    pub fn len(self: &Self) -> isize {
++        1
++    }
++}
++
++impl PubOne {
++    // A second impl for this struct -- the error span shouldn't mention this.
++    pub fn irrelevant(self: &Self) -> bool {
++        false
++    }
++}
++
++// Identical to `PubOne`, but with an `allow` attribute on the impl complaining `len`.
++pub struct PubAllowed;
++
++#[allow(clippy::len_without_is_empty)]
++impl PubAllowed {
++    pub fn len(self: &Self) -> isize {
++        1
++    }
++}
++
++// No `allow` attribute on this impl block, but that doesn't matter -- we only require one on the
++// impl containing `len`.
++impl PubAllowed {
++    pub fn irrelevant(self: &Self) -> bool {
++        false
++    }
++}
++
++pub trait PubTraitsToo {
++    fn len(self: &Self) -> isize;
++}
++
++impl PubTraitsToo for One {
++    fn len(self: &Self) -> isize {
++        0
++    }
++}
++
++pub struct HasIsEmpty;
++
++impl HasIsEmpty {
++    pub fn len(self: &Self) -> isize {
++        1
++    }
++
++    fn is_empty(self: &Self) -> bool {
++        false
++    }
++}
++
++pub struct HasWrongIsEmpty;
++
++impl HasWrongIsEmpty {
++    pub fn len(self: &Self) -> isize {
++        1
++    }
++
++    pub fn is_empty(self: &Self, x: u32) -> bool {
++        false
++    }
++}
++
++struct NotPubOne;
++
++impl NotPubOne {
++    pub fn len(self: &Self) -> isize {
++        // No error; `len` is pub but `NotPubOne` is not exported anyway.
++        1
++    }
++}
++
++struct One;
++
++impl One {
++    fn len(self: &Self) -> isize {
++        // No error; `len` is private; see issue #1085.
++        1
++    }
++}
++
++trait TraitsToo {
++    fn len(self: &Self) -> isize;
++    // No error; `len` is private; see issue #1085.
++}
++
++impl TraitsToo for One {
++    fn len(self: &Self) -> isize {
++        0
++    }
++}
++
++struct HasPrivateIsEmpty;
++
++impl HasPrivateIsEmpty {
++    pub fn len(self: &Self) -> isize {
++        1
++    }
++
++    fn is_empty(self: &Self) -> bool {
++        false
++    }
++}
++
++struct Wither;
++
++pub trait WithIsEmpty {
++    fn len(self: &Self) -> isize;
++    fn is_empty(self: &Self) -> bool;
++}
++
++impl WithIsEmpty for Wither {
++    fn len(self: &Self) -> isize {
++        1
++    }
++
++    fn is_empty(self: &Self) -> bool {
++        false
++    }
++}
++
++pub trait Empty {
++    fn is_empty(&self) -> bool;
++}
++
++pub trait InheritingEmpty: Empty {
++    // Must not trigger `LEN_WITHOUT_IS_EMPTY`.
++    fn len(&self) -> isize;
++}
++
++// This used to ICE.
++pub trait Foo: Sized {}
++
++pub trait DependsOnFoo: Foo {
++    fn len(&mut self) -> usize;
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4493b17a4b4e5672a0a494165ee78788955bc206
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,54 @@@
++error: item `PubOne` has a public `len` method but no corresponding `is_empty` method
++  --> $DIR/len_without_is_empty.rs:6:1
++   |
++LL | / impl PubOne {
++LL | |     pub fn len(self: &Self) -> isize {
++LL | |         1
++LL | |     }
++LL | | }
++   | |_^
++   |
++   = note: `-D clippy::len-without-is-empty` implied by `-D warnings`
++
++error: trait `PubTraitsToo` has a `len` method but no (possibly inherited) `is_empty` method
++  --> $DIR/len_without_is_empty.rs:37:1
++   |
++LL | / pub trait PubTraitsToo {
++LL | |     fn len(self: &Self) -> isize;
++LL | | }
++   | |_^
++
++error: item `HasIsEmpty` has a public `len` method but a private `is_empty` method
++  --> $DIR/len_without_is_empty.rs:49:1
++   |
++LL | / impl HasIsEmpty {
++LL | |     pub fn len(self: &Self) -> isize {
++LL | |         1
++LL | |     }
++...  |
++LL | |     }
++LL | | }
++   | |_^
++
++error: item `HasWrongIsEmpty` has a public `len` method but no corresponding `is_empty` method
++  --> $DIR/len_without_is_empty.rs:61:1
++   |
++LL | / impl HasWrongIsEmpty {
++LL | |     pub fn len(self: &Self) -> isize {
++LL | |         1
++LL | |     }
++...  |
++LL | |     }
++LL | | }
++   | |_^
++
++error: trait `DependsOnFoo` has a `len` method but no (possibly inherited) `is_empty` method
++  --> $DIR/len_without_is_empty.rs:141:1
++   |
++LL | / pub trait DependsOnFoo: Foo {
++LL | |     fn len(&mut self) -> usize;
++LL | | }
++   | |_^
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..624e5ef8fcf13129184a1c5b1580ce50ad6c696c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,143 @@@
++// run-rustfix
++
++#![warn(clippy::len_zero)]
++#![allow(dead_code, unused, clippy::len_without_is_empty)]
++
++pub struct One;
++struct Wither;
++
++trait TraitsToo {
++    fn len(self: &Self) -> isize;
++    // No error; `len` is private; see issue #1085.
++}
++
++impl TraitsToo for One {
++    fn len(self: &Self) -> isize {
++        0
++    }
++}
++
++pub struct HasIsEmpty;
++
++impl HasIsEmpty {
++    pub fn len(self: &Self) -> isize {
++        1
++    }
++
++    fn is_empty(self: &Self) -> bool {
++        false
++    }
++}
++
++pub struct HasWrongIsEmpty;
++
++impl HasWrongIsEmpty {
++    pub fn len(self: &Self) -> isize {
++        1
++    }
++
++    pub fn is_empty(self: &Self, x: u32) -> bool {
++        false
++    }
++}
++
++pub trait WithIsEmpty {
++    fn len(self: &Self) -> isize;
++    fn is_empty(self: &Self) -> bool;
++}
++
++impl WithIsEmpty for Wither {
++    fn len(self: &Self) -> isize {
++        1
++    }
++
++    fn is_empty(self: &Self) -> bool {
++        false
++    }
++}
++
++fn main() {
++    let x = [1, 2];
++    if x.is_empty() {
++        println!("This should not happen!");
++    }
++
++    if "".is_empty() {}
++
++    let y = One;
++    if y.len() == 0 {
++        // No error; `One` does not have `.is_empty()`.
++        println!("This should not happen either!");
++    }
++
++    let z: &dyn TraitsToo = &y;
++    if z.len() > 0 {
++        // No error; `TraitsToo` has no `.is_empty()` method.
++        println!("Nor should this!");
++    }
++
++    let has_is_empty = HasIsEmpty;
++    if has_is_empty.is_empty() {
++        println!("Or this!");
++    }
++    if !has_is_empty.is_empty() {
++        println!("Or this!");
++    }
++    if !has_is_empty.is_empty() {
++        println!("Or this!");
++    }
++    if has_is_empty.is_empty() {
++        println!("Or this!");
++    }
++    if !has_is_empty.is_empty() {
++        println!("Or this!");
++    }
++    if has_is_empty.len() > 1 {
++        // No error.
++        println!("This can happen.");
++    }
++    if has_is_empty.len() <= 1 {
++        // No error.
++        println!("This can happen.");
++    }
++    if has_is_empty.is_empty() {
++        println!("Or this!");
++    }
++    if !has_is_empty.is_empty() {
++        println!("Or this!");
++    }
++    if !has_is_empty.is_empty() {
++        println!("Or this!");
++    }
++    if !has_is_empty.is_empty() {
++        println!("Or this!");
++    }
++    if has_is_empty.is_empty() {
++        println!("Or this!");
++    }
++    if 1 < has_is_empty.len() {
++        // No error.
++        println!("This can happen.");
++    }
++    if 1 >= has_is_empty.len() {
++        // No error.
++        println!("This can happen.");
++    }
++    assert!(!has_is_empty.is_empty());
++
++    let with_is_empty: &dyn WithIsEmpty = &Wither;
++    if with_is_empty.is_empty() {
++        println!("Or this!");
++    }
++    assert!(!with_is_empty.is_empty());
++
++    let has_wrong_is_empty = HasWrongIsEmpty;
++    if has_wrong_is_empty.len() == 0 {
++        // No error; `HasWrongIsEmpty` does not have `.is_empty()`.
++        println!("Or this!");
++    }
++}
++
++fn test_slice(b: &[u8]) {
++    if !b.is_empty() {}
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7fba971cfd8876ebb0e2713cdd09e492586b473c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,143 @@@
++// run-rustfix
++
++#![warn(clippy::len_zero)]
++#![allow(dead_code, unused, clippy::len_without_is_empty)]
++
++pub struct One;
++struct Wither;
++
++trait TraitsToo {
++    fn len(self: &Self) -> isize;
++    // No error; `len` is private; see issue #1085.
++}
++
++impl TraitsToo for One {
++    fn len(self: &Self) -> isize {
++        0
++    }
++}
++
++pub struct HasIsEmpty;
++
++impl HasIsEmpty {
++    pub fn len(self: &Self) -> isize {
++        1
++    }
++
++    fn is_empty(self: &Self) -> bool {
++        false
++    }
++}
++
++pub struct HasWrongIsEmpty;
++
++impl HasWrongIsEmpty {
++    pub fn len(self: &Self) -> isize {
++        1
++    }
++
++    pub fn is_empty(self: &Self, x: u32) -> bool {
++        false
++    }
++}
++
++pub trait WithIsEmpty {
++    fn len(self: &Self) -> isize;
++    fn is_empty(self: &Self) -> bool;
++}
++
++impl WithIsEmpty for Wither {
++    fn len(self: &Self) -> isize {
++        1
++    }
++
++    fn is_empty(self: &Self) -> bool {
++        false
++    }
++}
++
++fn main() {
++    let x = [1, 2];
++    if x.len() == 0 {
++        println!("This should not happen!");
++    }
++
++    if "".len() == 0 {}
++
++    let y = One;
++    if y.len() == 0 {
++        // No error; `One` does not have `.is_empty()`.
++        println!("This should not happen either!");
++    }
++
++    let z: &dyn TraitsToo = &y;
++    if z.len() > 0 {
++        // No error; `TraitsToo` has no `.is_empty()` method.
++        println!("Nor should this!");
++    }
++
++    let has_is_empty = HasIsEmpty;
++    if has_is_empty.len() == 0 {
++        println!("Or this!");
++    }
++    if has_is_empty.len() != 0 {
++        println!("Or this!");
++    }
++    if has_is_empty.len() > 0 {
++        println!("Or this!");
++    }
++    if has_is_empty.len() < 1 {
++        println!("Or this!");
++    }
++    if has_is_empty.len() >= 1 {
++        println!("Or this!");
++    }
++    if has_is_empty.len() > 1 {
++        // No error.
++        println!("This can happen.");
++    }
++    if has_is_empty.len() <= 1 {
++        // No error.
++        println!("This can happen.");
++    }
++    if 0 == has_is_empty.len() {
++        println!("Or this!");
++    }
++    if 0 != has_is_empty.len() {
++        println!("Or this!");
++    }
++    if 0 < has_is_empty.len() {
++        println!("Or this!");
++    }
++    if 1 <= has_is_empty.len() {
++        println!("Or this!");
++    }
++    if 1 > has_is_empty.len() {
++        println!("Or this!");
++    }
++    if 1 < has_is_empty.len() {
++        // No error.
++        println!("This can happen.");
++    }
++    if 1 >= has_is_empty.len() {
++        // No error.
++        println!("This can happen.");
++    }
++    assert!(!has_is_empty.is_empty());
++
++    let with_is_empty: &dyn WithIsEmpty = &Wither;
++    if with_is_empty.len() == 0 {
++        println!("Or this!");
++    }
++    assert!(!with_is_empty.is_empty());
++
++    let has_wrong_is_empty = HasWrongIsEmpty;
++    if has_wrong_is_empty.len() == 0 {
++        // No error; `HasWrongIsEmpty` does not have `.is_empty()`.
++        println!("Or this!");
++    }
++}
++
++fn test_slice(b: &[u8]) {
++    if b.len() != 0 {}
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6c71f1beeac67c27a89916538cbdfcc2af70d896
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,88 @@@
++error: length comparison to zero
++  --> $DIR/len_zero.rs:61:8
++   |
++LL |     if x.len() == 0 {
++   |        ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `x.is_empty()`
++   |
++   = note: `-D clippy::len-zero` implied by `-D warnings`
++
++error: length comparison to zero
++  --> $DIR/len_zero.rs:65:8
++   |
++LL |     if "".len() == 0 {}
++   |        ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `"".is_empty()`
++
++error: length comparison to zero
++  --> $DIR/len_zero.rs:80:8
++   |
++LL |     if has_is_empty.len() == 0 {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
++
++error: length comparison to zero
++  --> $DIR/len_zero.rs:83:8
++   |
++LL |     if has_is_empty.len() != 0 {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
++
++error: length comparison to zero
++  --> $DIR/len_zero.rs:86:8
++   |
++LL |     if has_is_empty.len() > 0 {
++   |        ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
++
++error: length comparison to one
++  --> $DIR/len_zero.rs:89:8
++   |
++LL |     if has_is_empty.len() < 1 {
++   |        ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
++
++error: length comparison to one
++  --> $DIR/len_zero.rs:92:8
++   |
++LL |     if has_is_empty.len() >= 1 {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
++
++error: length comparison to zero
++  --> $DIR/len_zero.rs:103:8
++   |
++LL |     if 0 == has_is_empty.len() {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
++
++error: length comparison to zero
++  --> $DIR/len_zero.rs:106:8
++   |
++LL |     if 0 != has_is_empty.len() {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
++
++error: length comparison to zero
++  --> $DIR/len_zero.rs:109:8
++   |
++LL |     if 0 < has_is_empty.len() {
++   |        ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
++
++error: length comparison to one
++  --> $DIR/len_zero.rs:112:8
++   |
++LL |     if 1 <= has_is_empty.len() {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
++
++error: length comparison to one
++  --> $DIR/len_zero.rs:115:8
++   |
++LL |     if 1 > has_is_empty.len() {
++   |        ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
++
++error: length comparison to zero
++  --> $DIR/len_zero.rs:129:8
++   |
++LL |     if with_is_empty.len() == 0 {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `with_is_empty.is_empty()`
++
++error: length comparison to zero
++  --> $DIR/len_zero.rs:142:8
++   |
++LL |     if b.len() != 0 {}
++   |        ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()`
++
++error: aborting due to 14 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..802beeb4be6b18360bd5f6e59defababa1319c74
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,119 @@@
++#![allow(
++    unused_variables,
++    unused_assignments,
++    clippy::similar_names,
++    clippy::blacklisted_name
++)]
++#![warn(clippy::useless_let_if_seq)]
++
++fn f() -> bool {
++    true
++}
++fn g(x: i32) -> i32 {
++    x + 1
++}
++
++fn issue985() -> i32 {
++    let mut x = 42;
++    if f() {
++        x = g(x);
++    }
++
++    x
++}
++
++fn issue985_alt() -> i32 {
++    let mut x = 42;
++    if f() {
++        f();
++    } else {
++        x = g(x);
++    }
++
++    x
++}
++
++fn issue975() -> String {
++    let mut udn = "dummy".to_string();
++    if udn.starts_with("uuid:") {
++        udn = String::from(&udn[5..]);
++    }
++    udn
++}
++
++fn early_return() -> u8 {
++    // FIXME: we could extend the lint to include such cases:
++    let foo;
++
++    if f() {
++        return 42;
++    } else {
++        foo = 0;
++    }
++
++    foo
++}
++
++fn main() {
++    early_return();
++    issue975();
++    issue985();
++    issue985_alt();
++
++    let mut foo = 0;
++    if f() {
++        foo = 42;
++    }
++
++    let mut bar = 0;
++    if f() {
++        f();
++        bar = 42;
++    } else {
++        f();
++    }
++
++    let quz;
++    if f() {
++        quz = 42;
++    } else {
++        quz = 0;
++    }
++
++    // `toto` is used several times
++    let mut toto;
++    if f() {
++        toto = 42;
++    } else {
++        for i in &[1, 2] {
++            toto = *i;
++        }
++
++        toto = 2;
++    }
++
++    // found in libcore, the inner if is not a statement but the block's expr
++    let mut ch = b'x';
++    if f() {
++        ch = b'*';
++        if f() {
++            ch = b'?';
++        }
++    }
++
++    // baz needs to be mut
++    let mut baz = 0;
++    if f() {
++        baz = 42;
++    }
++
++    baz = 1337;
++
++    // issue 3043 - types with interior mutability should not trigger this lint
++    use std::cell::Cell;
++    let mut val = Cell::new(1);
++    if true {
++        val = Cell::new(2);
++    }
++    println!("{}", val.get());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c53a63a541bc9d9045f62a540620d2df68077606
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++error: `if _ { .. } else { .. }` is an expression
++  --> $DIR/let_if_seq.rs:63:5
++   |
++LL | /     let mut foo = 0;
++LL | |     if f() {
++LL | |         foo = 42;
++LL | |     }
++   | |_____^ help: it is more idiomatic to write: `let <mut> foo = if f() { 42 } else { 0 };`
++   |
++   = note: `-D clippy::useless-let-if-seq` implied by `-D warnings`
++   = note: you might not need `mut` at all
++
++error: `if _ { .. } else { .. }` is an expression
++  --> $DIR/let_if_seq.rs:68:5
++   |
++LL | /     let mut bar = 0;
++LL | |     if f() {
++LL | |         f();
++LL | |         bar = 42;
++LL | |     } else {
++LL | |         f();
++LL | |     }
++   | |_____^ help: it is more idiomatic to write: `let <mut> bar = if f() { ..; 42 } else { ..; 0 };`
++   |
++   = note: you might not need `mut` at all
++
++error: `if _ { .. } else { .. }` is an expression
++  --> $DIR/let_if_seq.rs:76:5
++   |
++LL | /     let quz;
++LL | |     if f() {
++LL | |         quz = 42;
++LL | |     } else {
++LL | |         quz = 0;
++LL | |     }
++   | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };`
++
++error: `if _ { .. } else { .. }` is an expression
++  --> $DIR/let_if_seq.rs:105:5
++   |
++LL | /     let mut baz = 0;
++LL | |     if f() {
++LL | |         baz = 42;
++LL | |     }
++   | |_____^ help: it is more idiomatic to write: `let <mut> baz = if f() { 42 } else { 0 };`
++   |
++   = note: you might not need `mut` at all
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..23645d48fe79913acf7f7c856ca5fa846ee2ee72
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,70 @@@
++#![allow(unused)]
++#![warn(clippy::let_and_return)]
++
++fn test() -> i32 {
++    let _y = 0; // no warning
++    let x = 5;
++    x
++}
++
++fn test_inner() -> i32 {
++    if true {
++        let x = 5;
++        x
++    } else {
++        0
++    }
++}
++
++fn test_nowarn_1() -> i32 {
++    let mut x = 5;
++    x += 1;
++    x
++}
++
++fn test_nowarn_2() -> i32 {
++    let x = 5;
++    x + 1
++}
++
++fn test_nowarn_3() -> (i32, i32) {
++    // this should technically warn, but we do not compare complex patterns
++    let (x, y) = (5, 9);
++    (x, y)
++}
++
++fn test_nowarn_4() -> i32 {
++    // this should technically warn, but not b/c of clippy::let_and_return, but b/c of useless type
++    let x: i32 = 5;
++    x
++}
++
++fn test_nowarn_5(x: i16) -> u16 {
++    #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
++    let x = x as u16;
++    x
++}
++
++// False positive example
++trait Decode {
++    fn decode<D: std::io::Read>(d: D) -> Result<Self, ()>
++    where
++        Self: Sized;
++}
++
++macro_rules! tuple_encode {
++    ($($x:ident),*) => (
++        impl<$($x: Decode),*> Decode for ($($x),*) {
++            #[inline]
++            #[allow(non_snake_case)]
++            fn decode<D: std::io::Read>(mut d: D) -> Result<Self, ()> {
++                // Shouldn't trigger lint
++                Ok(($({let $x = Decode::decode(&mut d)?; $x }),*))
++            }
++        }
++    );
++}
++
++tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7);
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..128a22c86e36079026be192ca7c52225ded17908
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++error: returning the result of a `let` binding from a block
++  --> $DIR/let_return.rs:7:5
++   |
++LL |     let x = 5;
++   |     ---------- unnecessary `let` binding
++LL |     x
++   |     ^
++   |
++   = note: `-D clippy::let-and-return` implied by `-D warnings`
++help: return the expression directly
++   |
++LL |     
++LL |     5
++   |
++
++error: returning the result of a `let` binding from a block
++  --> $DIR/let_return.rs:13:9
++   |
++LL |         let x = 5;
++   |         ---------- unnecessary `let` binding
++LL |         x
++   |         ^
++   |
++help: return the expression directly
++   |
++LL |         
++LL |         5
++   |
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..88fb216a74329597eb2c0d2fe29f696b952ca30f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++#![warn(clippy::let_underscore_lock)]
++
++fn main() {
++    let m = std::sync::Mutex::new(());
++    let rw = std::sync::RwLock::new(());
++
++    let _ = m.lock();
++    let _ = rw.read();
++    let _ = rw.write();
++    let _ = m.try_lock();
++    let _ = rw.try_read();
++    let _ = rw.try_write();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d5f6059ef13e93b51387b3a2adca96142526727
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++error: non-binding let on a synchronization lock
++  --> $DIR/let_underscore_lock.rs:7:5
++   |
++LL |     let _ = m.lock();
++   |     ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::let-underscore-lock` implied by `-D warnings`
++   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
++
++error: non-binding let on a synchronization lock
++  --> $DIR/let_underscore_lock.rs:8:5
++   |
++LL |     let _ = rw.read();
++   |     ^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
++
++error: non-binding let on a synchronization lock
++  --> $DIR/let_underscore_lock.rs:9:5
++   |
++LL |     let _ = rw.write();
++   |     ^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
++
++error: non-binding let on a synchronization lock
++  --> $DIR/let_underscore_lock.rs:10:5
++   |
++LL |     let _ = m.try_lock();
++   |     ^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
++
++error: non-binding let on a synchronization lock
++  --> $DIR/let_underscore_lock.rs:11:5
++   |
++LL |     let _ = rw.try_read();
++   |     ^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
++
++error: non-binding let on a synchronization lock
++  --> $DIR/let_underscore_lock.rs:12:5
++   |
++LL |     let _ = rw.try_write();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..27dda606067aa7fa8760316cb468c33111d4254e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,94 @@@
++#![warn(clippy::let_underscore_must_use)]
++
++// Debug implementations can fire this lint,
++// so we shouldn't lint external macros
++#[derive(Debug)]
++struct Foo {
++    field: i32,
++}
++
++#[must_use]
++fn f() -> u32 {
++    0
++}
++
++fn g() -> Result<u32, u32> {
++    Ok(0)
++}
++
++#[must_use]
++fn l<T>(x: T) -> T {
++    x
++}
++
++fn h() -> u32 {
++    0
++}
++
++struct S {}
++
++impl S {
++    #[must_use]
++    pub fn f(&self) -> u32 {
++        0
++    }
++
++    pub fn g(&self) -> Result<u32, u32> {
++        Ok(0)
++    }
++
++    fn k(&self) -> u32 {
++        0
++    }
++
++    #[must_use]
++    fn h() -> u32 {
++        0
++    }
++
++    fn p() -> Result<u32, u32> {
++        Ok(0)
++    }
++}
++
++trait Trait {
++    #[must_use]
++    fn a() -> u32;
++}
++
++impl Trait for S {
++    fn a() -> u32 {
++        0
++    }
++}
++
++fn main() {
++    let _ = f();
++    let _ = g();
++    let _ = h();
++    let _ = l(0_u32);
++
++    let s = S {};
++
++    let _ = s.f();
++    let _ = s.g();
++    let _ = s.k();
++
++    let _ = S::h();
++    let _ = S::p();
++
++    let _ = S::a();
++
++    let _ = if true { Ok(()) } else { Err(()) };
++
++    let a = Result::<(), ()>::Ok(());
++
++    let _ = a.is_ok();
++
++    let _ = a.map(|_| ());
++
++    let _ = a;
++
++    #[allow(clippy::let_underscore_must_use)]
++    let _ = a;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..447f2419e3bdb50932203b3ca2af5d7a33a3b1a8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,99 @@@
++error: non-binding let on a result of a `#[must_use]` function
++  --> $DIR/let_underscore_must_use.rs:66:5
++   |
++LL |     let _ = f();
++   |     ^^^^^^^^^^^^
++   |
++   = note: `-D clippy::let-underscore-must-use` implied by `-D warnings`
++   = help: consider explicitly using function result
++
++error: non-binding let on an expression with `#[must_use]` type
++  --> $DIR/let_underscore_must_use.rs:67:5
++   |
++LL |     let _ = g();
++   |     ^^^^^^^^^^^^
++   |
++   = help: consider explicitly using expression value
++
++error: non-binding let on a result of a `#[must_use]` function
++  --> $DIR/let_underscore_must_use.rs:69:5
++   |
++LL |     let _ = l(0_u32);
++   |     ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider explicitly using function result
++
++error: non-binding let on a result of a `#[must_use]` function
++  --> $DIR/let_underscore_must_use.rs:73:5
++   |
++LL |     let _ = s.f();
++   |     ^^^^^^^^^^^^^^
++   |
++   = help: consider explicitly using function result
++
++error: non-binding let on an expression with `#[must_use]` type
++  --> $DIR/let_underscore_must_use.rs:74:5
++   |
++LL |     let _ = s.g();
++   |     ^^^^^^^^^^^^^^
++   |
++   = help: consider explicitly using expression value
++
++error: non-binding let on a result of a `#[must_use]` function
++  --> $DIR/let_underscore_must_use.rs:77:5
++   |
++LL |     let _ = S::h();
++   |     ^^^^^^^^^^^^^^^
++   |
++   = help: consider explicitly using function result
++
++error: non-binding let on an expression with `#[must_use]` type
++  --> $DIR/let_underscore_must_use.rs:78:5
++   |
++LL |     let _ = S::p();
++   |     ^^^^^^^^^^^^^^^
++   |
++   = help: consider explicitly using expression value
++
++error: non-binding let on a result of a `#[must_use]` function
++  --> $DIR/let_underscore_must_use.rs:80:5
++   |
++LL |     let _ = S::a();
++   |     ^^^^^^^^^^^^^^^
++   |
++   = help: consider explicitly using function result
++
++error: non-binding let on an expression with `#[must_use]` type
++  --> $DIR/let_underscore_must_use.rs:82:5
++   |
++LL |     let _ = if true { Ok(()) } else { Err(()) };
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider explicitly using expression value
++
++error: non-binding let on a result of a `#[must_use]` function
++  --> $DIR/let_underscore_must_use.rs:86:5
++   |
++LL |     let _ = a.is_ok();
++   |     ^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider explicitly using function result
++
++error: non-binding let on an expression with `#[must_use]` type
++  --> $DIR/let_underscore_must_use.rs:88:5
++   |
++LL |     let _ = a.map(|_| ());
++   |     ^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider explicitly using expression value
++
++error: non-binding let on an expression with `#[must_use]` type
++  --> $DIR/let_underscore_must_use.rs:90:5
++   |
++LL |     let _ = a;
++   |     ^^^^^^^^^^
++   |
++   = help: consider explicitly using expression value
++
++error: aborting due to 12 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f398edc23cb5e0bdd019bfea348570872fb5d39d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++// run-rustfix
++
++#![warn(clippy::let_unit_value)]
++#![allow(clippy::no_effect)]
++#![allow(unused_variables)]
++
++macro_rules! let_and_return {
++    ($n:expr) => {{
++        let ret = $n;
++    }};
++}
++
++fn main() {
++    println!("x");
++    let _y = 1; // this is fine
++    let _z = ((), 1); // this as well
++    if true {
++        ();
++    }
++
++    consume_units_with_for_loop(); // should be fine as well
++
++    multiline_sugg();
++
++    let_and_return!(()) // should be fine
++}
++
++// Related to issue #1964
++fn consume_units_with_for_loop() {
++    // `for_let_unit` lint should not be triggered by consuming them using for loop.
++    let v = vec![(), (), ()];
++    let mut count = 0;
++    for _ in v {
++        count += 1;
++    }
++    assert_eq!(count, 3);
++
++    // Same for consuming from some other Iterator<Item = ()>.
++    let (tx, rx) = ::std::sync::mpsc::channel();
++    tx.send(()).unwrap();
++    drop(tx);
++
++    count = 0;
++    for _ in rx.iter() {
++        count += 1;
++    }
++    assert_eq!(count, 1);
++}
++
++fn multiline_sugg() {
++    let v: Vec<u8> = vec![2];
++
++    v
++        .into_iter()
++        .map(|i| i * 2)
++        .filter(|i| i % 2 == 0)
++        .map(|_| ())
++        .next()
++        .unwrap();
++}
++
++#[derive(Copy, Clone)]
++pub struct ContainsUnit(()); // should be fine
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..af5b1fb2ac7e4d526eb6eaa47025d75db8a7992d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++// run-rustfix
++
++#![warn(clippy::let_unit_value)]
++#![allow(clippy::no_effect)]
++#![allow(unused_variables)]
++
++macro_rules! let_and_return {
++    ($n:expr) => {{
++        let ret = $n;
++    }};
++}
++
++fn main() {
++    let _x = println!("x");
++    let _y = 1; // this is fine
++    let _z = ((), 1); // this as well
++    if true {
++        let _a = ();
++    }
++
++    consume_units_with_for_loop(); // should be fine as well
++
++    multiline_sugg();
++
++    let_and_return!(()) // should be fine
++}
++
++// Related to issue #1964
++fn consume_units_with_for_loop() {
++    // `for_let_unit` lint should not be triggered by consuming them using for loop.
++    let v = vec![(), (), ()];
++    let mut count = 0;
++    for _ in v {
++        count += 1;
++    }
++    assert_eq!(count, 3);
++
++    // Same for consuming from some other Iterator<Item = ()>.
++    let (tx, rx) = ::std::sync::mpsc::channel();
++    tx.send(()).unwrap();
++    drop(tx);
++
++    count = 0;
++    for _ in rx.iter() {
++        count += 1;
++    }
++    assert_eq!(count, 1);
++}
++
++fn multiline_sugg() {
++    let v: Vec<u8> = vec![2];
++
++    let _ = v
++        .into_iter()
++        .map(|i| i * 2)
++        .filter(|i| i % 2 == 0)
++        .map(|_| ())
++        .next()
++        .unwrap();
++}
++
++#[derive(Copy, Clone)]
++pub struct ContainsUnit(()); // should be fine
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eb8482087bcc87711d4c67fbfc0a60115754a04a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++error: this let-binding has unit value
++  --> $DIR/let_unit.rs:14:5
++   |
++LL |     let _x = println!("x");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `println!("x");`
++   |
++   = note: `-D clippy::let-unit-value` implied by `-D warnings`
++
++error: this let-binding has unit value
++  --> $DIR/let_unit.rs:18:9
++   |
++LL |         let _a = ();
++   |         ^^^^^^^^^^^^ help: omit the `let` binding: `();`
++
++error: this let-binding has unit value
++  --> $DIR/let_unit.rs:53:5
++   |
++LL | /     let _ = v
++LL | |         .into_iter()
++LL | |         .map(|i| i * 2)
++LL | |         .filter(|i| i % 2 == 0)
++LL | |         .map(|_| ())
++LL | |         .next()
++LL | |         .unwrap();
++   | |__________________^
++   |
++help: omit the `let` binding
++   |
++LL |     v
++LL |         .into_iter()
++LL |         .map(|i| i * 2)
++LL |         .filter(|i| i % 2 == 0)
++LL |         .map(|_| ())
++LL |         .next()
++ ...
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..beaef79a340afad2c371352f2dda86f385c95d8c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++#![deny(clippy::internal)]
++#![feature(rustc_private)]
++
++#[macro_use]
++extern crate rustc_middle;
++#[macro_use]
++extern crate rustc_session;
++extern crate rustc_lint;
++use rustc_lint::LintPass;
++
++declare_tool_lint! {
++    pub clippy::TEST_LINT,
++    Warn,
++    "",
++    report_in_external_macro: true
++}
++
++declare_tool_lint! {
++    pub clippy::TEST_LINT_REGISTERED,
++    Warn,
++    "",
++    report_in_external_macro: true
++}
++
++declare_tool_lint! {
++    pub clippy::TEST_LINT_REGISTERED_ONLY_IMPL,
++    Warn,
++    "",
++    report_in_external_macro: true
++}
++
++pub struct Pass;
++impl LintPass for Pass {
++    fn name(&self) -> &'static str {
++        "TEST_LINT"
++    }
++}
++
++declare_lint_pass!(Pass2 => [TEST_LINT_REGISTERED]);
++
++pub struct Pass3;
++impl_lint_pass!(Pass3 => [TEST_LINT_REGISTERED_ONLY_IMPL]);
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1257dae96d71cc17c0261ff5f66efcea3575c165
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++error: the lint `TEST_LINT` is not added to any `LintPass`
++  --> $DIR/lint_without_lint_pass.rs:11:1
++   |
++LL | / declare_tool_lint! {
++LL | |     pub clippy::TEST_LINT,
++LL | |     Warn,
++LL | |     "",
++LL | |     report_in_external_macro: true
++LL | | }
++   | |_^
++   |
++note: the lint level is defined here
++  --> $DIR/lint_without_lint_pass.rs:1:9
++   |
++LL | #![deny(clippy::internal)]
++   |         ^^^^^^^^^^^^^^^^
++   = note: `#[deny(clippy::lint_without_lint_pass)]` implied by `#[deny(clippy::internal)]`
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c299b16c8ce8527686ec676c1657317a342fc5e9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++// does not test any rustfixable lints
++
++#![warn(clippy::mixed_case_hex_literals)]
++#![warn(clippy::zero_prefixed_literal)]
++#![allow(clippy::unseparated_literal_suffix)]
++#![allow(dead_code)]
++
++fn main() {
++    let ok1 = 0xABCD;
++    let ok3 = 0xab_cd;
++    let ok4 = 0xab_cd_i32;
++    let ok5 = 0xAB_CD_u32;
++    let ok5 = 0xAB_CD_isize;
++    let fail1 = 0xabCD;
++    let fail2 = 0xabCD_u32;
++    let fail2 = 0xabCD_isize;
++    let fail_multi_zero = 000_123usize;
++
++    let ok9 = 0;
++    let ok10 = 0_i64;
++    let fail8 = 0123;
++
++    let ok11 = 0o123;
++    let ok12 = 0b10_1010;
++
++    let ok13 = 0xab_abcd;
++    let ok14 = 0xBAFE_BAFE;
++    let ok15 = 0xab_cabc_abca_bcab_cabc;
++    let ok16 = 0xFE_BAFE_ABAB_ABCD;
++    let ok17 = 0x123_4567_8901_usize;
++    let ok18 = 0xF;
++
++    let fail19 = 12_3456_21;
++    let fail22 = 3__4___23;
++    let fail23 = 3__16___23;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0b3af2d8bc35fea0b4a2fcace4f6cf7a1ad1e8d3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,73 @@@
++error: inconsistent casing in hexadecimal literal
++  --> $DIR/literals.rs:14:17
++   |
++LL |     let fail1 = 0xabCD;
++   |                 ^^^^^^
++   |
++   = note: `-D clippy::mixed-case-hex-literals` implied by `-D warnings`
++
++error: inconsistent casing in hexadecimal literal
++  --> $DIR/literals.rs:15:17
++   |
++LL |     let fail2 = 0xabCD_u32;
++   |                 ^^^^^^^^^^
++
++error: inconsistent casing in hexadecimal literal
++  --> $DIR/literals.rs:16:17
++   |
++LL |     let fail2 = 0xabCD_isize;
++   |                 ^^^^^^^^^^^^
++
++error: this is a decimal constant
++  --> $DIR/literals.rs:17:27
++   |
++LL |     let fail_multi_zero = 000_123usize;
++   |                           ^^^^^^^^^^^^
++   |
++   = note: `-D clippy::zero-prefixed-literal` implied by `-D warnings`
++help: if you mean to use a decimal constant, remove the `0` to avoid confusion
++   |
++LL |     let fail_multi_zero = 123usize;
++   |                           ^^^^^^^^
++help: if you mean to use an octal constant, use `0o`
++   |
++LL |     let fail_multi_zero = 0o123usize;
++   |                           ^^^^^^^^^^
++
++error: this is a decimal constant
++  --> $DIR/literals.rs:21:17
++   |
++LL |     let fail8 = 0123;
++   |                 ^^^^
++   |
++help: if you mean to use a decimal constant, remove the `0` to avoid confusion
++   |
++LL |     let fail8 = 123;
++   |                 ^^^
++help: if you mean to use an octal constant, use `0o`
++   |
++LL |     let fail8 = 0o123;
++   |                 ^^^^^
++
++error: digits grouped inconsistently by underscores
++  --> $DIR/literals.rs:33:18
++   |
++LL |     let fail19 = 12_3456_21;
++   |                  ^^^^^^^^^^ help: consider: `12_345_621`
++   |
++   = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings`
++
++error: digits grouped inconsistently by underscores
++  --> $DIR/literals.rs:34:18
++   |
++LL |     let fail22 = 3__4___23;
++   |                  ^^^^^^^^^ help: consider: `3_423`
++
++error: digits grouped inconsistently by underscores
++  --> $DIR/literals.rs:35:18
++   |
++LL |     let fail23 = 3__16___23;
++   |                  ^^^^^^^^^^ help: consider: `31_623`
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b4163d776e73abf4da6f4c565b50d4386052a1c5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++#![allow(unused, clippy::many_single_char_names)]
++#![warn(clippy::logic_bug)]
++
++fn main() {
++    let a: bool = unimplemented!();
++    let b: bool = unimplemented!();
++    let c: bool = unimplemented!();
++    let d: bool = unimplemented!();
++    let e: bool = unimplemented!();
++    let _ = a && b || a;
++    let _ = !(a && b);
++    let _ = false && a;
++    // don't lint on cfgs
++    let _ = cfg!(you_shall_not_not_pass) && a;
++    let _ = a || !b || !c || !d || !e;
++    let _ = !(a && b || c);
++}
++
++fn equality_stuff() {
++    let a: i32 = unimplemented!();
++    let b: i32 = unimplemented!();
++    let _ = a == b && a != b;
++    let _ = a < b && a >= b;
++    let _ = a > b && a <= b;
++    let _ = a > b && a == b;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8f55e1c8ad859ee7f2ca6c12d52989717515de1f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++error: this boolean expression contains a logic bug
++  --> $DIR/logic_bug.rs:10:13
++   |
++LL |     let _ = a && b || a;
++   |             ^^^^^^^^^^^ help: it would look like the following: `a`
++   |
++   = note: `-D clippy::logic-bug` implied by `-D warnings`
++help: this expression can be optimized out by applying boolean operations to the outer expression
++  --> $DIR/logic_bug.rs:10:18
++   |
++LL |     let _ = a && b || a;
++   |                  ^
++
++error: this boolean expression contains a logic bug
++  --> $DIR/logic_bug.rs:12:13
++   |
++LL |     let _ = false && a;
++   |             ^^^^^^^^^^ help: it would look like the following: `false`
++   |
++help: this expression can be optimized out by applying boolean operations to the outer expression
++  --> $DIR/logic_bug.rs:12:22
++   |
++LL |     let _ = false && a;
++   |                      ^
++
++error: this boolean expression contains a logic bug
++  --> $DIR/logic_bug.rs:22:13
++   |
++LL |     let _ = a == b && a != b;
++   |             ^^^^^^^^^^^^^^^^ help: it would look like the following: `false`
++   |
++help: this expression can be optimized out by applying boolean operations to the outer expression
++  --> $DIR/logic_bug.rs:22:13
++   |
++LL |     let _ = a == b && a != b;
++   |             ^^^^^^
++
++error: this boolean expression contains a logic bug
++  --> $DIR/logic_bug.rs:23:13
++   |
++LL |     let _ = a < b && a >= b;
++   |             ^^^^^^^^^^^^^^^ help: it would look like the following: `false`
++   |
++help: this expression can be optimized out by applying boolean operations to the outer expression
++  --> $DIR/logic_bug.rs:23:13
++   |
++LL |     let _ = a < b && a >= b;
++   |             ^^^^^
++
++error: this boolean expression contains a logic bug
++  --> $DIR/logic_bug.rs:24:13
++   |
++LL |     let _ = a > b && a <= b;
++   |             ^^^^^^^^^^^^^^^ help: it would look like the following: `false`
++   |
++help: this expression can be optimized out by applying boolean operations to the outer expression
++  --> $DIR/logic_bug.rs:24:13
++   |
++LL |     let _ = a > b && a <= b;
++   |             ^^^^^
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..24e372354fc0517541ffe95684112d6cd08e55fe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++// run-rustfix
++#![warn(clippy::lossy_float_literal)]
++
++fn main() {
++    // Lossy whole-number float literals
++    let _: f32 = 16_777_216.0;
++    let _: f32 = 16_777_220.0;
++    let _: f32 = 16_777_220.0;
++    let _: f32 = 16_777_220.0;
++    let _ = 16_777_220_f32;
++    let _: f32 = -16_777_220.0;
++    let _: f64 = 9_007_199_254_740_992.0;
++    let _: f64 = 9_007_199_254_740_992.0;
++    let _: f64 = 9_007_199_254_740_992.0;
++    let _ = 9_007_199_254_740_992_f64;
++    let _: f64 = -9_007_199_254_740_992.0;
++
++    // Lossless whole number float literals
++    let _: f32 = 16_777_216.0;
++    let _: f32 = 16_777_218.0;
++    let _: f32 = 16_777_220.0;
++    let _: f32 = -16_777_216.0;
++    let _: f32 = -16_777_220.0;
++    let _: f64 = 16_777_217.0;
++    let _: f64 = -16_777_217.0;
++    let _: f64 = 9_007_199_254_740_992.0;
++    let _: f64 = -9_007_199_254_740_992.0;
++
++    // Ignored whole number float literals
++    let _: f32 = 1e25;
++    let _: f32 = 1E25;
++    let _: f64 = 1e99;
++    let _: f64 = 1E99;
++    let _: f32 = 0.1;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3dcf98fa0bddadd173477396c2d8a3f29f476302
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++// run-rustfix
++#![warn(clippy::lossy_float_literal)]
++
++fn main() {
++    // Lossy whole-number float literals
++    let _: f32 = 16_777_217.0;
++    let _: f32 = 16_777_219.0;
++    let _: f32 = 16_777_219.;
++    let _: f32 = 16_777_219.000;
++    let _ = 16_777_219f32;
++    let _: f32 = -16_777_219.0;
++    let _: f64 = 9_007_199_254_740_993.0;
++    let _: f64 = 9_007_199_254_740_993.;
++    let _: f64 = 9_007_199_254_740_993.00;
++    let _ = 9_007_199_254_740_993f64;
++    let _: f64 = -9_007_199_254_740_993.0;
++
++    // Lossless whole number float literals
++    let _: f32 = 16_777_216.0;
++    let _: f32 = 16_777_218.0;
++    let _: f32 = 16_777_220.0;
++    let _: f32 = -16_777_216.0;
++    let _: f32 = -16_777_220.0;
++    let _: f64 = 16_777_217.0;
++    let _: f64 = -16_777_217.0;
++    let _: f64 = 9_007_199_254_740_992.0;
++    let _: f64 = -9_007_199_254_740_992.0;
++
++    // Ignored whole number float literals
++    let _: f32 = 1e25;
++    let _: f32 = 1E25;
++    let _: f64 = 1e99;
++    let _: f64 = 1E99;
++    let _: f32 = 0.1;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d2193c0c819555f73b451f2959d234ca0afbb0a8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,70 @@@
++error: literal cannot be represented as the underlying type without loss of precision
++  --> $DIR/lossy_float_literal.rs:6:18
++   |
++LL |     let _: f32 = 16_777_217.0;
++   |                  ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_216.0`
++   |
++   = note: `-D clippy::lossy-float-literal` implied by `-D warnings`
++
++error: literal cannot be represented as the underlying type without loss of precision
++  --> $DIR/lossy_float_literal.rs:7:18
++   |
++LL |     let _: f32 = 16_777_219.0;
++   |                  ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
++
++error: literal cannot be represented as the underlying type without loss of precision
++  --> $DIR/lossy_float_literal.rs:8:18
++   |
++LL |     let _: f32 = 16_777_219.;
++   |                  ^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
++
++error: literal cannot be represented as the underlying type without loss of precision
++  --> $DIR/lossy_float_literal.rs:9:18
++   |
++LL |     let _: f32 = 16_777_219.000;
++   |                  ^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
++
++error: literal cannot be represented as the underlying type without loss of precision
++  --> $DIR/lossy_float_literal.rs:10:13
++   |
++LL |     let _ = 16_777_219f32;
++   |             ^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220_f32`
++
++error: literal cannot be represented as the underlying type without loss of precision
++  --> $DIR/lossy_float_literal.rs:11:19
++   |
++LL |     let _: f32 = -16_777_219.0;
++   |                   ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
++
++error: literal cannot be represented as the underlying type without loss of precision
++  --> $DIR/lossy_float_literal.rs:12:18
++   |
++LL |     let _: f64 = 9_007_199_254_740_993.0;
++   |                  ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
++
++error: literal cannot be represented as the underlying type without loss of precision
++  --> $DIR/lossy_float_literal.rs:13:18
++   |
++LL |     let _: f64 = 9_007_199_254_740_993.;
++   |                  ^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
++
++error: literal cannot be represented as the underlying type without loss of precision
++  --> $DIR/lossy_float_literal.rs:14:18
++   |
++LL |     let _: f64 = 9_007_199_254_740_993.00;
++   |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
++
++error: literal cannot be represented as the underlying type without loss of precision
++  --> $DIR/lossy_float_literal.rs:15:13
++   |
++LL |     let _ = 9_007_199_254_740_993f64;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992_f64`
++
++error: literal cannot be represented as the underlying type without loss of precision
++  --> $DIR/lossy_float_literal.rs:16:19
++   |
++LL |     let _: f64 = -9_007_199_254_740_993.0;
++   |                   ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
++
++error: aborting due to 11 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..60c64ee8146e517f71f28f1c0f62c9600bea9fd0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++// edition:2018
++#![warn(clippy::macro_use_imports)]
++
++use std::collections::HashMap;
++#[macro_use]
++use std::prelude;
++
++fn main() {
++    let _ = HashMap::<u8, u8>::new();
++    println!();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b5e3dbec572772f2e9aaf7924e0f011eaaedd878
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: `macro_use` attributes are no longer needed in the Rust 2018 edition
++  --> $DIR/macro_use_imports.rs:5:1
++   |
++LL | #[macro_use]
++   | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use std::prelude::<macro name>`
++   |
++   = note: `-D clippy::macro-use-imports` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aa347288875d5cc5f89058de21964be3230e3f55
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,110 @@@
++#![warn(clippy::needless_range_loop, clippy::manual_memcpy)]
++
++const LOOP_OFFSET: usize = 5000;
++
++pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) {
++    // plain manual memcpy
++    for i in 0..src.len() {
++        dst[i] = src[i];
++    }
++
++    // dst offset memcpy
++    for i in 0..src.len() {
++        dst[i + 10] = src[i];
++    }
++
++    // src offset memcpy
++    for i in 0..src.len() {
++        dst[i] = src[i + 10];
++    }
++
++    // src offset memcpy
++    for i in 11..src.len() {
++        dst[i] = src[i - 10];
++    }
++
++    // overwrite entire dst
++    for i in 0..dst.len() {
++        dst[i] = src[i];
++    }
++
++    // manual copy with branch - can't easily convert to memcpy!
++    for i in 0..src.len() {
++        dst[i] = src[i];
++        if dst[i] > 5 {
++            break;
++        }
++    }
++
++    // multiple copies - suggest two memcpy statements
++    for i in 10..256 {
++        dst[i] = src[i - 5];
++        dst2[i + 500] = src[i]
++    }
++
++    // this is a reversal - the copy lint shouldn't be triggered
++    for i in 10..LOOP_OFFSET {
++        dst[i + LOOP_OFFSET] = src[LOOP_OFFSET - i];
++    }
++
++    let some_var = 5;
++    // Offset in variable
++    for i in 10..LOOP_OFFSET {
++        dst[i + LOOP_OFFSET] = src[i - some_var];
++    }
++
++    // Non continuous copy - don't trigger lint
++    for i in 0..10 {
++        dst[i + i] = src[i];
++    }
++
++    let src_vec = vec![1, 2, 3, 4, 5];
++    let mut dst_vec = vec![0, 0, 0, 0, 0];
++
++    // make sure vectors are supported
++    for i in 0..src_vec.len() {
++        dst_vec[i] = src_vec[i];
++    }
++
++    // lint should not trigger when either
++    // source or destination type is not
++    // slice-like, like DummyStruct
++    struct DummyStruct(i32);
++
++    impl ::std::ops::Index<usize> for DummyStruct {
++        type Output = i32;
++
++        fn index(&self, _: usize) -> &i32 {
++            &self.0
++        }
++    }
++
++    let src = DummyStruct(5);
++    let mut dst_vec = vec![0; 10];
++
++    for i in 0..10 {
++        dst_vec[i] = src[i];
++    }
++
++    // Simplify suggestion (issue #3004)
++    let src = [0, 1, 2, 3, 4];
++    let mut dst = [0, 0, 0, 0, 0, 0];
++    let from = 1;
++
++    for i in from..from + src.len() {
++        dst[i] = src[i - from];
++    }
++
++    for i in from..from + 3 {
++        dst[i] = src[i - from];
++    }
++}
++
++#[warn(clippy::needless_range_loop, clippy::manual_memcpy)]
++pub fn manual_clone(src: &[String], dst: &mut [String]) {
++    for i in 0..src.len() {
++        dst[i] = src[i].clone();
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3dbb2155d4de7e52effaabfcebbeaa5929806f0a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,76 @@@
++error: it looks like you're manually copying between slices
++  --> $DIR/manual_memcpy.rs:7:14
++   |
++LL |     for i in 0..src.len() {
++   |              ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])`
++   |
++   = note: `-D clippy::manual-memcpy` implied by `-D warnings`
++
++error: it looks like you're manually copying between slices
++  --> $DIR/manual_memcpy.rs:12:14
++   |
++LL |     for i in 0..src.len() {
++   |              ^^^^^^^^^^^^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..])`
++
++error: it looks like you're manually copying between slices
++  --> $DIR/manual_memcpy.rs:17:14
++   |
++LL |     for i in 0..src.len() {
++   |              ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..])`
++
++error: it looks like you're manually copying between slices
++  --> $DIR/manual_memcpy.rs:22:14
++   |
++LL |     for i in 11..src.len() {
++   |              ^^^^^^^^^^^^^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)])`
++
++error: it looks like you're manually copying between slices
++  --> $DIR/manual_memcpy.rs:27:14
++   |
++LL |     for i in 0..dst.len() {
++   |              ^^^^^^^^^^^^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()])`
++
++error: it looks like you're manually copying between slices
++  --> $DIR/manual_memcpy.rs:40:14
++   |
++LL |     for i in 10..256 {
++   |              ^^^^^^^
++   |
++help: try replacing the loop by
++   |
++LL |     for i in dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)])
++LL |     dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]) {
++   |
++
++error: it looks like you're manually copying between slices
++  --> $DIR/manual_memcpy.rs:52:14
++   |
++LL |     for i in 10..LOOP_OFFSET {
++   |              ^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)])`
++
++error: it looks like you're manually copying between slices
++  --> $DIR/manual_memcpy.rs:65:14
++   |
++LL |     for i in 0..src_vec.len() {
++   |              ^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..])`
++
++error: it looks like you're manually copying between slices
++  --> $DIR/manual_memcpy.rs:94:14
++   |
++LL |     for i in from..from + src.len() {
++   |              ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + src.len()].clone_from_slice(&src[0..(from + src.len() - from)])`
++
++error: it looks like you're manually copying between slices
++  --> $DIR/manual_memcpy.rs:98:14
++   |
++LL |     for i in from..from + 3 {
++   |              ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[0..(from + 3 - from)])`
++
++error: it looks like you're manually copying between slices
++  --> $DIR/manual_memcpy.rs:105:14
++   |
++LL |     for i in 0..src.len() {
++   |              ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])`
++
++error: aborting due to 11 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c4f53c446c9f74974a15455a3afb85d3d68250f6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++// run-rustfix
++
++#![allow(unused_imports)]
++
++use std::{i128, i32, u128, u32};
++
++fn main() {
++    let _ = 1u32.saturating_add(1);
++    let _ = 1u32.saturating_add(1);
++    let _ = 1u8.saturating_add(1);
++    let _ = 1u128.saturating_add(1);
++    let _ = 1u32.checked_add(1).unwrap_or(1234); // ok
++    let _ = 1u8.checked_add(1).unwrap_or(0); // ok
++    let _ = 1u32.saturating_mul(1);
++
++    let _ = 1u32.saturating_sub(1);
++    let _ = 1u32.saturating_sub(1);
++    let _ = 1u8.saturating_sub(1);
++    let _ = 1u32.checked_sub(1).unwrap_or(1234); // ok
++    let _ = 1u8.checked_sub(1).unwrap_or(255); // ok
++
++    let _ = 1i32.saturating_add(1);
++    let _ = 1i32.saturating_add(1);
++    let _ = 1i8.saturating_add(1);
++    let _ = 1i128.saturating_add(1);
++    let _ = 1i32.saturating_add(-1);
++    let _ = 1i32.saturating_add(-1);
++    let _ = 1i8.saturating_add(-1);
++    let _ = 1i128.saturating_add(-1);
++    let _ = 1i32.checked_add(1).unwrap_or(1234); // ok
++    let _ = 1i8.checked_add(1).unwrap_or(-128); // ok
++    let _ = 1i8.checked_add(-1).unwrap_or(127); // ok
++
++    let _ = 1i32.saturating_sub(1);
++    let _ = 1i32.saturating_sub(1);
++    let _ = 1i8.saturating_sub(1);
++    let _ = 1i128.saturating_sub(1);
++    let _ = 1i32.saturating_sub(-1);
++    let _ = 1i32.saturating_sub(-1);
++    let _ = 1i8.saturating_sub(-1);
++    let _ = 1i128.saturating_sub(-1);
++    let _ = 1i32.checked_sub(1).unwrap_or(1234); // ok
++    let _ = 1i8.checked_sub(1).unwrap_or(127); // ok
++    let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cd83cf6e65e9403b0ac0b8703f5f448293e0a975
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++// run-rustfix
++
++#![allow(unused_imports)]
++
++use std::{i128, i32, u128, u32};
++
++fn main() {
++    let _ = 1u32.checked_add(1).unwrap_or(u32::max_value());
++    let _ = 1u32.checked_add(1).unwrap_or(u32::MAX);
++    let _ = 1u8.checked_add(1).unwrap_or(255);
++    let _ = 1u128
++        .checked_add(1)
++        .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455);
++    let _ = 1u32.checked_add(1).unwrap_or(1234); // ok
++    let _ = 1u8.checked_add(1).unwrap_or(0); // ok
++    let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX);
++
++    let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value());
++    let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN);
++    let _ = 1u8.checked_sub(1).unwrap_or(0);
++    let _ = 1u32.checked_sub(1).unwrap_or(1234); // ok
++    let _ = 1u8.checked_sub(1).unwrap_or(255); // ok
++
++    let _ = 1i32.checked_add(1).unwrap_or(i32::max_value());
++    let _ = 1i32.checked_add(1).unwrap_or(i32::MAX);
++    let _ = 1i8.checked_add(1).unwrap_or(127);
++    let _ = 1i128
++        .checked_add(1)
++        .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
++    let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value());
++    let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN);
++    let _ = 1i8.checked_add(-1).unwrap_or(-128);
++    let _ = 1i128
++        .checked_add(-1)
++        .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
++    let _ = 1i32.checked_add(1).unwrap_or(1234); // ok
++    let _ = 1i8.checked_add(1).unwrap_or(-128); // ok
++    let _ = 1i8.checked_add(-1).unwrap_or(127); // ok
++
++    let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value());
++    let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN);
++    let _ = 1i8.checked_sub(1).unwrap_or(-128);
++    let _ = 1i128
++        .checked_sub(1)
++        .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
++    let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value());
++    let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX);
++    let _ = 1i8.checked_sub(-1).unwrap_or(127);
++    let _ = 1i128
++        .checked_sub(-1)
++        .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
++    let _ = 1i32.checked_sub(1).unwrap_or(1234); // ok
++    let _ = 1i8.checked_sub(1).unwrap_or(127); // ok
++    let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d985f2e754bc3e8e44b3a4ca264014bb39eede4c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,163 @@@
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:8:13
++   |
++LL |     let _ = 1u32.checked_add(1).unwrap_or(u32::max_value());
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u32.saturating_add(1)`
++   |
++   = note: `-D clippy::manual-saturating-arithmetic` implied by `-D warnings`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:9:13
++   |
++LL |     let _ = 1u32.checked_add(1).unwrap_or(u32::MAX);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u32.saturating_add(1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:10:13
++   |
++LL |     let _ = 1u8.checked_add(1).unwrap_or(255);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u8.saturating_add(1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:11:13
++   |
++LL |       let _ = 1u128
++   |  _____________^
++LL | |         .checked_add(1)
++LL | |         .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455);
++   | |_______________________________________________________________________^ help: try using `saturating_add`: `1u128.saturating_add(1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:16:13
++   |
++LL |     let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_mul`: `1u32.saturating_mul(1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:18:13
++   |
++LL |     let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value());
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u32.saturating_sub(1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:19:13
++   |
++LL |     let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u32.saturating_sub(1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:20:13
++   |
++LL |     let _ = 1u8.checked_sub(1).unwrap_or(0);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u8.saturating_sub(1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:24:13
++   |
++LL |     let _ = 1i32.checked_add(1).unwrap_or(i32::max_value());
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:25:13
++   |
++LL |     let _ = 1i32.checked_add(1).unwrap_or(i32::MAX);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:26:13
++   |
++LL |     let _ = 1i8.checked_add(1).unwrap_or(127);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i8.saturating_add(1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:27:13
++   |
++LL |       let _ = 1i128
++   |  _____________^
++LL | |         .checked_add(1)
++LL | |         .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
++   | |_______________________________________________________________________^ help: try using `saturating_add`: `1i128.saturating_add(1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:30:13
++   |
++LL |     let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value());
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(-1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:31:13
++   |
++LL |     let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(-1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:32:13
++   |
++LL |     let _ = 1i8.checked_add(-1).unwrap_or(-128);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i8.saturating_add(-1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:33:13
++   |
++LL |       let _ = 1i128
++   |  _____________^
++LL | |         .checked_add(-1)
++LL | |         .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
++   | |________________________________________________________________________^ help: try using `saturating_add`: `1i128.saturating_add(-1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:40:13
++   |
++LL |     let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value());
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:41:13
++   |
++LL |     let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:42:13
++   |
++LL |     let _ = 1i8.checked_sub(1).unwrap_or(-128);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i8.saturating_sub(1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:43:13
++   |
++LL |       let _ = 1i128
++   |  _____________^
++LL | |         .checked_sub(1)
++LL | |         .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
++   | |________________________________________________________________________^ help: try using `saturating_sub`: `1i128.saturating_sub(1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:46:13
++   |
++LL |     let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value());
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(-1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:47:13
++   |
++LL |     let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(-1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:48:13
++   |
++LL |     let _ = 1i8.checked_sub(-1).unwrap_or(127);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i8.saturating_sub(-1)`
++
++error: manual saturating arithmetic
++  --> $DIR/manual_saturating_arithmetic.rs:49:13
++   |
++LL |       let _ = 1i128
++   |  _____________^
++LL | |         .checked_sub(-1)
++LL | |         .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
++   | |_______________________________________________________________________^ help: try using `saturating_sub`: `1i128.saturating_sub(-1)`
++
++error: aborting due to 24 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..80800e487248fe52042fb9f15016a0f0a085098e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,73 @@@
++#[warn(clippy::many_single_char_names)]
++
++fn bla() {
++    let a: i32;
++    let (b, c, d): (i32, i64, i16);
++    {
++        {
++            let cdefg: i32;
++            let blar: i32;
++        }
++        {
++            let e: i32;
++        }
++        {
++            let e: i32;
++            let f: i32;
++        }
++        match 5 {
++            1 => println!(),
++            e => panic!(),
++        }
++        match 5 {
++            1 => println!(),
++            _ => panic!(),
++        }
++    }
++}
++
++fn bindings(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) {}
++
++fn bindings2() {
++    let (a, b, c, d, e, f, g, h): (bool, bool, bool, bool, bool, bool, bool, bool) = unimplemented!();
++}
++
++fn shadowing() {
++    let a = 0i32;
++    let a = 0i32;
++    let a = 0i32;
++    let a = 0i32;
++    let a = 0i32;
++    let a = 0i32;
++    {
++        let a = 0i32;
++    }
++}
++
++fn patterns() {
++    enum Z {
++        A(i32),
++        B(i32),
++        C(i32),
++        D(i32),
++        E(i32),
++        F(i32),
++    }
++
++    // These should not trigger a warning, since the pattern bindings are a new scope.
++    match Z::A(0) {
++        Z::A(a) => {},
++        Z::B(b) => {},
++        Z::C(c) => {},
++        Z::D(d) => {},
++        Z::E(e) => {},
++        Z::F(f) => {},
++    }
++}
++
++#[allow(clippy::many_single_char_names)]
++fn issue_3198_allow_works() {
++    let (a, b, c, d, e) = (0, 0, 0, 0, 0);
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..27e62e641ade9c2d3b208922bd7d3367c6d21f0a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++error: 5 bindings with single-character names in scope
++  --> $DIR/many_single_char_names.rs:4:9
++   |
++LL |     let a: i32;
++   |         ^
++LL |     let (b, c, d): (i32, i64, i16);
++   |          ^  ^  ^
++...
++LL |             let e: i32;
++   |                 ^
++   |
++   = note: `-D clippy::many-single-char-names` implied by `-D warnings`
++
++error: 6 bindings with single-character names in scope
++  --> $DIR/many_single_char_names.rs:4:9
++   |
++LL |     let a: i32;
++   |         ^
++LL |     let (b, c, d): (i32, i64, i16);
++   |          ^  ^  ^
++...
++LL |             let e: i32;
++   |                 ^
++LL |             let f: i32;
++   |                 ^
++
++error: 5 bindings with single-character names in scope
++  --> $DIR/many_single_char_names.rs:4:9
++   |
++LL |     let a: i32;
++   |         ^
++LL |     let (b, c, d): (i32, i64, i16);
++   |          ^  ^  ^
++...
++LL |             e => panic!(),
++   |             ^
++
++error: 8 bindings with single-character names in scope
++  --> $DIR/many_single_char_names.rs:29:13
++   |
++LL | fn bindings(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) {}
++   |             ^       ^       ^       ^       ^       ^       ^       ^
++
++error: 8 bindings with single-character names in scope
++  --> $DIR/many_single_char_names.rs:32:10
++   |
++LL |     let (a, b, c, d, e, f, g, h): (bool, bool, bool, bool, bool, bool, bool, bool) = unimplemented!();
++   |          ^  ^  ^  ^  ^  ^  ^  ^
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..81c7f659efb1fed41dbbd9433b9dd5a8c3d79ffe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++// run-rustfix
++#![warn(clippy::all, clippy::pedantic)]
++#![allow(clippy::iter_cloned_collect)]
++#![allow(clippy::clone_on_copy, clippy::redundant_clone)]
++#![allow(clippy::missing_docs_in_private_items)]
++#![allow(clippy::redundant_closure_for_method_calls)]
++#![allow(clippy::many_single_char_names)]
++
++fn main() {
++    let _: Vec<i8> = vec![5_i8; 6].iter().copied().collect();
++    let _: Vec<String> = vec![String::new()].iter().cloned().collect();
++    let _: Vec<u32> = vec![42, 43].iter().copied().collect();
++    let _: Option<u64> = Some(Box::new(16)).map(|b| *b);
++    let _: Option<u64> = Some(&16).copied();
++    let _: Option<u8> = Some(&1).copied();
++
++    // Don't lint these
++    let v = vec![5_i8; 6];
++    let a = 0;
++    let b = &a;
++    let _ = v.iter().map(|_x| *b);
++    let _ = v.iter().map(|_x| a.clone());
++    let _ = v.iter().map(|&_x| a);
++
++    // Issue #498
++    let _ = std::env::args();
++
++    // Issue #4824 item types that aren't references
++    {
++        use std::rc::Rc;
++
++        let o: Option<Rc<u32>> = Some(Rc::new(0_u32));
++        let _: Option<u32> = o.map(|x| *x);
++        let v: Vec<Rc<u32>> = vec![Rc::new(0_u32)];
++        let _: Vec<u32> = v.into_iter().map(|x| *x).collect();
++    }
++
++    // Issue #5524 mutable references
++    {
++        let mut c = 42;
++        let v = vec![&mut c];
++        let _: Vec<u32> = v.into_iter().map(|x| *x).collect();
++        let mut d = 21;
++        let v = vec![&mut d];
++        let _: Vec<u32> = v.into_iter().map(|&mut x| x).collect();
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8ed164f0ed51dabcf0d6e449351e427f6a1576d5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++// run-rustfix
++#![warn(clippy::all, clippy::pedantic)]
++#![allow(clippy::iter_cloned_collect)]
++#![allow(clippy::clone_on_copy, clippy::redundant_clone)]
++#![allow(clippy::missing_docs_in_private_items)]
++#![allow(clippy::redundant_closure_for_method_calls)]
++#![allow(clippy::many_single_char_names)]
++
++fn main() {
++    let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect();
++    let _: Vec<String> = vec![String::new()].iter().map(|x| x.clone()).collect();
++    let _: Vec<u32> = vec![42, 43].iter().map(|&x| x).collect();
++    let _: Option<u64> = Some(Box::new(16)).map(|b| *b);
++    let _: Option<u64> = Some(&16).map(|b| *b);
++    let _: Option<u8> = Some(&1).map(|x| x.clone());
++
++    // Don't lint these
++    let v = vec![5_i8; 6];
++    let a = 0;
++    let b = &a;
++    let _ = v.iter().map(|_x| *b);
++    let _ = v.iter().map(|_x| a.clone());
++    let _ = v.iter().map(|&_x| a);
++
++    // Issue #498
++    let _ = std::env::args().map(|v| v.clone());
++
++    // Issue #4824 item types that aren't references
++    {
++        use std::rc::Rc;
++
++        let o: Option<Rc<u32>> = Some(Rc::new(0_u32));
++        let _: Option<u32> = o.map(|x| *x);
++        let v: Vec<Rc<u32>> = vec![Rc::new(0_u32)];
++        let _: Vec<u32> = v.into_iter().map(|x| *x).collect();
++    }
++
++    // Issue #5524 mutable references
++    {
++        let mut c = 42;
++        let v = vec![&mut c];
++        let _: Vec<u32> = v.into_iter().map(|x| *x).collect();
++        let mut d = 21;
++        let v = vec![&mut d];
++        let _: Vec<u32> = v.into_iter().map(|&mut x| x).collect();
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9eec6928e8cee331d0b71086069eb322007877e1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: You are using an explicit closure for copying elements
++  --> $DIR/map_clone.rs:10:22
++   |
++LL |     let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect();
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()`
++   |
++   = note: `-D clippy::map-clone` implied by `-D warnings`
++
++error: You are using an explicit closure for cloning elements
++  --> $DIR/map_clone.rs:11:26
++   |
++LL |     let _: Vec<String> = vec![String::new()].iter().map(|x| x.clone()).collect();
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()`
++
++error: You are using an explicit closure for copying elements
++  --> $DIR/map_clone.rs:12:23
++   |
++LL |     let _: Vec<u32> = vec![42, 43].iter().map(|&x| x).collect();
++   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()`
++
++error: You are using an explicit closure for copying elements
++  --> $DIR/map_clone.rs:14:26
++   |
++LL |     let _: Option<u64> = Some(&16).map(|b| *b);
++   |                          ^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&16).copied()`
++
++error: You are using an explicit closure for copying elements
++  --> $DIR/map_clone.rs:15:25
++   |
++LL |     let _: Option<u8> = Some(&1).map(|x| x.clone());
++   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&1).copied()`
++
++error: You are needlessly cloning iterator elements
++  --> $DIR/map_clone.rs:26:29
++   |
++LL |     let _ = std::env::args().map(|v| v.clone());
++   |                             ^^^^^^^^^^^^^^^^^^^ help: Remove the `map` call
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7ac368878ab7ea341ea7c71c4896eb7be5cf6813
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++// run-rustfix
++
++#![warn(clippy::all, clippy::pedantic)]
++#![allow(clippy::missing_docs_in_private_items)]
++
++fn main() {
++    let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect();
++    let _: Option<_> = (Some(Some(1))).and_then(|x| x);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a608601039ce783c80e3dfa2a3abdc4d94ea5dc5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++// run-rustfix
++
++#![warn(clippy::all, clippy::pedantic)]
++#![allow(clippy::missing_docs_in_private_items)]
++
++fn main() {
++    let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
++    let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3cf2abd5b6d85a506a47e9fda694420c36bc3aff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)`
++  --> $DIR/map_flatten.rs:7:21
++   |
++LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)`
++   |
++   = note: `-D clippy::map-flatten` implied by `-D warnings`
++
++error: called `map(..).flatten()` on an `Option`. This is more succinctly expressed by calling `.and_then(..)`
++  --> $DIR/map_flatten.rs:8:24
++   |
++LL |     let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
++   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `(Some(Some(1))).and_then(|x| x)`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9a74da4e3b8b641ef4387246ab1f24ea7800de4d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++#![allow(unused)]
++struct Mappable {}
++
++impl Mappable {
++    pub fn map(&self) {}
++}
++
++fn main() {
++    let m = Mappable {};
++    m.map();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c61eb9216643e14159ca5d6fb26b93764b05e264
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++// run-rustfix
++
++#![allow(unused)]
++#![warn(clippy::match_as_ref)]
++
++fn match_as_ref() {
++    let owned: Option<()> = None;
++    let borrowed: Option<&()> = owned.as_ref();
++
++    let mut mut_owned: Option<()> = None;
++    let borrow_mut: Option<&mut ()> = mut_owned.as_mut();
++}
++
++mod issue4437 {
++    use std::{error::Error, fmt, num::ParseIntError};
++
++    #[derive(Debug)]
++    struct E {
++        source: Option<ParseIntError>,
++    }
++
++    impl Error for E {
++        fn source(&self) -> Option<&(dyn Error + 'static)> {
++            self.source.as_ref().map(|x| x as _)
++        }
++    }
++
++    impl fmt::Display for E {
++        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++            unimplemented!()
++        }
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2fbd0b255faae6d8c7f18a986620cef94aefdb49
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++// run-rustfix
++
++#![allow(unused)]
++#![warn(clippy::match_as_ref)]
++
++fn match_as_ref() {
++    let owned: Option<()> = None;
++    let borrowed: Option<&()> = match owned {
++        None => None,
++        Some(ref v) => Some(v),
++    };
++
++    let mut mut_owned: Option<()> = None;
++    let borrow_mut: Option<&mut ()> = match mut_owned {
++        None => None,
++        Some(ref mut v) => Some(v),
++    };
++}
++
++mod issue4437 {
++    use std::{error::Error, fmt, num::ParseIntError};
++
++    #[derive(Debug)]
++    struct E {
++        source: Option<ParseIntError>,
++    }
++
++    impl Error for E {
++        fn source(&self) -> Option<&(dyn Error + 'static)> {
++            match self.source {
++                Some(ref s) => Some(s),
++                None => None,
++            }
++        }
++    }
++
++    impl fmt::Display for E {
++        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++            unimplemented!()
++        }
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c3b62849cb33f5823d506a5dcea836b29705536b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++error: use `as_ref()` instead
++  --> $DIR/match_as_ref.rs:8:33
++   |
++LL |       let borrowed: Option<&()> = match owned {
++   |  _________________________________^
++LL | |         None => None,
++LL | |         Some(ref v) => Some(v),
++LL | |     };
++   | |_____^ help: try this: `owned.as_ref()`
++   |
++   = note: `-D clippy::match-as-ref` implied by `-D warnings`
++
++error: use `as_mut()` instead
++  --> $DIR/match_as_ref.rs:14:39
++   |
++LL |       let borrow_mut: Option<&mut ()> = match mut_owned {
++   |  _______________________________________^
++LL | |         None => None,
++LL | |         Some(ref mut v) => Some(v),
++LL | |     };
++   | |_____^ help: try this: `mut_owned.as_mut()`
++
++error: use `as_ref()` instead
++  --> $DIR/match_as_ref.rs:30:13
++   |
++LL | /             match self.source {
++LL | |                 Some(ref s) => Some(s),
++LL | |                 None => None,
++LL | |             }
++   | |_____________^ help: try this: `self.source.as_ref().map(|x| x as _)`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9ed55ca7ae7f9cef1bc2eb858a853d7a9a33d351
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++#![deny(clippy::match_bool)]
++
++fn match_bool() {
++    let test: bool = true;
++
++    match test {
++        true => 0,
++        false => 42,
++    };
++
++    let option = 1;
++    match option == 1 {
++        true => 1,
++        false => 0,
++    };
++
++    match test {
++        true => (),
++        false => {
++            println!("Noooo!");
++        },
++    };
++
++    match test {
++        false => {
++            println!("Noooo!");
++        },
++        _ => (),
++    };
++
++    match test && test {
++        false => {
++            println!("Noooo!");
++        },
++        _ => (),
++    };
++
++    match test {
++        false => {
++            println!("Noooo!");
++        },
++        true => {
++            println!("Yes!");
++        },
++    };
++
++    // Not linted
++    match option {
++        1..=10 => 1,
++        11..=20 => 2,
++        _ => 3,
++    };
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1ad78c740c68bf104e9b3fd8a6e9fc1b84cc6703
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,117 @@@
++error: this boolean expression can be simplified
++  --> $DIR/match_bool.rs:31:11
++   |
++LL |     match test && test {
++   |           ^^^^^^^^^^^^ help: try: `test`
++   |
++   = note: `-D clippy::nonminimal-bool` implied by `-D warnings`
++
++error: you seem to be trying to match on a boolean expression
++  --> $DIR/match_bool.rs:6:5
++   |
++LL | /     match test {
++LL | |         true => 0,
++LL | |         false => 42,
++LL | |     };
++   | |_____^ help: consider using an `if`/`else` expression: `if test { 0 } else { 42 }`
++   |
++note: the lint level is defined here
++  --> $DIR/match_bool.rs:1:9
++   |
++LL | #![deny(clippy::match_bool)]
++   |         ^^^^^^^^^^^^^^^^^^
++
++error: you seem to be trying to match on a boolean expression
++  --> $DIR/match_bool.rs:12:5
++   |
++LL | /     match option == 1 {
++LL | |         true => 1,
++LL | |         false => 0,
++LL | |     };
++   | |_____^ help: consider using an `if`/`else` expression: `if option == 1 { 1 } else { 0 }`
++
++error: you seem to be trying to match on a boolean expression
++  --> $DIR/match_bool.rs:17:5
++   |
++LL | /     match test {
++LL | |         true => (),
++LL | |         false => {
++LL | |             println!("Noooo!");
++LL | |         },
++LL | |     };
++   | |_____^
++   |
++help: consider using an `if`/`else` expression
++   |
++LL |     if !test {
++LL |         println!("Noooo!");
++LL |     };
++   |
++
++error: you seem to be trying to match on a boolean expression
++  --> $DIR/match_bool.rs:24:5
++   |
++LL | /     match test {
++LL | |         false => {
++LL | |             println!("Noooo!");
++LL | |         },
++LL | |         _ => (),
++LL | |     };
++   | |_____^
++   |
++help: consider using an `if`/`else` expression
++   |
++LL |     if !test {
++LL |         println!("Noooo!");
++LL |     };
++   |
++
++error: you seem to be trying to match on a boolean expression
++  --> $DIR/match_bool.rs:31:5
++   |
++LL | /     match test && test {
++LL | |         false => {
++LL | |             println!("Noooo!");
++LL | |         },
++LL | |         _ => (),
++LL | |     };
++   | |_____^
++   |
++help: consider using an `if`/`else` expression
++   |
++LL |     if !(test && test) {
++LL |         println!("Noooo!");
++LL |     };
++   |
++
++error: equal expressions as operands to `&&`
++  --> $DIR/match_bool.rs:31:11
++   |
++LL |     match test && test {
++   |           ^^^^^^^^^^^^
++   |
++   = note: `#[deny(clippy::eq_op)]` on by default
++
++error: you seem to be trying to match on a boolean expression
++  --> $DIR/match_bool.rs:38:5
++   |
++LL | /     match test {
++LL | |         false => {
++LL | |             println!("Noooo!");
++LL | |         },
++...  |
++LL | |         },
++LL | |     };
++   | |_____^
++   |
++help: consider using an `if`/`else` expression
++   |
++LL |     if test {
++LL |         println!("Yes!");
++LL |     } else {
++LL |         println!("Noooo!");
++LL |     };
++   |
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0bb39d77e461ea9edd610d0f82535537ba1a7654
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,130 @@@
++#![warn(clippy::match_on_vec_items)]
++
++fn match_with_wildcard() {
++    let arr = vec![0, 1, 2, 3];
++    let range = 1..3;
++    let idx = 1;
++
++    // Lint, may panic
++    match arr[idx] {
++        0 => println!("0"),
++        1 => println!("1"),
++        _ => {},
++    }
++
++    // Lint, may panic
++    match arr[range] {
++        [0, 1] => println!("0 1"),
++        [1, 2] => println!("1 2"),
++        _ => {},
++    }
++}
++
++fn match_without_wildcard() {
++    let arr = vec![0, 1, 2, 3];
++    let range = 1..3;
++    let idx = 2;
++
++    // Lint, may panic
++    match arr[idx] {
++        0 => println!("0"),
++        1 => println!("1"),
++        num => {},
++    }
++
++    // Lint, may panic
++    match arr[range] {
++        [0, 1] => println!("0 1"),
++        [1, 2] => println!("1 2"),
++        [ref sub @ ..] => {},
++    }
++}
++
++fn match_wildcard_and_action() {
++    let arr = vec![0, 1, 2, 3];
++    let range = 1..3;
++    let idx = 3;
++
++    // Lint, may panic
++    match arr[idx] {
++        0 => println!("0"),
++        1 => println!("1"),
++        _ => println!("Hello, World!"),
++    }
++
++    // Lint, may panic
++    match arr[range] {
++        [0, 1] => println!("0 1"),
++        [1, 2] => println!("1 2"),
++        _ => println!("Hello, World!"),
++    }
++}
++
++fn match_vec_ref() {
++    let arr = &vec![0, 1, 2, 3];
++    let range = 1..3;
++    let idx = 3;
++
++    // Lint, may panic
++    match arr[idx] {
++        0 => println!("0"),
++        1 => println!("1"),
++        _ => {},
++    }
++
++    // Lint, may panic
++    match arr[range] {
++        [0, 1] => println!("0 1"),
++        [1, 2] => println!("1 2"),
++        _ => {},
++    }
++}
++
++fn match_with_get() {
++    let arr = vec![0, 1, 2, 3];
++    let range = 1..3;
++    let idx = 3;
++
++    // Ok
++    match arr.get(idx) {
++        Some(0) => println!("0"),
++        Some(1) => println!("1"),
++        _ => {},
++    }
++
++    // Ok
++    match arr.get(range) {
++        Some(&[0, 1]) => println!("0 1"),
++        Some(&[1, 2]) => println!("1 2"),
++        _ => {},
++    }
++}
++
++fn match_with_array() {
++    let arr = [0, 1, 2, 3];
++    let range = 1..3;
++    let idx = 3;
++
++    // Ok
++    match arr[idx] {
++        0 => println!("0"),
++        1 => println!("1"),
++        _ => {},
++    }
++
++    // Ok
++    match arr[range] {
++        [0, 1] => println!("0 1"),
++        [1, 2] => println!("1 2"),
++        _ => {},
++    }
++}
++
++fn main() {
++    match_with_wildcard();
++    match_without_wildcard();
++    match_wildcard_and_action();
++    match_vec_ref();
++    match_with_get();
++    match_with_array();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..49446d715abe26682174f6f707475954cba14e1e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++error: indexing into a vector may panic
++  --> $DIR/match_on_vec_items.rs:9:11
++   |
++LL |     match arr[idx] {
++   |           ^^^^^^^^ help: try this: `arr.get(idx)`
++   |
++   = note: `-D clippy::match-on-vec-items` implied by `-D warnings`
++
++error: indexing into a vector may panic
++  --> $DIR/match_on_vec_items.rs:16:11
++   |
++LL |     match arr[range] {
++   |           ^^^^^^^^^^ help: try this: `arr.get(range)`
++
++error: indexing into a vector may panic
++  --> $DIR/match_on_vec_items.rs:29:11
++   |
++LL |     match arr[idx] {
++   |           ^^^^^^^^ help: try this: `arr.get(idx)`
++
++error: indexing into a vector may panic
++  --> $DIR/match_on_vec_items.rs:36:11
++   |
++LL |     match arr[range] {
++   |           ^^^^^^^^^^ help: try this: `arr.get(range)`
++
++error: indexing into a vector may panic
++  --> $DIR/match_on_vec_items.rs:49:11
++   |
++LL |     match arr[idx] {
++   |           ^^^^^^^^ help: try this: `arr.get(idx)`
++
++error: indexing into a vector may panic
++  --> $DIR/match_on_vec_items.rs:56:11
++   |
++LL |     match arr[range] {
++   |           ^^^^^^^^^^ help: try this: `arr.get(range)`
++
++error: indexing into a vector may panic
++  --> $DIR/match_on_vec_items.rs:69:11
++   |
++LL |     match arr[idx] {
++   |           ^^^^^^^^ help: try this: `arr.get(idx)`
++
++error: indexing into a vector may panic
++  --> $DIR/match_on_vec_items.rs:76:11
++   |
++LL |     match arr[range] {
++   |           ^^^^^^^^^^ help: try this: `arr.get(range)`
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..97789bb766f891027817d4c65204c23377adef18
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,82 @@@
++#![feature(exclusive_range_pattern)]
++#![feature(half_open_range_patterns)]
++#![warn(clippy::match_overlapping_arm)]
++#![allow(clippy::redundant_pattern_matching)]
++
++/// Tests for match_overlapping_arm
++
++fn overlapping() {
++    const FOO: u64 = 2;
++
++    match 42 {
++        0..=10 => println!("0 ... 10"),
++        0..=11 => println!("0 ... 11"),
++        _ => (),
++    }
++
++    match 42 {
++        0..=5 => println!("0 ... 5"),
++        6..=7 => println!("6 ... 7"),
++        FOO..=11 => println!("0 ... 11"),
++        _ => (),
++    }
++
++    match 42 {
++        2 => println!("2"),
++        0..=5 => println!("0 ... 5"),
++        _ => (),
++    }
++
++    match 42 {
++        2 => println!("2"),
++        0..=2 => println!("0 ... 2"),
++        _ => (),
++    }
++
++    match 42 {
++        0..=10 => println!("0 ... 10"),
++        11..=50 => println!("11 ... 50"),
++        _ => (),
++    }
++
++    match 42 {
++        2 => println!("2"),
++        0..2 => println!("0 .. 2"),
++        _ => (),
++    }
++
++    match 42 {
++        0..10 => println!("0 .. 10"),
++        10..50 => println!("10 .. 50"),
++        _ => (),
++    }
++
++    match 42 {
++        0..11 => println!("0 .. 11"),
++        0..=11 => println!("0 ... 11"),
++        _ => (),
++    }
++
++    /*
++    // FIXME(JohnTitor): uncomment this once rustfmt knows half-open patterns
++    match 42 {
++        0.. => println!("0 .. 42"),
++        3.. => println!("3 .. 42"),
++        _ => (),
++    }
++
++    match 42 {
++        ..=23 => println!("0 ... 23"),
++        ..26 => println!("0 .. 26"),
++        _ => (),
++    }
++    */
++
++    if let None = Some(42) {
++        // nothing
++    } else if let None = Some(42) {
++        // another nothing :-)
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eb20d5405a95e984b19ab2712f13362e28b800a9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++error: some ranges overlap
++  --> $DIR/match_overlapping_arm.rs:12:9
++   |
++LL |         0..=10 => println!("0 ... 10"),
++   |         ^^^^^^
++   |
++   = note: `-D clippy::match-overlapping-arm` implied by `-D warnings`
++note: overlaps with this
++  --> $DIR/match_overlapping_arm.rs:13:9
++   |
++LL |         0..=11 => println!("0 ... 11"),
++   |         ^^^^^^
++
++error: some ranges overlap
++  --> $DIR/match_overlapping_arm.rs:18:9
++   |
++LL |         0..=5 => println!("0 ... 5"),
++   |         ^^^^^
++   |
++note: overlaps with this
++  --> $DIR/match_overlapping_arm.rs:20:9
++   |
++LL |         FOO..=11 => println!("0 ... 11"),
++   |         ^^^^^^^^
++
++error: some ranges overlap
++  --> $DIR/match_overlapping_arm.rs:26:9
++   |
++LL |         0..=5 => println!("0 ... 5"),
++   |         ^^^^^
++   |
++note: overlaps with this
++  --> $DIR/match_overlapping_arm.rs:25:9
++   |
++LL |         2 => println!("2"),
++   |         ^
++
++error: some ranges overlap
++  --> $DIR/match_overlapping_arm.rs:32:9
++   |
++LL |         0..=2 => println!("0 ... 2"),
++   |         ^^^^^
++   |
++note: overlaps with this
++  --> $DIR/match_overlapping_arm.rs:31:9
++   |
++LL |         2 => println!("2"),
++   |         ^
++
++error: some ranges overlap
++  --> $DIR/match_overlapping_arm.rs:55:9
++   |
++LL |         0..11 => println!("0 .. 11"),
++   |         ^^^^^
++   |
++note: overlaps with this
++  --> $DIR/match_overlapping_arm.rs:56:9
++   |
++LL |         0..=11 => println!("0 ... 11"),
++   |         ^^^^^^
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5de43733ad33601a1108985bdb686b0d10d332e1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,74 @@@
++#![warn(clippy::match_ref_pats)]
++
++fn ref_pats() {
++    {
++        let v = &Some(0);
++        match v {
++            &Some(v) => println!("{:?}", v),
++            &None => println!("none"),
++        }
++        match v {
++            // This doesn't trigger; we have a different pattern.
++            &Some(v) => println!("some"),
++            other => println!("other"),
++        }
++    }
++    let tup = &(1, 2);
++    match tup {
++        &(v, 1) => println!("{}", v),
++        _ => println!("none"),
++    }
++    // Special case: using `&` both in expr and pats.
++    let w = Some(0);
++    match &w {
++        &Some(v) => println!("{:?}", v),
++        &None => println!("none"),
++    }
++    // False positive: only wildcard pattern.
++    let w = Some(0);
++    #[allow(clippy::match_single_binding)]
++    match w {
++        _ => println!("none"),
++    }
++
++    let a = &Some(0);
++    if let &None = a {
++        println!("none");
++    }
++
++    let b = Some(0);
++    if let &None = &b {
++        println!("none");
++    }
++}
++
++mod ice_3719 {
++    macro_rules! foo_variant(
++        ($idx:expr) => (Foo::get($idx).unwrap())
++    );
++
++    enum Foo {
++        A,
++        B,
++    }
++
++    impl Foo {
++        fn get(idx: u8) -> Option<&'static Self> {
++            match idx {
++                0 => Some(&Foo::A),
++                1 => Some(&Foo::B),
++                _ => None,
++            }
++        }
++    }
++
++    fn ice_3719() {
++        // ICE #3719
++        match foo_variant!(0) {
++            &Foo::A => println!("A"),
++            _ => println!("Wild"),
++        }
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..52cb4a14b72bcecce5de2cf219fc616ccfa2e7b6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,91 @@@
++error: you don't need to add `&` to all patterns
++  --> $DIR/match_ref_pats.rs:6:9
++   |
++LL | /         match v {
++LL | |             &Some(v) => println!("{:?}", v),
++LL | |             &None => println!("none"),
++LL | |         }
++   | |_________^
++   |
++   = note: `-D clippy::match-ref-pats` implied by `-D warnings`
++help: instead of prefixing all patterns with `&`, you can dereference the expression
++   |
++LL |         match *v {
++LL |             Some(v) => println!("{:?}", v),
++LL |             None => println!("none"),
++   |
++
++error: you don't need to add `&` to all patterns
++  --> $DIR/match_ref_pats.rs:17:5
++   |
++LL | /     match tup {
++LL | |         &(v, 1) => println!("{}", v),
++LL | |         _ => println!("none"),
++LL | |     }
++   | |_____^
++   |
++help: instead of prefixing all patterns with `&`, you can dereference the expression
++   |
++LL |     match *tup {
++LL |         (v, 1) => println!("{}", v),
++   |
++
++error: you don't need to add `&` to both the expression and the patterns
++  --> $DIR/match_ref_pats.rs:23:5
++   |
++LL | /     match &w {
++LL | |         &Some(v) => println!("{:?}", v),
++LL | |         &None => println!("none"),
++LL | |     }
++   | |_____^
++   |
++help: try
++   |
++LL |     match w {
++LL |         Some(v) => println!("{:?}", v),
++LL |         None => println!("none"),
++   |
++
++error: you don't need to add `&` to all patterns
++  --> $DIR/match_ref_pats.rs:35:5
++   |
++LL | /     if let &None = a {
++LL | |         println!("none");
++LL | |     }
++   | |_____^
++   |
++help: instead of prefixing all patterns with `&`, you can dereference the expression
++   |
++LL |     if let None = *a {
++   |            ^^^^   ^^
++
++error: you don't need to add `&` to both the expression and the patterns
++  --> $DIR/match_ref_pats.rs:40:5
++   |
++LL | /     if let &None = &b {
++LL | |         println!("none");
++LL | |     }
++   | |_____^
++   |
++help: try
++   |
++LL |     if let None = b {
++   |            ^^^^   ^
++
++error: you don't need to add `&` to all patterns
++  --> $DIR/match_ref_pats.rs:67:9
++   |
++LL | /         match foo_variant!(0) {
++LL | |             &Foo::A => println!("A"),
++LL | |             _ => println!("Wild"),
++LL | |         }
++   | |_________^
++   |
++help: instead of prefixing all patterns with `&`, you can dereference the expression
++   |
++LL |         match *foo_variant!(0) {
++LL |             Foo::A => println!("A"),
++   |
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0b9342c9c4234efd70ad5e0552d7ecea018e3884
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++#![warn(clippy::match_same_arms)]
++
++pub enum Abc {
++    A,
++    B,
++    C,
++}
++
++fn match_same_arms() {
++    let _ = match Abc::A {
++        Abc::A => 0,
++        Abc::B => 1,
++        _ => 0, //~ ERROR match arms have same body
++    };
++
++    match (1, 2, 3) {
++        (1, .., 3) => 42,
++        (.., 3) => 42, //~ ERROR match arms have same body
++        _ => 0,
++    };
++
++    let _ = match 42 {
++        42 => 1,
++        51 => 1, //~ ERROR match arms have same body
++        41 => 2,
++        52 => 2, //~ ERROR match arms have same body
++        _ => 0,
++    };
++
++    let _ = match 42 {
++        1 => 2,
++        2 => 2, //~ ERROR 2nd matched arms have same body
++        3 => 2, //~ ERROR 3rd matched arms have same body
++        4 => 3,
++        _ => 0,
++    };
++}
++
++mod issue4244 {
++    #[derive(PartialEq, PartialOrd, Eq, Ord)]
++    pub enum CommandInfo {
++        BuiltIn { name: String, about: Option<String> },
++        External { name: String, path: std::path::PathBuf },
++    }
++
++    impl CommandInfo {
++        pub fn name(&self) -> String {
++            match self {
++                CommandInfo::BuiltIn { name, .. } => name.to_string(),
++                CommandInfo::External { name, .. } => name.to_string(),
++            }
++        }
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0549886a1e8ec7ad8a52f497c1ff647033e63528
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,139 @@@
++error: this `match` has identical arm bodies
++  --> $DIR/match_same_arms.rs:13:14
++   |
++LL |         _ => 0, //~ ERROR match arms have same body
++   |              ^
++   |
++   = note: `-D clippy::match-same-arms` implied by `-D warnings`
++note: same as this
++  --> $DIR/match_same_arms.rs:11:19
++   |
++LL |         Abc::A => 0,
++   |                   ^
++note: `Abc::A` has the same arm body as the `_` wildcard, consider removing it
++  --> $DIR/match_same_arms.rs:11:19
++   |
++LL |         Abc::A => 0,
++   |                   ^
++
++error: this `match` has identical arm bodies
++  --> $DIR/match_same_arms.rs:18:20
++   |
++LL |         (.., 3) => 42, //~ ERROR match arms have same body
++   |                    ^^
++   |
++note: same as this
++  --> $DIR/match_same_arms.rs:17:23
++   |
++LL |         (1, .., 3) => 42,
++   |                       ^^
++help: consider refactoring into `(1, .., 3) | (.., 3)`
++  --> $DIR/match_same_arms.rs:17:9
++   |
++LL |         (1, .., 3) => 42,
++   |         ^^^^^^^^^^
++
++error: this `match` has identical arm bodies
++  --> $DIR/match_same_arms.rs:24:15
++   |
++LL |         51 => 1, //~ ERROR match arms have same body
++   |               ^
++   |
++note: same as this
++  --> $DIR/match_same_arms.rs:23:15
++   |
++LL |         42 => 1,
++   |               ^
++help: consider refactoring into `42 | 51`
++  --> $DIR/match_same_arms.rs:23:9
++   |
++LL |         42 => 1,
++   |         ^^
++
++error: this `match` has identical arm bodies
++  --> $DIR/match_same_arms.rs:26:15
++   |
++LL |         52 => 2, //~ ERROR match arms have same body
++   |               ^
++   |
++note: same as this
++  --> $DIR/match_same_arms.rs:25:15
++   |
++LL |         41 => 2,
++   |               ^
++help: consider refactoring into `41 | 52`
++  --> $DIR/match_same_arms.rs:25:9
++   |
++LL |         41 => 2,
++   |         ^^
++
++error: this `match` has identical arm bodies
++  --> $DIR/match_same_arms.rs:32:14
++   |
++LL |         2 => 2, //~ ERROR 2nd matched arms have same body
++   |              ^
++   |
++note: same as this
++  --> $DIR/match_same_arms.rs:31:14
++   |
++LL |         1 => 2,
++   |              ^
++help: consider refactoring into `1 | 2`
++  --> $DIR/match_same_arms.rs:31:9
++   |
++LL |         1 => 2,
++   |         ^
++
++error: this `match` has identical arm bodies
++  --> $DIR/match_same_arms.rs:33:14
++   |
++LL |         3 => 2, //~ ERROR 3rd matched arms have same body
++   |              ^
++   |
++note: same as this
++  --> $DIR/match_same_arms.rs:31:14
++   |
++LL |         1 => 2,
++   |              ^
++help: consider refactoring into `1 | 3`
++  --> $DIR/match_same_arms.rs:31:9
++   |
++LL |         1 => 2,
++   |         ^
++
++error: this `match` has identical arm bodies
++  --> $DIR/match_same_arms.rs:33:14
++   |
++LL |         3 => 2, //~ ERROR 3rd matched arms have same body
++   |              ^
++   |
++note: same as this
++  --> $DIR/match_same_arms.rs:32:14
++   |
++LL |         2 => 2, //~ ERROR 2nd matched arms have same body
++   |              ^
++help: consider refactoring into `2 | 3`
++  --> $DIR/match_same_arms.rs:32:9
++   |
++LL |         2 => 2, //~ ERROR 2nd matched arms have same body
++   |         ^
++
++error: this `match` has identical arm bodies
++  --> $DIR/match_same_arms.rs:50:55
++   |
++LL |                 CommandInfo::External { name, .. } => name.to_string(),
++   |                                                       ^^^^^^^^^^^^^^^^
++   |
++note: same as this
++  --> $DIR/match_same_arms.rs:49:54
++   |
++LL |                 CommandInfo::BuiltIn { name, .. } => name.to_string(),
++   |                                                      ^^^^^^^^^^^^^^^^
++help: consider refactoring into `CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. }`
++  --> $DIR/match_same_arms.rs:49:17
++   |
++LL |                 CommandInfo::BuiltIn { name, .. } => name.to_string(),
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e1401d2796a52870068e125744826766deb3010a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,124 @@@
++#![warn(clippy::match_same_arms)]
++#![allow(clippy::blacklisted_name)]
++
++fn bar<T>(_: T) {}
++fn foo() -> bool {
++    unimplemented!()
++}
++
++fn match_same_arms() {
++    let _ = match 42 {
++        42 => {
++            foo();
++            let mut a = 42 + [23].len() as i32;
++            if true {
++                a += 7;
++            }
++            a = -31 - a;
++            a
++        },
++        _ => {
++            //~ ERROR match arms have same body
++            foo();
++            let mut a = 42 + [23].len() as i32;
++            if true {
++                a += 7;
++            }
++            a = -31 - a;
++            a
++        },
++    };
++
++    let _ = match 42 {
++        42 => foo(),
++        51 => foo(), //~ ERROR match arms have same body
++        _ => true,
++    };
++
++    let _ = match Some(42) {
++        Some(_) => 24,
++        None => 24, //~ ERROR match arms have same body
++    };
++
++    let _ = match Some(42) {
++        Some(foo) => 24,
++        None => 24,
++    };
++
++    let _ = match Some(42) {
++        Some(42) => 24,
++        Some(a) => 24, // bindings are different
++        None => 0,
++    };
++
++    let _ = match Some(42) {
++        Some(a) if a > 0 => 24,
++        Some(a) => 24, // one arm has a guard
++        None => 0,
++    };
++
++    match (Some(42), Some(42)) {
++        (Some(a), None) => bar(a),
++        (None, Some(a)) => bar(a), //~ ERROR match arms have same body
++        _ => (),
++    }
++
++    match (Some(42), Some(42)) {
++        (Some(a), ..) => bar(a),
++        (.., Some(a)) => bar(a), //~ ERROR match arms have same body
++        _ => (),
++    }
++
++    let _ = match Some(()) {
++        Some(()) => 0.0,
++        None => -0.0,
++    };
++
++    match (Some(42), Some("")) {
++        (Some(a), None) => bar(a),
++        (None, Some(a)) => bar(a), // bindings have different types
++        _ => (),
++    }
++
++    let x: Result<i32, &str> = Ok(3);
++
++    // No warning because of the guard.
++    match x {
++        Ok(x) if x * x == 64 => println!("ok"),
++        Ok(_) => println!("ok"),
++        Err(_) => println!("err"),
++    }
++
++    // This used to be a false positive; see issue #1996.
++    match x {
++        Ok(3) => println!("ok"),
++        Ok(x) if x * x == 64 => println!("ok 64"),
++        Ok(_) => println!("ok"),
++        Err(_) => println!("err"),
++    }
++
++    match (x, Some(1i32)) {
++        (Ok(x), Some(_)) => println!("ok {}", x),
++        (Ok(_), Some(x)) => println!("ok {}", x),
++        _ => println!("err"),
++    }
++
++    // No warning; different types for `x`.
++    match (x, Some(1.0f64)) {
++        (Ok(x), Some(_)) => println!("ok {}", x),
++        (Ok(_), Some(x)) => println!("ok {}", x),
++        _ => println!("err"),
++    }
++
++    // False negative #2251.
++    match x {
++        Ok(_tmp) => println!("ok"),
++        Ok(3) => println!("ok"),
++        Ok(_) => println!("ok"),
++        Err(_) => {
++            unreachable!();
++        },
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..26c65f32ad780a454d133432a2d2f02c00dc6465
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,145 @@@
++error: this `match` has identical arm bodies
++  --> $DIR/match_same_arms2.rs:20:14
++   |
++LL |           _ => {
++   |  ______________^
++LL | |             //~ ERROR match arms have same body
++LL | |             foo();
++LL | |             let mut a = 42 + [23].len() as i32;
++...  |
++LL | |             a
++LL | |         },
++   | |_________^
++   |
++   = note: `-D clippy::match-same-arms` implied by `-D warnings`
++note: same as this
++  --> $DIR/match_same_arms2.rs:11:15
++   |
++LL |           42 => {
++   |  _______________^
++LL | |             foo();
++LL | |             let mut a = 42 + [23].len() as i32;
++LL | |             if true {
++...  |
++LL | |             a
++LL | |         },
++   | |_________^
++note: `42` has the same arm body as the `_` wildcard, consider removing it
++  --> $DIR/match_same_arms2.rs:11:15
++   |
++LL |           42 => {
++   |  _______________^
++LL | |             foo();
++LL | |             let mut a = 42 + [23].len() as i32;
++LL | |             if true {
++...  |
++LL | |             a
++LL | |         },
++   | |_________^
++
++error: this `match` has identical arm bodies
++  --> $DIR/match_same_arms2.rs:34:15
++   |
++LL |         51 => foo(), //~ ERROR match arms have same body
++   |               ^^^^^
++   |
++note: same as this
++  --> $DIR/match_same_arms2.rs:33:15
++   |
++LL |         42 => foo(),
++   |               ^^^^^
++help: consider refactoring into `42 | 51`
++  --> $DIR/match_same_arms2.rs:33:9
++   |
++LL |         42 => foo(),
++   |         ^^
++
++error: this `match` has identical arm bodies
++  --> $DIR/match_same_arms2.rs:40:17
++   |
++LL |         None => 24, //~ ERROR match arms have same body
++   |                 ^^
++   |
++note: same as this
++  --> $DIR/match_same_arms2.rs:39:20
++   |
++LL |         Some(_) => 24,
++   |                    ^^
++help: consider refactoring into `Some(_) | None`
++  --> $DIR/match_same_arms2.rs:39:9
++   |
++LL |         Some(_) => 24,
++   |         ^^^^^^^
++
++error: this `match` has identical arm bodies
++  --> $DIR/match_same_arms2.rs:62:28
++   |
++LL |         (None, Some(a)) => bar(a), //~ ERROR match arms have same body
++   |                            ^^^^^^
++   |
++note: same as this
++  --> $DIR/match_same_arms2.rs:61:28
++   |
++LL |         (Some(a), None) => bar(a),
++   |                            ^^^^^^
++help: consider refactoring into `(Some(a), None) | (None, Some(a))`
++  --> $DIR/match_same_arms2.rs:61:9
++   |
++LL |         (Some(a), None) => bar(a),
++   |         ^^^^^^^^^^^^^^^
++
++error: this `match` has identical arm bodies
++  --> $DIR/match_same_arms2.rs:68:26
++   |
++LL |         (.., Some(a)) => bar(a), //~ ERROR match arms have same body
++   |                          ^^^^^^
++   |
++note: same as this
++  --> $DIR/match_same_arms2.rs:67:26
++   |
++LL |         (Some(a), ..) => bar(a),
++   |                          ^^^^^^
++help: consider refactoring into `(Some(a), ..) | (.., Some(a))`
++  --> $DIR/match_same_arms2.rs:67:9
++   |
++LL |         (Some(a), ..) => bar(a),
++   |         ^^^^^^^^^^^^^
++
++error: this `match` has identical arm bodies
++  --> $DIR/match_same_arms2.rs:102:29
++   |
++LL |         (Ok(_), Some(x)) => println!("ok {}", x),
++   |                             ^^^^^^^^^^^^^^^^^^^^
++   |
++note: same as this
++  --> $DIR/match_same_arms2.rs:101:29
++   |
++LL |         (Ok(x), Some(_)) => println!("ok {}", x),
++   |                             ^^^^^^^^^^^^^^^^^^^^
++help: consider refactoring into `(Ok(x), Some(_)) | (Ok(_), Some(x))`
++  --> $DIR/match_same_arms2.rs:101:9
++   |
++LL |         (Ok(x), Some(_)) => println!("ok {}", x),
++   |         ^^^^^^^^^^^^^^^^
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: this `match` has identical arm bodies
++  --> $DIR/match_same_arms2.rs:117:18
++   |
++LL |         Ok(_) => println!("ok"),
++   |                  ^^^^^^^^^^^^^^
++   |
++note: same as this
++  --> $DIR/match_same_arms2.rs:116:18
++   |
++LL |         Ok(3) => println!("ok"),
++   |                  ^^^^^^^^^^^^^^
++help: consider refactoring into `Ok(3) | Ok(_)`
++  --> $DIR/match_same_arms2.rs:116:9
++   |
++LL |         Ok(3) => println!("ok"),
++   |         ^^^^^
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f3627902eec985b15e2e192b0371285fa0848b57
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,90 @@@
++// run-rustfix
++
++#![warn(clippy::match_single_binding)]
++#![allow(unused_variables, clippy::many_single_char_names, clippy::toplevel_ref_arg)]
++
++struct Point {
++    x: i32,
++    y: i32,
++}
++
++fn coords() -> Point {
++    Point { x: 1, y: 2 }
++}
++
++macro_rules! foo {
++    ($param:expr) => {
++        match $param {
++            _ => println!("whatever"),
++        }
++    };
++}
++
++fn main() {
++    let a = 1;
++    let b = 2;
++    let c = 3;
++    // Lint
++    let (x, y, z) = (a, b, c);
++    {
++        println!("{} {} {}", x, y, z);
++    }
++    // Lint
++    let (x, y, z) = (a, b, c);
++    println!("{} {} {}", x, y, z);
++    // Ok
++    foo!(a);
++    // Ok
++    match a {
++        2 => println!("2"),
++        _ => println!("Not 2"),
++    }
++    // Ok
++    let d = Some(5);
++    match d {
++        Some(d) => println!("{}", d),
++        _ => println!("None"),
++    }
++    // Lint
++    println!("whatever");
++    // Lint
++    {
++        let x = 29;
++        println!("x has a value of {}", x);
++    }
++    // Lint
++    {
++        let e = 5 * a;
++        if e >= 5 {
++            println!("e is superior to 5");
++        }
++    }
++    // Lint
++    let p = Point { x: 0, y: 7 };
++    let Point { x, y } = p;
++    println!("Coords: ({}, {})", x, y);
++    // Lint
++    let Point { x: x1, y: y1 } = p;
++    println!("Coords: ({}, {})", x1, y1);
++    // Lint
++    let x = 5;
++    let ref r = x;
++    println!("Got a reference to {}", r);
++    // Lint
++    let mut x = 5;
++    let ref mut mr = x;
++    println!("Got a mutable reference to {}", mr);
++    // Lint
++    let Point { x, y } = coords();
++    let product = x * y;
++    // Lint
++    let v = vec![Some(1), Some(2), Some(3), Some(4)];
++    #[allow(clippy::let_and_return)]
++    let _ = v
++        .iter()
++        .map(|i| {
++            let unwrapped = i.unwrap();
++            unwrapped
++        })
++        .collect::<Vec<u8>>();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8c182148ae184652c18b413e3f239dde954b6da1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,102 @@@
++// run-rustfix
++
++#![warn(clippy::match_single_binding)]
++#![allow(unused_variables, clippy::many_single_char_names, clippy::toplevel_ref_arg)]
++
++struct Point {
++    x: i32,
++    y: i32,
++}
++
++fn coords() -> Point {
++    Point { x: 1, y: 2 }
++}
++
++macro_rules! foo {
++    ($param:expr) => {
++        match $param {
++            _ => println!("whatever"),
++        }
++    };
++}
++
++fn main() {
++    let a = 1;
++    let b = 2;
++    let c = 3;
++    // Lint
++    match (a, b, c) {
++        (x, y, z) => {
++            println!("{} {} {}", x, y, z);
++        },
++    }
++    // Lint
++    match (a, b, c) {
++        (x, y, z) => println!("{} {} {}", x, y, z),
++    }
++    // Ok
++    foo!(a);
++    // Ok
++    match a {
++        2 => println!("2"),
++        _ => println!("Not 2"),
++    }
++    // Ok
++    let d = Some(5);
++    match d {
++        Some(d) => println!("{}", d),
++        _ => println!("None"),
++    }
++    // Lint
++    match a {
++        _ => println!("whatever"),
++    }
++    // Lint
++    match a {
++        _ => {
++            let x = 29;
++            println!("x has a value of {}", x);
++        },
++    }
++    // Lint
++    match a {
++        _ => {
++            let e = 5 * a;
++            if e >= 5 {
++                println!("e is superior to 5");
++            }
++        },
++    }
++    // Lint
++    let p = Point { x: 0, y: 7 };
++    match p {
++        Point { x, y } => println!("Coords: ({}, {})", x, y),
++    }
++    // Lint
++    match p {
++        Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1),
++    }
++    // Lint
++    let x = 5;
++    match x {
++        ref r => println!("Got a reference to {}", r),
++    }
++    // Lint
++    let mut x = 5;
++    match x {
++        ref mut mr => println!("Got a mutable reference to {}", mr),
++    }
++    // Lint
++    let product = match coords() {
++        Point { x, y } => x * y,
++    };
++    // Lint
++    let v = vec![Some(1), Some(2), Some(3), Some(4)];
++    #[allow(clippy::let_and_return)]
++    let _ = v
++        .iter()
++        .map(|i| match i.unwrap() {
++            unwrapped => unwrapped,
++        })
++        .collect::<Vec<u8>>();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..795c8c3e24d7e44816419aabc3c3f2256feb2b4f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,171 @@@
++error: this match could be written as a `let` statement
++  --> $DIR/match_single_binding.rs:28:5
++   |
++LL | /     match (a, b, c) {
++LL | |         (x, y, z) => {
++LL | |             println!("{} {} {}", x, y, z);
++LL | |         },
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::match-single-binding` implied by `-D warnings`
++help: consider using `let` statement
++   |
++LL |     let (x, y, z) = (a, b, c);
++LL |     {
++LL |         println!("{} {} {}", x, y, z);
++LL |     }
++   |
++
++error: this match could be written as a `let` statement
++  --> $DIR/match_single_binding.rs:34:5
++   |
++LL | /     match (a, b, c) {
++LL | |         (x, y, z) => println!("{} {} {}", x, y, z),
++LL | |     }
++   | |_____^
++   |
++help: consider using `let` statement
++   |
++LL |     let (x, y, z) = (a, b, c);
++LL |     println!("{} {} {}", x, y, z);
++   |
++
++error: this match could be replaced by its body itself
++  --> $DIR/match_single_binding.rs:51:5
++   |
++LL | /     match a {
++LL | |         _ => println!("whatever"),
++LL | |     }
++   | |_____^ help: consider using the match body instead: `println!("whatever");`
++
++error: this match could be replaced by its body itself
++  --> $DIR/match_single_binding.rs:55:5
++   |
++LL | /     match a {
++LL | |         _ => {
++LL | |             let x = 29;
++LL | |             println!("x has a value of {}", x);
++LL | |         },
++LL | |     }
++   | |_____^
++   |
++help: consider using the match body instead
++   |
++LL |     {
++LL |         let x = 29;
++LL |         println!("x has a value of {}", x);
++LL |     }
++   |
++
++error: this match could be replaced by its body itself
++  --> $DIR/match_single_binding.rs:62:5
++   |
++LL | /     match a {
++LL | |         _ => {
++LL | |             let e = 5 * a;
++LL | |             if e >= 5 {
++...  |
++LL | |         },
++LL | |     }
++   | |_____^
++   |
++help: consider using the match body instead
++   |
++LL |     {
++LL |         let e = 5 * a;
++LL |         if e >= 5 {
++LL |             println!("e is superior to 5");
++LL |         }
++LL |     }
++   |
++
++error: this match could be written as a `let` statement
++  --> $DIR/match_single_binding.rs:72:5
++   |
++LL | /     match p {
++LL | |         Point { x, y } => println!("Coords: ({}, {})", x, y),
++LL | |     }
++   | |_____^
++   |
++help: consider using `let` statement
++   |
++LL |     let Point { x, y } = p;
++LL |     println!("Coords: ({}, {})", x, y);
++   |
++
++error: this match could be written as a `let` statement
++  --> $DIR/match_single_binding.rs:76:5
++   |
++LL | /     match p {
++LL | |         Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1),
++LL | |     }
++   | |_____^
++   |
++help: consider using `let` statement
++   |
++LL |     let Point { x: x1, y: y1 } = p;
++LL |     println!("Coords: ({}, {})", x1, y1);
++   |
++
++error: this match could be written as a `let` statement
++  --> $DIR/match_single_binding.rs:81:5
++   |
++LL | /     match x {
++LL | |         ref r => println!("Got a reference to {}", r),
++LL | |     }
++   | |_____^
++   |
++help: consider using `let` statement
++   |
++LL |     let ref r = x;
++LL |     println!("Got a reference to {}", r);
++   |
++
++error: this match could be written as a `let` statement
++  --> $DIR/match_single_binding.rs:86:5
++   |
++LL | /     match x {
++LL | |         ref mut mr => println!("Got a mutable reference to {}", mr),
++LL | |     }
++   | |_____^
++   |
++help: consider using `let` statement
++   |
++LL |     let ref mut mr = x;
++LL |     println!("Got a mutable reference to {}", mr);
++   |
++
++error: this match could be written as a `let` statement
++  --> $DIR/match_single_binding.rs:90:5
++   |
++LL | /     let product = match coords() {
++LL | |         Point { x, y } => x * y,
++LL | |     };
++   | |______^
++   |
++help: consider using `let` statement
++   |
++LL |     let Point { x, y } = coords();
++LL |     let product = x * y;
++   |
++
++error: this match could be written as a `let` statement
++  --> $DIR/match_single_binding.rs:98:18
++   |
++LL |           .map(|i| match i.unwrap() {
++   |  __________________^
++LL | |             unwrapped => unwrapped,
++LL | |         })
++   | |_________^
++   |
++help: consider using `let` statement
++   |
++LL |         .map(|i| {
++LL |             let unwrapped = i.unwrap();
++LL |             unwrapped
++LL |         })
++   |
++
++error: aborting due to 11 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..823be65efe065703b8481ced833636d15b245373
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,65 @@@
++#![feature(exclusive_range_pattern)]
++#![allow(clippy::match_same_arms)]
++#![warn(clippy::match_wild_err_arm)]
++
++fn match_wild_err_arm() {
++    let x: Result<i32, &str> = Ok(3);
++
++    match x {
++        Ok(3) => println!("ok"),
++        Ok(_) => println!("ok"),
++        Err(_) => panic!("err"),
++    }
++
++    match x {
++        Ok(3) => println!("ok"),
++        Ok(_) => println!("ok"),
++        Err(_) => panic!(),
++    }
++
++    match x {
++        Ok(3) => println!("ok"),
++        Ok(_) => println!("ok"),
++        Err(_) => {
++            panic!();
++        },
++    }
++
++    match x {
++        Ok(3) => println!("ok"),
++        Ok(_) => println!("ok"),
++        Err(_e) => panic!(),
++    }
++
++    // Allowed when used in `panic!`.
++    match x {
++        Ok(3) => println!("ok"),
++        Ok(_) => println!("ok"),
++        Err(_e) => panic!("{}", _e),
++    }
++
++    // Allowed when not with `panic!` block.
++    match x {
++        Ok(3) => println!("ok"),
++        Ok(_) => println!("ok"),
++        Err(_) => println!("err"),
++    }
++
++    // Allowed when used with `unreachable!`.
++    match x {
++        Ok(3) => println!("ok"),
++        Ok(_) => println!("ok"),
++        Err(_) => unreachable!(),
++    }
++
++    // Allowed when used with `unreachable!`.
++    match x {
++        Ok(3) => println!("ok"),
++        Ok(_) => println!("ok"),
++        Err(_) => {
++            unreachable!();
++        },
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..20d4c933418b7a928cd2b292e799a4f6161b547e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++error: `Err(_)` matches all errors
++  --> $DIR/match_wild_err_arm.rs:11:9
++   |
++LL |         Err(_) => panic!("err"),
++   |         ^^^^^^
++   |
++   = note: `-D clippy::match-wild-err-arm` implied by `-D warnings`
++   = note: match each error separately or use the error output
++
++error: `Err(_)` matches all errors
++  --> $DIR/match_wild_err_arm.rs:17:9
++   |
++LL |         Err(_) => panic!(),
++   |         ^^^^^^
++   |
++   = note: match each error separately or use the error output
++
++error: `Err(_)` matches all errors
++  --> $DIR/match_wild_err_arm.rs:23:9
++   |
++LL |         Err(_) => {
++   |         ^^^^^^
++   |
++   = note: match each error separately or use the error output
++
++error: `Err(_e)` matches all errors
++  --> $DIR/match_wild_err_arm.rs:31:9
++   |
++LL |         Err(_e) => panic!(),
++   |         ^^^^^^^
++   |
++   = note: match each error separately or use the error output
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..69a8f286d050d398dc19f78e0252a8d31f67d62f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++// run-rustfix
++
++#![deny(clippy::mem_discriminant_non_enum)]
++
++use std::mem;
++
++enum Foo {
++    One(usize),
++    Two(u8),
++}
++
++fn main() {
++    // bad
++    mem::discriminant(&Some(2));
++    mem::discriminant(&None::<u8>);
++    mem::discriminant(&Foo::One(5));
++    mem::discriminant(&Foo::Two(5));
++
++    let ro = &Some(3);
++    let rro = &ro;
++    mem::discriminant(ro);
++    mem::discriminant(*rro);
++    mem::discriminant(*rro);
++
++    macro_rules! mem_discriminant_but_in_a_macro {
++        ($param:expr) => {
++            mem::discriminant($param)
++        };
++    }
++
++    mem_discriminant_but_in_a_macro!(*rro);
++
++    let rrrrro = &&&rro;
++    mem::discriminant(****rrrrro);
++    mem::discriminant(****rrrrro);
++
++    // ok
++    mem::discriminant(&Some(2));
++    mem::discriminant(&None::<u8>);
++    mem::discriminant(&Foo::One(5));
++    mem::discriminant(&Foo::Two(5));
++    mem::discriminant(ro);
++    mem::discriminant(*rro);
++    mem::discriminant(****rrrrro);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..55db50fcdc733d4a0180df3ff1eedf535045711d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++// run-rustfix
++
++#![deny(clippy::mem_discriminant_non_enum)]
++
++use std::mem;
++
++enum Foo {
++    One(usize),
++    Two(u8),
++}
++
++fn main() {
++    // bad
++    mem::discriminant(&&Some(2));
++    mem::discriminant(&&None::<u8>);
++    mem::discriminant(&&Foo::One(5));
++    mem::discriminant(&&Foo::Two(5));
++
++    let ro = &Some(3);
++    let rro = &ro;
++    mem::discriminant(&ro);
++    mem::discriminant(rro);
++    mem::discriminant(&rro);
++
++    macro_rules! mem_discriminant_but_in_a_macro {
++        ($param:expr) => {
++            mem::discriminant($param)
++        };
++    }
++
++    mem_discriminant_but_in_a_macro!(&rro);
++
++    let rrrrro = &&&rro;
++    mem::discriminant(&rrrrro);
++    mem::discriminant(*rrrrro);
++
++    // ok
++    mem::discriminant(&Some(2));
++    mem::discriminant(&None::<u8>);
++    mem::discriminant(&Foo::One(5));
++    mem::discriminant(&Foo::Two(5));
++    mem::discriminant(ro);
++    mem::discriminant(*rro);
++    mem::discriminant(****rrrrro);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8d9810970adee1bbe7d8301b15eec78c6d36f6ee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,94 @@@
++error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>`
++  --> $DIR/mem_discriminant.rs:14:5
++   |
++LL |     mem::discriminant(&&Some(2));
++   |     ^^^^^^^^^^^^^^^^^^---------^
++   |                       |
++   |                       help: try dereferencing: `&Some(2)`
++   |
++note: the lint level is defined here
++  --> $DIR/mem_discriminant.rs:3:9
++   |
++LL | #![deny(clippy::mem_discriminant_non_enum)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: calling `mem::discriminant` on non-enum type `&std::option::Option<u8>`
++  --> $DIR/mem_discriminant.rs:15:5
++   |
++LL |     mem::discriminant(&&None::<u8>);
++   |     ^^^^^^^^^^^^^^^^^^------------^
++   |                       |
++   |                       help: try dereferencing: `&None::<u8>`
++
++error: calling `mem::discriminant` on non-enum type `&Foo`
++  --> $DIR/mem_discriminant.rs:16:5
++   |
++LL |     mem::discriminant(&&Foo::One(5));
++   |     ^^^^^^^^^^^^^^^^^^-------------^
++   |                       |
++   |                       help: try dereferencing: `&Foo::One(5)`
++
++error: calling `mem::discriminant` on non-enum type `&Foo`
++  --> $DIR/mem_discriminant.rs:17:5
++   |
++LL |     mem::discriminant(&&Foo::Two(5));
++   |     ^^^^^^^^^^^^^^^^^^-------------^
++   |                       |
++   |                       help: try dereferencing: `&Foo::Two(5)`
++
++error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>`
++  --> $DIR/mem_discriminant.rs:21:5
++   |
++LL |     mem::discriminant(&ro);
++   |     ^^^^^^^^^^^^^^^^^^---^
++   |                       |
++   |                       help: try dereferencing: `ro`
++
++error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>`
++  --> $DIR/mem_discriminant.rs:22:5
++   |
++LL |     mem::discriminant(rro);
++   |     ^^^^^^^^^^^^^^^^^^---^
++   |                       |
++   |                       help: try dereferencing: `*rro`
++
++error: calling `mem::discriminant` on non-enum type `&&std::option::Option<i32>`
++  --> $DIR/mem_discriminant.rs:23:5
++   |
++LL |     mem::discriminant(&rro);
++   |     ^^^^^^^^^^^^^^^^^^----^
++   |                       |
++   |                       help: try dereferencing: `*rro`
++
++error: calling `mem::discriminant` on non-enum type `&&std::option::Option<i32>`
++  --> $DIR/mem_discriminant.rs:27:13
++   |
++LL |             mem::discriminant($param)
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
++...
++LL |     mem_discriminant_but_in_a_macro!(&rro);
++   |     ---------------------------------------
++   |     |                                |
++   |     |                                help: try dereferencing: `*rro`
++   |     in this macro invocation
++   |
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: calling `mem::discriminant` on non-enum type `&&&&&std::option::Option<i32>`
++  --> $DIR/mem_discriminant.rs:34:5
++   |
++LL |     mem::discriminant(&rrrrro);
++   |     ^^^^^^^^^^^^^^^^^^-------^
++   |                       |
++   |                       help: try dereferencing: `****rrrrro`
++
++error: calling `mem::discriminant` on non-enum type `&&&std::option::Option<i32>`
++  --> $DIR/mem_discriminant.rs:35:5
++   |
++LL |     mem::discriminant(*rrrrro);
++   |     ^^^^^^^^^^^^^^^^^^-------^
++   |                       |
++   |                       help: try dereferencing: `****rrrrro`
++
++error: aborting due to 10 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e245d3257d55d02a5639ec09761a8a8af87f58f2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++#![deny(clippy::mem_discriminant_non_enum)]
++
++use std::mem;
++
++enum Foo {
++    One(usize),
++    Two(u8),
++}
++
++struct A(Foo);
++
++fn main() {
++    // bad
++    mem::discriminant(&"hello");
++    mem::discriminant(&A(Foo::One(0)));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e2de3776f2c911ac0f4684790f5a2e9162b9fbc5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++error: calling `mem::discriminant` on non-enum type `&str`
++  --> $DIR/mem_discriminant_unfixable.rs:14:5
++   |
++LL |     mem::discriminant(&"hello");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++note: the lint level is defined here
++  --> $DIR/mem_discriminant_unfixable.rs:1:9
++   |
++LL | #![deny(clippy::mem_discriminant_non_enum)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: calling `mem::discriminant` on non-enum type `A`
++  --> $DIR/mem_discriminant_unfixable.rs:15:5
++   |
++LL |     mem::discriminant(&A(Foo::One(0)));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e5b35c098a23969a2e47ee732d03aaae7ea23c35
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++use std::rc::Rc;
++use std::sync::Arc;
++
++use std::mem as memstuff;
++use std::mem::forget as forgetSomething;
++
++#[warn(clippy::mem_forget)]
++#[allow(clippy::forget_copy)]
++fn main() {
++    let five: i32 = 5;
++    forgetSomething(five);
++
++    let six: Arc<i32> = Arc::new(6);
++    memstuff::forget(six);
++
++    let seven: Rc<i32> = Rc::new(7);
++    std::mem::forget(seven);
++
++    let eight: Vec<i32> = vec![8];
++    forgetSomething(eight);
++
++    std::mem::forget(7);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a90d8b1655dc4d0f0c4216bad9215dcf6d595877
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: usage of `mem::forget` on `Drop` type
++  --> $DIR/mem_forget.rs:14:5
++   |
++LL |     memstuff::forget(six);
++   |     ^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::mem-forget` implied by `-D warnings`
++
++error: usage of `mem::forget` on `Drop` type
++  --> $DIR/mem_forget.rs:17:5
++   |
++LL |     std::mem::forget(seven);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^
++
++error: usage of `mem::forget` on `Drop` type
++  --> $DIR/mem_forget.rs:20:5
++   |
++LL |     forgetSomething(eight);
++   |     ^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..54e962e7116e82a36c531f74a231b97b03b97d74
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++// run-rustfix
++#![allow(unused_imports)]
++#![warn(
++    clippy::all,
++    clippy::style,
++    clippy::mem_replace_option_with_none,
++    clippy::mem_replace_with_default
++)]
++
++use std::mem;
++
++fn replace_option_with_none() {
++    let mut an_option = Some(1);
++    let _ = an_option.take();
++    let an_option = &mut Some(1);
++    let _ = an_option.take();
++}
++
++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);
++}
++
++fn main() {
++    replace_option_with_none();
++    replace_with_default();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..60f527810716fb909e05271ef50ab43f9d93be60
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++// run-rustfix
++#![allow(unused_imports)]
++#![warn(
++    clippy::all,
++    clippy::style,
++    clippy::mem_replace_option_with_none,
++    clippy::mem_replace_with_default
++)]
++
++use std::mem;
++
++fn replace_option_with_none() {
++    let mut an_option = Some(1);
++    let _ = mem::replace(&mut an_option, None);
++    let an_option = &mut Some(1);
++    let _ = mem::replace(an_option, 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());
++}
++
++fn main() {
++    replace_option_with_none();
++    replace_with_default();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..245d33aa4f260141650f9f00401e6bbf49d5a974
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++error: replacing an `Option` with `None`
++  --> $DIR/mem_replace.rs:14:13
++   |
++LL |     let _ = mem::replace(&mut an_option, None);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
++   |
++   = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings`
++
++error: replacing an `Option` with `None`
++  --> $DIR/mem_replace.rs:16: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
++   |
++LL |     let _ = std::mem::replace(&mut s, String::default());
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
++   |
++   = 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
++   |
++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
++   |
++LL |     let _ = std::mem::replace(s, Default::default());
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0c09344b80d10b7f85d0e5495bc26abcc853a559
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++// aux-build:macro_rules.rs
++#![warn(clippy::mem_replace_with_default)]
++
++#[macro_use]
++extern crate macro_rules;
++
++macro_rules! take {
++    ($s:expr) => {
++        std::mem::replace($s, Default::default())
++    };
++}
++
++fn replace_with_default() {
++    let s = &mut String::from("foo");
++    take!(s);
++    take_external!(s);
++}
++
++fn main() {
++    replace_with_default();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4971a91050bf20f968bd2bf62547a8558babd156
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
++  --> $DIR/mem_replace_macro.rs:9:9
++   |
++LL |         std::mem::replace($s, Default::default())
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++...
++LL |     take!(s);
++   |     --------- in this macro invocation
++   |
++   = note: `-D clippy::mem-replace-with-default` implied by `-D warnings`
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7880cf36415ff87acc8ba620ceb7712ec8038e35
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,247 @@@
++// aux-build:option_helpers.rs
++// edition:2018
++
++#![warn(clippy::all, clippy::pedantic)]
++#![allow(
++    clippy::blacklisted_name,
++    clippy::default_trait_access,
++    clippy::missing_docs_in_private_items,
++    clippy::missing_safety_doc,
++    clippy::non_ascii_literal,
++    clippy::new_without_default,
++    clippy::needless_pass_by_value,
++    clippy::print_stdout,
++    clippy::must_use_candidate,
++    clippy::use_self,
++    clippy::useless_format,
++    clippy::wrong_self_convention,
++    clippy::unused_self,
++    unused
++)]
++
++#[macro_use]
++extern crate option_helpers;
++
++use std::collections::BTreeMap;
++use std::collections::HashMap;
++use std::collections::HashSet;
++use std::collections::VecDeque;
++use std::iter::FromIterator;
++use std::ops::Mul;
++use std::rc::{self, Rc};
++use std::sync::{self, Arc};
++
++use option_helpers::IteratorFalsePositives;
++
++pub struct T;
++
++impl T {
++    pub fn add(self, other: T) -> T {
++        self
++    }
++
++    // no error, not public interface
++    pub(crate) fn drop(&mut self) {}
++
++    // no error, private function
++    fn neg(self) -> Self {
++        self
++    }
++
++    // no error, private function
++    fn eq(&self, other: T) -> bool {
++        true
++    }
++
++    // No error; self is a ref.
++    fn sub(&self, other: T) -> &T {
++        self
++    }
++
++    // No error; different number of arguments.
++    fn div(self) -> T {
++        self
++    }
++
++    // No error; wrong return type.
++    fn rem(self, other: T) {}
++
++    // Fine
++    fn into_u32(self) -> u32 {
++        0
++    }
++
++    fn into_u16(&self) -> u16 {
++        0
++    }
++
++    fn to_something(self) -> u32 {
++        0
++    }
++
++    fn new(self) -> Self {
++        unimplemented!();
++    }
++}
++
++pub struct T1;
++
++impl T1 {
++    // Shouldn't trigger lint as it is unsafe.
++    pub unsafe fn add(self, rhs: T1) -> T1 {
++        self
++    }
++
++    // Should not trigger lint since this is an async function.
++    pub async fn next(&mut self) -> Option<T1> {
++        None
++    }
++}
++
++struct Lt<'a> {
++    foo: &'a u32,
++}
++
++impl<'a> Lt<'a> {
++    // The lifetime is different, but that’s irrelevant; see issue #734.
++    #[allow(clippy::needless_lifetimes)]
++    pub fn new<'b>(s: &'b str) -> Lt<'b> {
++        unimplemented!()
++    }
++}
++
++struct Lt2<'a> {
++    foo: &'a u32,
++}
++
++impl<'a> Lt2<'a> {
++    // The lifetime is different, but that’s irrelevant; see issue #734.
++    pub fn new(s: &str) -> Lt2 {
++        unimplemented!()
++    }
++}
++
++struct Lt3<'a> {
++    foo: &'a u32,
++}
++
++impl<'a> Lt3<'a> {
++    // The lifetime is different, but that’s irrelevant; see issue #734.
++    pub fn new() -> Lt3<'static> {
++        unimplemented!()
++    }
++}
++
++#[derive(Clone, Copy)]
++struct U;
++
++impl U {
++    fn new() -> Self {
++        U
++    }
++    // Ok because `U` is `Copy`.
++    fn to_something(self) -> u32 {
++        0
++    }
++}
++
++struct V<T> {
++    _dummy: T,
++}
++
++impl<T> V<T> {
++    fn new() -> Option<V<T>> {
++        None
++    }
++}
++
++struct AsyncNew;
++
++impl AsyncNew {
++    async fn new() -> Option<Self> {
++        None
++    }
++}
++
++struct BadNew;
++
++impl BadNew {
++    fn new() -> i32 {
++        0
++    }
++}
++
++impl Mul<T> for T {
++    type Output = T;
++    // No error, obviously.
++    fn mul(self, other: T) -> T {
++        self
++    }
++}
++
++/// Checks implementation of `FILTER_NEXT` lint.
++#[rustfmt::skip]
++fn filter_next() {
++    let v = vec![3, 2, 1, 0, -1, -2, -3];
++
++    // Single-line case.
++    let _ = v.iter().filter(|&x| *x < 0).next();
++
++    // Multi-line case.
++    let _ = v.iter().filter(|&x| {
++                                *x < 0
++                            }
++                   ).next();
++
++    // Check that hat we don't lint if the caller is not an `Iterator`.
++    let foo = IteratorFalsePositives { foo: 0 };
++    let _ = foo.filter().next();
++}
++
++/// Checks implementation of `SEARCH_IS_SOME` lint.
++#[rustfmt::skip]
++fn search_is_some() {
++    let v = vec![3, 2, 1, 0, -1, -2, -3];
++    let y = &&42;
++
++    // Check `find().is_some()`, single-line case.
++    let _ = v.iter().find(|&x| *x < 0).is_some();
++    let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
++    let _ = (0..1).find(|x| *x == 0).is_some();
++    let _ = v.iter().find(|x| **x == 0).is_some();
++
++    // Check `find().is_some()`, multi-line case.
++    let _ = v.iter().find(|&x| {
++                              *x < 0
++                          }
++                   ).is_some();
++
++    // Check `position().is_some()`, single-line case.
++    let _ = v.iter().position(|&x| x < 0).is_some();
++
++    // Check `position().is_some()`, multi-line case.
++    let _ = v.iter().position(|&x| {
++                                  x < 0
++                              }
++                   ).is_some();
++
++    // Check `rposition().is_some()`, single-line case.
++    let _ = v.iter().rposition(|&x| x < 0).is_some();
++
++    // Check `rposition().is_some()`, multi-line case.
++    let _ = v.iter().rposition(|&x| {
++                                   x < 0
++                               }
++                   ).is_some();
++
++    // Check that we don't lint if the caller is not an `Iterator`.
++    let foo = IteratorFalsePositives { foo: 0 };
++    let _ = foo.find().is_some();
++    let _ = foo.position().is_some();
++    let _ = foo.rposition().is_some();
++}
++
++fn main() {
++    filter_next();
++    search_is_some();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..01cf487ac148e77f519563bac11d9a1da659e858
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,109 @@@
++error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name
++  --> $DIR/methods.rs:39:5
++   |
++LL | /     pub fn add(self, other: T) -> T {
++LL | |         self
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::should-implement-trait` implied by `-D warnings`
++
++error: methods called `new` usually return `Self`
++  --> $DIR/methods.rs:169:5
++   |
++LL | /     fn new() -> i32 {
++LL | |         0
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::new-ret-no-self` implied by `-D warnings`
++
++error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead.
++  --> $DIR/methods.rs:188:13
++   |
++LL |     let _ = v.iter().filter(|&x| *x < 0).next();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::filter-next` implied by `-D warnings`
++   = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)`
++
++error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead.
++  --> $DIR/methods.rs:191:13
++   |
++LL |       let _ = v.iter().filter(|&x| {
++   |  _____________^
++LL | |                                 *x < 0
++LL | |                             }
++LL | |                    ).next();
++   | |___________________________^
++
++error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
++  --> $DIR/methods.rs:208:22
++   |
++LL |     let _ = v.iter().find(|&x| *x < 0).is_some();
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)`
++   |
++   = note: `-D clippy::search-is-some` implied by `-D warnings`
++
++error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
++  --> $DIR/methods.rs:209:20
++   |
++LL |     let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
++   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)`
++
++error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
++  --> $DIR/methods.rs:210:20
++   |
++LL |     let _ = (0..1).find(|x| *x == 0).is_some();
++   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)`
++
++error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
++  --> $DIR/methods.rs:211:22
++   |
++LL |     let _ = v.iter().find(|x| **x == 0).is_some();
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)`
++
++error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
++  --> $DIR/methods.rs:214:13
++   |
++LL |       let _ = v.iter().find(|&x| {
++   |  _____________^
++LL | |                               *x < 0
++LL | |                           }
++LL | |                    ).is_some();
++   | |______________________________^
++
++error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`.
++  --> $DIR/methods.rs:220:22
++   |
++LL |     let _ = v.iter().position(|&x| x < 0).is_some();
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)`
++
++error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`.
++  --> $DIR/methods.rs:223:13
++   |
++LL |       let _ = v.iter().position(|&x| {
++   |  _____________^
++LL | |                                   x < 0
++LL | |                               }
++LL | |                    ).is_some();
++   | |______________________________^
++
++error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`.
++  --> $DIR/methods.rs:229:22
++   |
++LL |     let _ = v.iter().rposition(|&x| x < 0).is_some();
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)`
++
++error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`.
++  --> $DIR/methods.rs:232:13
++   |
++LL |       let _ = v.iter().rposition(|&x| {
++   |  _____________^
++LL | |                                    x < 0
++LL | |                                }
++LL | |                    ).is_some();
++   | |______________________________^
++
++error: aborting due to 13 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8307d4b3019f7cf077fc673c34bc7481da55c3ae
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++#![warn(clippy::all)]
++
++use std::cmp::max as my_max;
++use std::cmp::min as my_min;
++use std::cmp::{max, min};
++
++const LARGE: usize = 3;
++
++fn main() {
++    let x;
++    x = 2usize;
++    min(1, max(3, x));
++    min(max(3, x), 1);
++    max(min(x, 1), 3);
++    max(3, min(x, 1));
++
++    my_max(3, my_min(x, 1));
++
++    min(3, max(1, x)); // ok, could be 1, 2 or 3 depending on x
++
++    min(1, max(LARGE, x)); // no error, we don't lookup consts here
++
++    let y = 2isize;
++    min(max(y, -1), 3);
++
++    let s;
++    s = "Hello";
++
++    min("Apple", max("Zoo", s));
++    max(min(s, "Apple"), "Zoo");
++
++    max("Apple", min(s, "Zoo")); // ok
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b552c137f7c7c8513799936ce51fe45c7f8c14b3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++error: this `min`/`max` combination leads to constant result
++  --> $DIR/min_max.rs:12:5
++   |
++LL |     min(1, max(3, x));
++   |     ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::min-max` implied by `-D warnings`
++
++error: this `min`/`max` combination leads to constant result
++  --> $DIR/min_max.rs:13:5
++   |
++LL |     min(max(3, x), 1);
++   |     ^^^^^^^^^^^^^^^^^
++
++error: this `min`/`max` combination leads to constant result
++  --> $DIR/min_max.rs:14:5
++   |
++LL |     max(min(x, 1), 3);
++   |     ^^^^^^^^^^^^^^^^^
++
++error: this `min`/`max` combination leads to constant result
++  --> $DIR/min_max.rs:15:5
++   |
++LL |     max(3, min(x, 1));
++   |     ^^^^^^^^^^^^^^^^^
++
++error: this `min`/`max` combination leads to constant result
++  --> $DIR/min_max.rs:17:5
++   |
++LL |     my_max(3, my_min(x, 1));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this `min`/`max` combination leads to constant result
++  --> $DIR/min_max.rs:29:5
++   |
++LL |     min("Apple", max("Zoo", s));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this `min`/`max` combination leads to constant result
++  --> $DIR/min_max.rs:30:5
++   |
++LL |     max(min(s, "Apple"), "Zoo");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3ee77dcac31a0d6b3658fb4104147ab0a41402ef
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++// run-rustfix
++
++#![warn(clippy::mismatched_target_os)]
++#![allow(unused)]
++
++#[cfg(target_os = "cloudabi")]
++fn cloudabi() {}
++
++#[cfg(target_os = "hermit")]
++fn hermit() {}
++
++#[cfg(target_os = "wasi")]
++fn wasi() {}
++
++#[cfg(target_os = "none")]
++fn none() {}
++
++// list with conditions
++#[cfg(all(not(any(windows, target_os = "cloudabi")), target_os = "wasi"))]
++fn list() {}
++
++// windows is a valid target family, should be ignored
++#[cfg(windows)]
++fn windows() {}
++
++// correct use, should be ignored
++#[cfg(target_os = "hermit")]
++fn correct() {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9cc411418e4c4d979ca53f274aab55a803558226
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++// run-rustfix
++
++#![warn(clippy::mismatched_target_os)]
++#![allow(unused)]
++
++#[cfg(cloudabi)]
++fn cloudabi() {}
++
++#[cfg(hermit)]
++fn hermit() {}
++
++#[cfg(wasi)]
++fn wasi() {}
++
++#[cfg(none)]
++fn none() {}
++
++// list with conditions
++#[cfg(all(not(any(windows, cloudabi)), wasi))]
++fn list() {}
++
++// windows is a valid target family, should be ignored
++#[cfg(windows)]
++fn windows() {}
++
++// correct use, should be ignored
++#[cfg(target_os = "hermit")]
++fn correct() {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..78fc27752d239bcc9d57f2c54139b19503d9bc97
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_non_unix.rs:6:1
++   |
++LL | #[cfg(cloudabi)]
++   | ^^^^^^--------^^
++   |       |
++   |       help: try: `target_os = "cloudabi"`
++   |
++   = note: `-D clippy::mismatched-target-os` implied by `-D warnings`
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_non_unix.rs:9:1
++   |
++LL | #[cfg(hermit)]
++   | ^^^^^^------^^
++   |       |
++   |       help: try: `target_os = "hermit"`
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_non_unix.rs:12:1
++   |
++LL | #[cfg(wasi)]
++   | ^^^^^^----^^
++   |       |
++   |       help: try: `target_os = "wasi"`
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_non_unix.rs:15:1
++   |
++LL | #[cfg(none)]
++   | ^^^^^^----^^
++   |       |
++   |       help: try: `target_os = "none"`
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_non_unix.rs:19:1
++   |
++LL | #[cfg(all(not(any(windows, cloudabi)), wasi))]
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: try
++   |
++LL | #[cfg(all(not(any(windows, target_os = "cloudabi")), wasi))]
++   |                            ^^^^^^^^^^^^^^^^^^^^^^
++help: try
++   |
++LL | #[cfg(all(not(any(windows, cloudabi)), target_os = "wasi"))]
++   |                                        ^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7d9d406d99dfcabf0828ed6eb5f1b3125f916f7e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++// run-rustfix
++
++#![warn(clippy::mismatched_target_os)]
++#![allow(unused)]
++
++#[cfg(target_os = "linux")]
++fn linux() {}
++
++#[cfg(target_os = "freebsd")]
++fn freebsd() {}
++
++#[cfg(target_os = "dragonfly")]
++fn dragonfly() {}
++
++#[cfg(target_os = "openbsd")]
++fn openbsd() {}
++
++#[cfg(target_os = "netbsd")]
++fn netbsd() {}
++
++#[cfg(target_os = "macos")]
++fn macos() {}
++
++#[cfg(target_os = "ios")]
++fn ios() {}
++
++#[cfg(target_os = "android")]
++fn android() {}
++
++#[cfg(target_os = "emscripten")]
++fn emscripten() {}
++
++#[cfg(target_os = "fuchsia")]
++fn fuchsia() {}
++
++#[cfg(target_os = "haiku")]
++fn haiku() {}
++
++#[cfg(target_os = "illumos")]
++fn illumos() {}
++
++#[cfg(target_os = "l4re")]
++fn l4re() {}
++
++#[cfg(target_os = "redox")]
++fn redox() {}
++
++#[cfg(target_os = "solaris")]
++fn solaris() {}
++
++#[cfg(target_os = "vxworks")]
++fn vxworks() {}
++
++// list with conditions
++#[cfg(all(not(any(target_os = "solaris", target_os = "linux")), target_os = "freebsd"))]
++fn list() {}
++
++// correct use, should be ignored
++#[cfg(target_os = "freebsd")]
++fn correct() {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c1177f1eedc62153ad9eb2c48596fdffc9b346fc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++// run-rustfix
++
++#![warn(clippy::mismatched_target_os)]
++#![allow(unused)]
++
++#[cfg(linux)]
++fn linux() {}
++
++#[cfg(freebsd)]
++fn freebsd() {}
++
++#[cfg(dragonfly)]
++fn dragonfly() {}
++
++#[cfg(openbsd)]
++fn openbsd() {}
++
++#[cfg(netbsd)]
++fn netbsd() {}
++
++#[cfg(macos)]
++fn macos() {}
++
++#[cfg(ios)]
++fn ios() {}
++
++#[cfg(android)]
++fn android() {}
++
++#[cfg(emscripten)]
++fn emscripten() {}
++
++#[cfg(fuchsia)]
++fn fuchsia() {}
++
++#[cfg(haiku)]
++fn haiku() {}
++
++#[cfg(illumos)]
++fn illumos() {}
++
++#[cfg(l4re)]
++fn l4re() {}
++
++#[cfg(redox)]
++fn redox() {}
++
++#[cfg(solaris)]
++fn solaris() {}
++
++#[cfg(vxworks)]
++fn vxworks() {}
++
++// list with conditions
++#[cfg(all(not(any(solaris, linux)), freebsd))]
++fn list() {}
++
++// correct use, should be ignored
++#[cfg(target_os = "freebsd")]
++fn correct() {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fe9aeedb59c45b80b8b7d3b609e0841363fd98b2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,183 @@@
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_unix.rs:6:1
++   |
++LL | #[cfg(linux)]
++   | ^^^^^^-----^^
++   |       |
++   |       help: try: `target_os = "linux"`
++   |
++   = note: `-D clippy::mismatched-target-os` implied by `-D warnings`
++   = help: Did you mean `unix`?
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_unix.rs:9:1
++   |
++LL | #[cfg(freebsd)]
++   | ^^^^^^-------^^
++   |       |
++   |       help: try: `target_os = "freebsd"`
++   |
++   = help: Did you mean `unix`?
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_unix.rs:12:1
++   |
++LL | #[cfg(dragonfly)]
++   | ^^^^^^---------^^
++   |       |
++   |       help: try: `target_os = "dragonfly"`
++   |
++   = help: Did you mean `unix`?
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_unix.rs:15:1
++   |
++LL | #[cfg(openbsd)]
++   | ^^^^^^-------^^
++   |       |
++   |       help: try: `target_os = "openbsd"`
++   |
++   = help: Did you mean `unix`?
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_unix.rs:18:1
++   |
++LL | #[cfg(netbsd)]
++   | ^^^^^^------^^
++   |       |
++   |       help: try: `target_os = "netbsd"`
++   |
++   = help: Did you mean `unix`?
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_unix.rs:21:1
++   |
++LL | #[cfg(macos)]
++   | ^^^^^^-----^^
++   |       |
++   |       help: try: `target_os = "macos"`
++   |
++   = help: Did you mean `unix`?
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_unix.rs:24:1
++   |
++LL | #[cfg(ios)]
++   | ^^^^^^---^^
++   |       |
++   |       help: try: `target_os = "ios"`
++   |
++   = help: Did you mean `unix`?
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_unix.rs:27:1
++   |
++LL | #[cfg(android)]
++   | ^^^^^^-------^^
++   |       |
++   |       help: try: `target_os = "android"`
++   |
++   = help: Did you mean `unix`?
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_unix.rs:30:1
++   |
++LL | #[cfg(emscripten)]
++   | ^^^^^^----------^^
++   |       |
++   |       help: try: `target_os = "emscripten"`
++   |
++   = help: Did you mean `unix`?
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_unix.rs:33:1
++   |
++LL | #[cfg(fuchsia)]
++   | ^^^^^^-------^^
++   |       |
++   |       help: try: `target_os = "fuchsia"`
++   |
++   = help: Did you mean `unix`?
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_unix.rs:36:1
++   |
++LL | #[cfg(haiku)]
++   | ^^^^^^-----^^
++   |       |
++   |       help: try: `target_os = "haiku"`
++   |
++   = help: Did you mean `unix`?
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_unix.rs:39:1
++   |
++LL | #[cfg(illumos)]
++   | ^^^^^^-------^^
++   |       |
++   |       help: try: `target_os = "illumos"`
++   |
++   = help: Did you mean `unix`?
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_unix.rs:42:1
++   |
++LL | #[cfg(l4re)]
++   | ^^^^^^----^^
++   |       |
++   |       help: try: `target_os = "l4re"`
++   |
++   = help: Did you mean `unix`?
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_unix.rs:45:1
++   |
++LL | #[cfg(redox)]
++   | ^^^^^^-----^^
++   |       |
++   |       help: try: `target_os = "redox"`
++   |
++   = help: Did you mean `unix`?
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_unix.rs:48:1
++   |
++LL | #[cfg(solaris)]
++   | ^^^^^^-------^^
++   |       |
++   |       help: try: `target_os = "solaris"`
++   |
++   = help: Did you mean `unix`?
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_unix.rs:51:1
++   |
++LL | #[cfg(vxworks)]
++   | ^^^^^^-------^^
++   |       |
++   |       help: try: `target_os = "vxworks"`
++   |
++   = help: Did you mean `unix`?
++
++error: operating system used in target family position
++  --> $DIR/mismatched_target_os_unix.rs:55:1
++   |
++LL | #[cfg(all(not(any(solaris, linux)), freebsd))]
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: Did you mean `unix`?
++help: try
++   |
++LL | #[cfg(all(not(any(target_os = "solaris", linux)), freebsd))]
++   |                   ^^^^^^^^^^^^^^^^^^^^^
++help: try
++   |
++LL | #[cfg(all(not(any(solaris, target_os = "linux")), freebsd))]
++   |                            ^^^^^^^^^^^^^^^^^^^
++help: try
++   |
++LL | #[cfg(all(not(any(solaris, linux)), target_os = "freebsd"))]
++   |                                     ^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 17 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..51fd57df8df1da962322d50dc6edcf85726e9661
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++#![warn(clippy::missing_docs_in_private_items)]
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..da46f9886366c821f2f5feb90555f90e728be166
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++error: missing documentation for crate
++  --> $DIR/missing-doc-crate-missing.rs:1:1
++   |
++LL | / #![warn(clippy::missing_docs_in_private_items)]
++LL | |
++LL | | fn main() {}
++   | |____________^
++   |
++   = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..04711f864886b06aab41b07bef741e9a2e84271d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++#![warn(clippy::missing_docs_in_private_items)]
++#![feature(external_doc)]
++#![doc(include = "../../README.md")]
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..57af84dcdf4d0905c9235821335a672980d214b4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,87 @@@
++#![warn(clippy::missing_docs_in_private_items)]
++#![allow(dead_code)]
++#![feature(associated_type_defaults)]
++
++//! Some garbage docs for the crate here
++#![doc = "More garbage"]
++
++struct Foo {
++    a: isize,
++    b: isize,
++}
++
++pub struct PubFoo {
++    pub a: isize,
++    b: isize,
++}
++
++#[allow(clippy::missing_docs_in_private_items)]
++pub struct PubFoo2 {
++    pub a: isize,
++    pub c: isize,
++}
++
++/// dox
++pub trait A {
++    /// dox
++    fn foo(&self);
++    /// dox
++    fn foo_with_impl(&self) {}
++}
++
++#[allow(clippy::missing_docs_in_private_items)]
++trait B {
++    fn foo(&self);
++    fn foo_with_impl(&self) {}
++}
++
++pub trait C {
++    fn foo(&self);
++    fn foo_with_impl(&self) {}
++}
++
++#[allow(clippy::missing_docs_in_private_items)]
++pub trait D {
++    fn dummy(&self) {}
++}
++
++/// dox
++pub trait E: Sized {
++    type AssociatedType;
++    type AssociatedTypeDef = Self;
++
++    /// dox
++    type DocumentedType;
++    /// dox
++    type DocumentedTypeDef = Self;
++    /// dox
++    fn dummy(&self) {}
++}
++
++impl Foo {
++    pub fn foo() {}
++    fn bar() {}
++}
++
++impl PubFoo {
++    pub fn foo() {}
++    /// dox
++    pub fn foo1() {}
++    fn foo2() {}
++    #[allow(clippy::missing_docs_in_private_items)]
++    pub fn foo3() {}
++}
++
++#[allow(clippy::missing_docs_in_private_items)]
++trait F {
++    fn a();
++    fn b(&self);
++}
++
++// should need to redefine documentation for implementations of traits
++impl F for Foo {
++    fn a() {}
++    fn b(&self) {}
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9656a39abceb5b460b1261a84a1c0919daa98a57
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,103 @@@
++error: missing documentation for a struct
++  --> $DIR/missing-doc-impl.rs:8:1
++   |
++LL | / struct Foo {
++LL | |     a: isize,
++LL | |     b: isize,
++LL | | }
++   | |_^
++   |
++   = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings`
++
++error: missing documentation for a struct field
++  --> $DIR/missing-doc-impl.rs:9:5
++   |
++LL |     a: isize,
++   |     ^^^^^^^^
++
++error: missing documentation for a struct field
++  --> $DIR/missing-doc-impl.rs:10:5
++   |
++LL |     b: isize,
++   |     ^^^^^^^^
++
++error: missing documentation for a struct
++  --> $DIR/missing-doc-impl.rs:13:1
++   |
++LL | / pub struct PubFoo {
++LL | |     pub a: isize,
++LL | |     b: isize,
++LL | | }
++   | |_^
++
++error: missing documentation for a struct field
++  --> $DIR/missing-doc-impl.rs:14:5
++   |
++LL |     pub a: isize,
++   |     ^^^^^^^^^^^^
++
++error: missing documentation for a struct field
++  --> $DIR/missing-doc-impl.rs:15:5
++   |
++LL |     b: isize,
++   |     ^^^^^^^^
++
++error: missing documentation for a trait
++  --> $DIR/missing-doc-impl.rs:38:1
++   |
++LL | / pub trait C {
++LL | |     fn foo(&self);
++LL | |     fn foo_with_impl(&self) {}
++LL | | }
++   | |_^
++
++error: missing documentation for a trait method
++  --> $DIR/missing-doc-impl.rs:39:5
++   |
++LL |     fn foo(&self);
++   |     ^^^^^^^^^^^^^^
++
++error: missing documentation for a trait method
++  --> $DIR/missing-doc-impl.rs:40:5
++   |
++LL |     fn foo_with_impl(&self) {}
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for an associated type
++  --> $DIR/missing-doc-impl.rs:50:5
++   |
++LL |     type AssociatedType;
++   |     ^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for an associated type
++  --> $DIR/missing-doc-impl.rs:51:5
++   |
++LL |     type AssociatedTypeDef = Self;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a method
++  --> $DIR/missing-doc-impl.rs:62:5
++   |
++LL |     pub fn foo() {}
++   |     ^^^^^^^^^^^^^^^
++
++error: missing documentation for a method
++  --> $DIR/missing-doc-impl.rs:63:5
++   |
++LL |     fn bar() {}
++   |     ^^^^^^^^^^^
++
++error: missing documentation for a method
++  --> $DIR/missing-doc-impl.rs:67:5
++   |
++LL |     pub fn foo() {}
++   |     ^^^^^^^^^^^^^^^
++
++error: missing documentation for a method
++  --> $DIR/missing-doc-impl.rs:70:5
++   |
++LL |     fn foo2() {}
++   |     ^^^^^^^^^^^^
++
++error: aborting due to 15 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a9bf7140a1e594ae61cf64f9a000ee6716b669b3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,102 @@@
++#![warn(clippy::missing_docs_in_private_items)]
++// When denying at the crate level, be sure to not get random warnings from the
++// injected intrinsics by the compiler.
++#![allow(dead_code)]
++#![feature(global_asm)]
++
++//! Some garbage docs for the crate here
++#![doc = "More garbage"]
++
++type Typedef = String;
++pub type PubTypedef = String;
++
++mod module_no_dox {}
++pub mod pub_module_no_dox {}
++
++/// dox
++pub fn foo() {}
++pub fn foo2() {}
++fn foo3() {}
++#[allow(clippy::missing_docs_in_private_items)]
++pub fn foo4() {}
++
++// It sure is nice if doc(hidden) implies allow(missing_docs), and that it
++// applies recursively
++#[doc(hidden)]
++mod a {
++    pub fn baz() {}
++    pub mod b {
++        pub fn baz() {}
++    }
++}
++
++enum Baz {
++    BazA { a: isize, b: isize },
++    BarB,
++}
++
++pub enum PubBaz {
++    PubBazA { a: isize },
++}
++
++/// dox
++pub enum PubBaz2 {
++    /// dox
++    PubBaz2A {
++        /// dox
++        a: isize,
++    },
++}
++
++#[allow(clippy::missing_docs_in_private_items)]
++pub enum PubBaz3 {
++    PubBaz3A { b: isize },
++}
++
++#[doc(hidden)]
++pub fn baz() {}
++
++const FOO: u32 = 0;
++/// dox
++pub const FOO1: u32 = 0;
++#[allow(clippy::missing_docs_in_private_items)]
++pub const FOO2: u32 = 0;
++#[doc(hidden)]
++pub const FOO3: u32 = 0;
++pub const FOO4: u32 = 0;
++
++static BAR: u32 = 0;
++/// dox
++pub static BAR1: u32 = 0;
++#[allow(clippy::missing_docs_in_private_items)]
++pub static BAR2: u32 = 0;
++#[doc(hidden)]
++pub static BAR3: u32 = 0;
++pub static BAR4: u32 = 0;
++
++mod internal_impl {
++    /// dox
++    pub fn documented() {}
++    pub fn undocumented1() {}
++    pub fn undocumented2() {}
++    fn undocumented3() {}
++    /// dox
++    pub mod globbed {
++        /// dox
++        pub fn also_documented() {}
++        pub fn also_undocumented1() {}
++        fn also_undocumented2() {}
++    }
++}
++/// dox
++pub mod public_interface {
++    pub use internal_impl::documented as foo;
++    pub use internal_impl::globbed::*;
++    pub use internal_impl::undocumented1 as bar;
++    pub use internal_impl::{documented, undocumented2};
++}
++
++fn main() {}
++
++// Ensure global asm doesn't require documentation.
++global_asm! { "" }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a876dc078ebff96f25c8e34efad90156b896a70a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,159 @@@
++error: missing documentation for a type alias
++  --> $DIR/missing-doc.rs:10:1
++   |
++LL | type Typedef = String;
++   | ^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings`
++
++error: missing documentation for a type alias
++  --> $DIR/missing-doc.rs:11:1
++   |
++LL | pub type PubTypedef = String;
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a module
++  --> $DIR/missing-doc.rs:13:1
++   |
++LL | mod module_no_dox {}
++   | ^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a module
++  --> $DIR/missing-doc.rs:14:1
++   |
++LL | pub mod pub_module_no_dox {}
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a function
++  --> $DIR/missing-doc.rs:18:1
++   |
++LL | pub fn foo2() {}
++   | ^^^^^^^^^^^^^^^^
++
++error: missing documentation for a function
++  --> $DIR/missing-doc.rs:19:1
++   |
++LL | fn foo3() {}
++   | ^^^^^^^^^^^^
++
++error: missing documentation for an enum
++  --> $DIR/missing-doc.rs:33:1
++   |
++LL | / enum Baz {
++LL | |     BazA { a: isize, b: isize },
++LL | |     BarB,
++LL | | }
++   | |_^
++
++error: missing documentation for a variant
++  --> $DIR/missing-doc.rs:34:5
++   |
++LL |     BazA { a: isize, b: isize },
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a struct field
++  --> $DIR/missing-doc.rs:34:12
++   |
++LL |     BazA { a: isize, b: isize },
++   |            ^^^^^^^^
++
++error: missing documentation for a struct field
++  --> $DIR/missing-doc.rs:34:22
++   |
++LL |     BazA { a: isize, b: isize },
++   |                      ^^^^^^^^
++
++error: missing documentation for a variant
++  --> $DIR/missing-doc.rs:35:5
++   |
++LL |     BarB,
++   |     ^^^^
++
++error: missing documentation for an enum
++  --> $DIR/missing-doc.rs:38:1
++   |
++LL | / pub enum PubBaz {
++LL | |     PubBazA { a: isize },
++LL | | }
++   | |_^
++
++error: missing documentation for a variant
++  --> $DIR/missing-doc.rs:39:5
++   |
++LL |     PubBazA { a: isize },
++   |     ^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a struct field
++  --> $DIR/missing-doc.rs:39:15
++   |
++LL |     PubBazA { a: isize },
++   |               ^^^^^^^^
++
++error: missing documentation for a constant
++  --> $DIR/missing-doc.rs:59:1
++   |
++LL | const FOO: u32 = 0;
++   | ^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a constant
++  --> $DIR/missing-doc.rs:66:1
++   |
++LL | pub const FOO4: u32 = 0;
++   | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a static
++  --> $DIR/missing-doc.rs:68:1
++   |
++LL | static BAR: u32 = 0;
++   | ^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a static
++  --> $DIR/missing-doc.rs:75:1
++   |
++LL | pub static BAR4: u32 = 0;
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a module
++  --> $DIR/missing-doc.rs:77:1
++   |
++LL | / mod internal_impl {
++LL | |     /// dox
++LL | |     pub fn documented() {}
++LL | |     pub fn undocumented1() {}
++...  |
++LL | |     }
++LL | | }
++   | |_^
++
++error: missing documentation for a function
++  --> $DIR/missing-doc.rs:80:5
++   |
++LL |     pub fn undocumented1() {}
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a function
++  --> $DIR/missing-doc.rs:81:5
++   |
++LL |     pub fn undocumented2() {}
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a function
++  --> $DIR/missing-doc.rs:82:5
++   |
++LL |     fn undocumented3() {}
++   |     ^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a function
++  --> $DIR/missing-doc.rs:87:9
++   |
++LL |         pub fn also_undocumented1() {}
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: missing documentation for a function
++  --> $DIR/missing-doc.rs:88:9
++   |
++LL |         fn also_undocumented2() {}
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 24 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ba352ef9ee932a64064409e83f6c721e23286617
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,103 @@@
++//! False-positive tests to ensure we don't suggest `const` for things where it would cause a
++//! compilation error.
++//! The .stderr output of this test should be empty. Otherwise it's a bug somewhere.
++
++#![warn(clippy::missing_const_for_fn)]
++#![allow(incomplete_features)]
++#![feature(start, const_generics)]
++
++struct Game;
++
++// This should not be linted because it's already const
++const fn already_const() -> i32 {
++    32
++}
++
++impl Game {
++    // This should not be linted because it's already const
++    pub const fn already_const() -> i32 {
++        32
++    }
++}
++
++// Allowing on this function, because it would lint, which we don't want in this case.
++#[allow(clippy::missing_const_for_fn)]
++fn random() -> u32 {
++    42
++}
++
++// We should not suggest to make this function `const` because `random()` is non-const
++fn random_caller() -> u32 {
++    random()
++}
++
++static Y: u32 = 0;
++
++// We should not suggest to make this function `const` because const functions are not allowed to
++// refer to a static variable
++fn get_y() -> u32 {
++    Y
++    //~^ ERROR E0013
++}
++
++// Don't lint entrypoint functions
++#[start]
++fn init(num: isize, something: *const *const u8) -> isize {
++    1
++}
++
++trait Foo {
++    // This should not be suggested to be made const
++    // (rustc doesn't allow const trait methods)
++    fn f() -> u32;
++
++    // This should not be suggested to be made const either
++    fn g() -> u32 {
++        33
++    }
++}
++
++// Don't lint in external macros (derive)
++#[derive(PartialEq, Eq)]
++struct Point(isize, isize);
++
++impl std::ops::Add for Point {
++    type Output = Self;
++
++    // Don't lint in trait impls of derived methods
++    fn add(self, other: Self) -> Self {
++        Point(self.0 + other.0, self.1 + other.1)
++    }
++}
++
++mod with_drop {
++    pub struct A;
++    pub struct B;
++    impl Drop for A {
++        fn drop(&mut self) {}
++    }
++
++    impl A {
++        // This can not be const because the type implements `Drop`.
++        pub fn a(self) -> B {
++            B
++        }
++    }
++
++    impl B {
++        // This can not be const because `a` implements `Drop`.
++        pub fn a(self, a: A) -> B {
++            B
++        }
++    }
++}
++
++fn const_generic_params<T, const N: usize>(t: &[T; N]) -> &[T; N] {
++    t
++}
++
++fn const_generic_return<T, const N: usize>(t: &[T]) -> &[T; N] {
++    let p = t.as_ptr() as *const [T; N];
++
++    unsafe { &*p }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c6f44b7daa3421b13a6c9da0b28bc564577af9c6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,74 @@@
++#![warn(clippy::missing_const_for_fn)]
++#![allow(incomplete_features, clippy::let_and_return)]
++#![feature(const_generics)]
++
++use std::mem::transmute;
++
++struct Game {
++    guess: i32,
++}
++
++impl Game {
++    // Could be const
++    pub fn new() -> Self {
++        Self { guess: 42 }
++    }
++
++    fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
++        b
++    }
++}
++
++// Could be const
++fn one() -> i32 {
++    1
++}
++
++// Could also be const
++fn two() -> i32 {
++    let abc = 2;
++    abc
++}
++
++// Could be const (since Rust 1.39)
++fn string() -> String {
++    String::new()
++}
++
++// Could be const
++unsafe fn four() -> i32 {
++    4
++}
++
++// Could also be const
++fn generic<T>(t: T) -> T {
++    t
++}
++
++fn sub(x: u32) -> usize {
++    unsafe { transmute(&x) }
++}
++
++// NOTE: This is currently not yet allowed to be const
++// Once implemented, Clippy should be able to suggest this as const, too.
++fn generic_arr<T: Copy>(t: [T; 1]) -> T {
++    t[0]
++}
++
++mod with_drop {
++    pub struct A;
++    pub struct B;
++    impl Drop for A {
++        fn drop(&mut self) {}
++    }
++
++    impl B {
++        // This can be const, because `a` is passed by reference
++        pub fn b(self, a: &A) -> B {
++            B
++        }
++    }
++}
++
++// Should not be const
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8dde56cd79f443f39b57873eda012e25f16055de
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,77 @@@
++error: this could be a `const fn`
++  --> $DIR/could_be_const.rs:13:5
++   |
++LL | /     pub fn new() -> Self {
++LL | |         Self { guess: 42 }
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
++
++error: this could be a `const fn`
++  --> $DIR/could_be_const.rs:17:5
++   |
++LL | /     fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
++LL | |         b
++LL | |     }
++   | |_____^
++
++error: this could be a `const fn`
++  --> $DIR/could_be_const.rs:23:1
++   |
++LL | / fn one() -> i32 {
++LL | |     1
++LL | | }
++   | |_^
++
++error: this could be a `const fn`
++  --> $DIR/could_be_const.rs:28:1
++   |
++LL | / fn two() -> i32 {
++LL | |     let abc = 2;
++LL | |     abc
++LL | | }
++   | |_^
++
++error: this could be a `const fn`
++  --> $DIR/could_be_const.rs:34:1
++   |
++LL | / fn string() -> String {
++LL | |     String::new()
++LL | | }
++   | |_^
++
++error: this could be a `const fn`
++  --> $DIR/could_be_const.rs:39:1
++   |
++LL | / unsafe fn four() -> i32 {
++LL | |     4
++LL | | }
++   | |_^
++
++error: this could be a `const fn`
++  --> $DIR/could_be_const.rs:44:1
++   |
++LL | / fn generic<T>(t: T) -> T {
++LL | |     t
++LL | | }
++   | |_^
++
++error: this could be a `const fn`
++  --> $DIR/could_be_const.rs:48:1
++   |
++LL | / fn sub(x: u32) -> usize {
++LL | |     unsafe { transmute(&x) }
++LL | | }
++   | |_^
++
++error: this could be a `const fn`
++  --> $DIR/could_be_const.rs:67:9
++   |
++LL | /         pub fn b(self, a: &A) -> B {
++LL | |             B
++LL | |         }
++   | |_________^
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b73b24b8e0a3b9b50fc6eaaf7b3e68d95168d324
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,66 @@@
++#![warn(clippy::missing_inline_in_public_items)]
++#![crate_type = "dylib"]
++// When denying at the crate level, be sure to not get random warnings from the
++// injected intrinsics by the compiler.
++#![allow(dead_code, non_snake_case)]
++
++type Typedef = String;
++pub type PubTypedef = String;
++
++struct Foo {} // ok
++pub struct PubFoo {} // ok
++enum FooE {} // ok
++pub enum PubFooE {} // ok
++
++mod module {} // ok
++pub mod pub_module {} // ok
++
++fn foo() {}
++pub fn pub_foo() {} // missing #[inline]
++#[inline]
++pub fn pub_foo_inline() {} // ok
++#[inline(always)]
++pub fn pub_foo_inline_always() {} // ok
++
++#[allow(clippy::missing_inline_in_public_items)]
++pub fn pub_foo_no_inline() {}
++
++trait Bar {
++    fn Bar_a(); // ok
++    fn Bar_b() {} // ok
++}
++
++pub trait PubBar {
++    fn PubBar_a(); // ok
++    fn PubBar_b() {} // missing #[inline]
++    #[inline]
++    fn PubBar_c() {} // ok
++}
++
++// none of these need inline because Foo is not exported
++impl PubBar for Foo {
++    fn PubBar_a() {} // ok
++    fn PubBar_b() {} // ok
++    fn PubBar_c() {} // ok
++}
++
++// all of these need inline because PubFoo is exported
++impl PubBar for PubFoo {
++    fn PubBar_a() {} // missing #[inline]
++    fn PubBar_b() {} // missing #[inline]
++    fn PubBar_c() {} // missing #[inline]
++}
++
++// do not need inline because Foo is not exported
++impl Foo {
++    fn FooImpl() {} // ok
++}
++
++// need inline because PubFoo is exported
++impl PubFoo {
++    pub fn PubFooImpl() {} // missing #[inline]
++}
++
++// do not lint this since users cannot control the external code
++#[derive(Debug)]
++pub struct S {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..40b92b7647bf766065fcba43519b9d0709450e3a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: missing `#[inline]` for a function
++  --> $DIR/missing_inline.rs:19:1
++   |
++LL | pub fn pub_foo() {} // missing #[inline]
++   | ^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::missing-inline-in-public-items` implied by `-D warnings`
++
++error: missing `#[inline]` for a default trait method
++  --> $DIR/missing_inline.rs:35:5
++   |
++LL |     fn PubBar_b() {} // missing #[inline]
++   |     ^^^^^^^^^^^^^^^^
++
++error: missing `#[inline]` for a method
++  --> $DIR/missing_inline.rs:49:5
++   |
++LL |     fn PubBar_a() {} // missing #[inline]
++   |     ^^^^^^^^^^^^^^^^
++
++error: missing `#[inline]` for a method
++  --> $DIR/missing_inline.rs:50:5
++   |
++LL |     fn PubBar_b() {} // missing #[inline]
++   |     ^^^^^^^^^^^^^^^^
++
++error: missing `#[inline]` for a method
++  --> $DIR/missing_inline.rs:51:5
++   |
++LL |     fn PubBar_c() {} // missing #[inline]
++   |     ^^^^^^^^^^^^^^^^
++
++error: missing `#[inline]` for a method
++  --> $DIR/missing_inline.rs:61:5
++   |
++LL |     pub fn PubFooImpl() {} // missing #[inline]
++   |     ^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..baee77357303814e749dbc3c9909368b4e531125
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++// run-rustfix
++
++#![allow(dead_code, unused_variables, clippy::excessive_precision)]
++
++fn main() {
++    let fail14 = 2_i32;
++    let fail15 = 4_i64;
++    let fail16 = 7_i8; //
++    let fail17 = 23_i16; //
++    let ok18 = 23_128;
++
++    let fail20 = 2_i8; //
++    let fail21 = 4_i16; //
++
++    let fail24 = 12.34_f64;
++    let fail25 = 1E2_f32;
++    let fail26 = 43E7_f64;
++    let fail27 = 243E17_f32;
++    #[allow(overflowing_literals)]
++    let fail28 = 241_251_235E723_f64;
++    let fail29 = 42_279.911_f32;
++
++    let _ = 1.123_45E1_f32;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6de447f40214b527083054f64874989a93c984f5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++// run-rustfix
++
++#![allow(dead_code, unused_variables, clippy::excessive_precision)]
++
++fn main() {
++    let fail14 = 2_32;
++    let fail15 = 4_64;
++    let fail16 = 7_8; //
++    let fail17 = 23_16; //
++    let ok18 = 23_128;
++
++    let fail20 = 2__8; //
++    let fail21 = 4___16; //
++
++    let fail24 = 12.34_64;
++    let fail25 = 1E2_32;
++    let fail26 = 43E7_64;
++    let fail27 = 243E17_32;
++    #[allow(overflowing_literals)]
++    let fail28 = 241251235E723_64;
++    let fail29 = 42279.911_32;
++
++    let _ = 1.12345E1_32;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..48a7ae904948c279c8c121e04526ce991e48888d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,82 @@@
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:6:18
++   |
++LL |     let fail14 = 2_32;
++   |                  ^^^^ help: did you mean to write: `2_i32`
++   |
++   = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default
++
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:7:18
++   |
++LL |     let fail15 = 4_64;
++   |                  ^^^^ help: did you mean to write: `4_i64`
++
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:8:18
++   |
++LL |     let fail16 = 7_8; //
++   |                  ^^^ help: did you mean to write: `7_i8`
++
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:9:18
++   |
++LL |     let fail17 = 23_16; //
++   |                  ^^^^^ help: did you mean to write: `23_i16`
++
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:12:18
++   |
++LL |     let fail20 = 2__8; //
++   |                  ^^^^ help: did you mean to write: `2_i8`
++
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:13:18
++   |
++LL |     let fail21 = 4___16; //
++   |                  ^^^^^^ help: did you mean to write: `4_i16`
++
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:15:18
++   |
++LL |     let fail24 = 12.34_64;
++   |                  ^^^^^^^^ help: did you mean to write: `12.34_f64`
++
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:16:18
++   |
++LL |     let fail25 = 1E2_32;
++   |                  ^^^^^^ help: did you mean to write: `1E2_f32`
++
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:17:18
++   |
++LL |     let fail26 = 43E7_64;
++   |                  ^^^^^^^ help: did you mean to write: `43E7_f64`
++
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:18:18
++   |
++LL |     let fail27 = 243E17_32;
++   |                  ^^^^^^^^^ help: did you mean to write: `243E17_f32`
++
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:20:18
++   |
++LL |     let fail28 = 241251235E723_64;
++   |                  ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64`
++
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:21:18
++   |
++LL |     let fail29 = 42279.911_32;
++   |                  ^^^^^^^^^^^^ help: did you mean to write: `42_279.911_f32`
++
++error: mistyped literal suffix
++  --> $DIR/mistyped_literal_suffix.rs:23:13
++   |
++LL |     let _ = 1.12345E1_32;
++   |             ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32`
++
++error: aborting due to 13 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a23aba9164a5c2d22cfc20b30ad4251da01b26a7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++#![warn(clippy::module_inception)]
++
++mod foo {
++    mod bar {
++        mod bar {
++            mod foo {}
++        }
++        mod foo {}
++    }
++    mod foo {
++        mod bar {}
++    }
++}
++
++// No warning. See <https://github.com/rust-lang/rust-clippy/issues/1220>.
++mod bar {
++    #[allow(clippy::module_inception)]
++    mod bar {}
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..77564dce9eb48fd5a3524f220004d92d2b0f1c08
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++error: module has the same name as its containing module
++  --> $DIR/module_inception.rs:5:9
++   |
++LL | /         mod bar {
++LL | |             mod foo {}
++LL | |         }
++   | |_________^
++   |
++   = note: `-D clippy::module-inception` implied by `-D warnings`
++
++error: module has the same name as its containing module
++  --> $DIR/module_inception.rs:10:5
++   |
++LL | /     mod foo {
++LL | |         mod bar {}
++LL | |     }
++   | |_____^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..669bf01a84c1067f0eb9222c286abc64fb3f7fcf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++// compile-flags: --test
++
++#![warn(clippy::module_name_repetitions)]
++#![allow(dead_code)]
++
++mod foo {
++    pub fn foo() {}
++    pub fn foo_bar() {}
++    pub fn bar_foo() {}
++    pub struct FooCake {}
++    pub enum CakeFoo {}
++    pub struct Foo7Bar;
++
++    // Should not warn
++    pub struct Foobar;
++}
++
++#[cfg(test)]
++mod test {
++    #[test]
++    fn it_works() {
++        assert_eq!(2 + 2, 4);
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bdd217a969c05acfcee5efe23b2e111b72451b62
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++error: item name starts with its containing module's name
++  --> $DIR/module_name_repetitions.rs:8:5
++   |
++LL |     pub fn foo_bar() {}
++   |     ^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::module-name-repetitions` implied by `-D warnings`
++
++error: item name ends with its containing module's name
++  --> $DIR/module_name_repetitions.rs:9:5
++   |
++LL |     pub fn bar_foo() {}
++   |     ^^^^^^^^^^^^^^^^^^^
++
++error: item name starts with its containing module's name
++  --> $DIR/module_name_repetitions.rs:10:5
++   |
++LL |     pub struct FooCake {}
++   |     ^^^^^^^^^^^^^^^^^^^^^
++
++error: item name ends with its containing module's name
++  --> $DIR/module_name_repetitions.rs:11:5
++   |
++LL |     pub enum CakeFoo {}
++   |     ^^^^^^^^^^^^^^^^^^^
++
++error: item name starts with its containing module's name
++  --> $DIR/module_name_repetitions.rs:12:5
++   |
++LL |     pub struct Foo7Bar;
++   |     ^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b010b0dbdfa69e5c241745a70ddf6eaef7a1d42a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++#![warn(clippy::modulo_arithmetic)]
++#![allow(
++    unused,
++    clippy::shadow_reuse,
++    clippy::shadow_unrelated,
++    clippy::no_effect,
++    clippy::unnecessary_operation,
++    clippy::modulo_one
++)]
++
++fn main() {
++    // Lint when both sides are const and of the opposite sign
++    -1.6 % 2.1;
++    1.6 % -2.1;
++    (1.1 - 2.3) % (1.1 + 2.3);
++    (1.1 + 2.3) % (1.1 - 2.3);
++
++    // Lint on floating point numbers
++    let a_f32: f32 = -1.6;
++    let mut b_f32: f32 = 2.1;
++    a_f32 % b_f32;
++    b_f32 % a_f32;
++    b_f32 %= a_f32;
++
++    let a_f64: f64 = -1.6;
++    let mut b_f64: f64 = 2.1;
++    a_f64 % b_f64;
++    b_f64 % a_f64;
++    b_f64 %= a_f64;
++
++    // No lint when both sides are const and of the same sign
++    1.6 % 2.1;
++    -1.6 % -2.1;
++    (1.1 + 2.3) % (-1.1 + 2.3);
++    (-1.1 - 2.3) % (1.1 - 2.3);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7bfdb0bde60706bc3765f87bd98a02211cde1265
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,83 @@@
++error: you are using modulo operator on constants with different signs: `-1.600 % 2.100`
++  --> $DIR/modulo_arithmetic_float.rs:13:5
++   |
++LL |     -1.6 % 2.1;
++   |     ^^^^^^^^^^
++   |
++   = note: `-D clippy::modulo-arithmetic` implied by `-D warnings`
++   = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on constants with different signs: `1.600 % -2.100`
++  --> $DIR/modulo_arithmetic_float.rs:14:5
++   |
++LL |     1.6 % -2.1;
++   |     ^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on constants with different signs: `-1.200 % 3.400`
++  --> $DIR/modulo_arithmetic_float.rs:15:5
++   |
++LL |     (1.1 - 2.3) % (1.1 + 2.3);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on constants with different signs: `3.400 % -1.200`
++  --> $DIR/modulo_arithmetic_float.rs:16:5
++   |
++LL |     (1.1 + 2.3) % (1.1 - 2.3);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_float.rs:21:5
++   |
++LL |     a_f32 % b_f32;
++   |     ^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_float.rs:22:5
++   |
++LL |     b_f32 % a_f32;
++   |     ^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_float.rs:23:5
++   |
++LL |     b_f32 %= a_f32;
++   |     ^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_float.rs:27:5
++   |
++LL |     a_f64 % b_f64;
++   |     ^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_float.rs:28:5
++   |
++LL |     b_f64 % a_f64;
++   |     ^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_float.rs:29:5
++   |
++LL |     b_f64 %= a_f64;
++   |     ^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++
++error: aborting due to 10 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..779d035c5f8a28850939afd1000a494b2fb737cc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,90 @@@
++#![warn(clippy::modulo_arithmetic)]
++#![allow(
++    unused,
++    clippy::shadow_reuse,
++    clippy::shadow_unrelated,
++    clippy::no_effect,
++    clippy::unnecessary_operation,
++    clippy::modulo_one
++)]
++
++fn main() {
++    // Lint on signed integral numbers
++    let a = -1;
++    let mut b = 2;
++    a % b;
++    b % a;
++    b %= a;
++
++    let a_i8: i8 = 1;
++    let mut b_i8: i8 = 2;
++    a_i8 % b_i8;
++    b_i8 %= a_i8;
++
++    let a_i16: i16 = 1;
++    let mut b_i16: i16 = 2;
++    a_i16 % b_i16;
++    b_i16 %= a_i16;
++
++    let a_i32: i32 = 1;
++    let mut b_i32: i32 = 2;
++    a_i32 % b_i32;
++    b_i32 %= a_i32;
++
++    let a_i64: i64 = 1;
++    let mut b_i64: i64 = 2;
++    a_i64 % b_i64;
++    b_i64 %= a_i64;
++
++    let a_i128: i128 = 1;
++    let mut b_i128: i128 = 2;
++    a_i128 % b_i128;
++    b_i128 %= a_i128;
++
++    let a_isize: isize = 1;
++    let mut b_isize: isize = 2;
++    a_isize % b_isize;
++    b_isize %= a_isize;
++
++    let a = 1;
++    let mut b = 2;
++    a % b;
++    b %= a;
++
++    // No lint on unsigned integral value
++    let a_u8: u8 = 17;
++    let b_u8: u8 = 3;
++    a_u8 % b_u8;
++    let mut a_u8: u8 = 1;
++    a_u8 %= 2;
++
++    let a_u16: u16 = 17;
++    let b_u16: u16 = 3;
++    a_u16 % b_u16;
++    let mut a_u16: u16 = 1;
++    a_u16 %= 2;
++
++    let a_u32: u32 = 17;
++    let b_u32: u32 = 3;
++    a_u32 % b_u32;
++    let mut a_u32: u32 = 1;
++    a_u32 %= 2;
++
++    let a_u64: u64 = 17;
++    let b_u64: u64 = 3;
++    a_u64 % b_u64;
++    let mut a_u64: u64 = 1;
++    a_u64 %= 2;
++
++    let a_u128: u128 = 17;
++    let b_u128: u128 = 3;
++    a_u128 % b_u128;
++    let mut a_u128: u128 = 1;
++    a_u128 %= 2;
++
++    let a_usize: usize = 17;
++    let b_usize: usize = 3;
++    a_usize % b_usize;
++    let mut a_usize: usize = 1;
++    a_usize %= 2;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e863b838699e9dc5a3a61a97490ddd162de16944
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,156 @@@
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_integral.rs:15:5
++   |
++LL |     a % b;
++   |     ^^^^^
++   |
++   = note: `-D clippy::modulo-arithmetic` implied by `-D warnings`
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_integral.rs:16:5
++   |
++LL |     b % a;
++   |     ^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_integral.rs:17:5
++   |
++LL |     b %= a;
++   |     ^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_integral.rs:21:5
++   |
++LL |     a_i8 % b_i8;
++   |     ^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_integral.rs:22:5
++   |
++LL |     b_i8 %= a_i8;
++   |     ^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_integral.rs:26:5
++   |
++LL |     a_i16 % b_i16;
++   |     ^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_integral.rs:27:5
++   |
++LL |     b_i16 %= a_i16;
++   |     ^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_integral.rs:31:5
++   |
++LL |     a_i32 % b_i32;
++   |     ^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_integral.rs:32:5
++   |
++LL |     b_i32 %= a_i32;
++   |     ^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_integral.rs:36:5
++   |
++LL |     a_i64 % b_i64;
++   |     ^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_integral.rs:37:5
++   |
++LL |     b_i64 %= a_i64;
++   |     ^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_integral.rs:41:5
++   |
++LL |     a_i128 % b_i128;
++   |     ^^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_integral.rs:42:5
++   |
++LL |     b_i128 %= a_i128;
++   |     ^^^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_integral.rs:46:5
++   |
++LL |     a_isize % b_isize;
++   |     ^^^^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_integral.rs:47:5
++   |
++LL |     b_isize %= a_isize;
++   |     ^^^^^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_integral.rs:51:5
++   |
++LL |     a % b;
++   |     ^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on types that might have different signs
++  --> $DIR/modulo_arithmetic_integral.rs:52:5
++   |
++LL |     b %= a;
++   |     ^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: aborting due to 17 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..57a96692c0097fb26d32857cf53ba75a412ed72e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++#![warn(clippy::modulo_arithmetic)]
++#![allow(
++    unused,
++    clippy::shadow_reuse,
++    clippy::shadow_unrelated,
++    clippy::no_effect,
++    clippy::unnecessary_operation,
++    clippy::modulo_one
++)]
++
++fn main() {
++    // Lint when both sides are const and of the opposite sign
++    -1 % 2;
++    1 % -2;
++    (1 - 2) % (1 + 2);
++    (1 + 2) % (1 - 2);
++    35 * (7 - 4 * 2) % (-500 * -600);
++
++    -1i8 % 2i8;
++    1i8 % -2i8;
++    -1i16 % 2i16;
++    1i16 % -2i16;
++    -1i32 % 2i32;
++    1i32 % -2i32;
++    -1i64 % 2i64;
++    1i64 % -2i64;
++    -1i128 % 2i128;
++    1i128 % -2i128;
++    -1isize % 2isize;
++    1isize % -2isize;
++
++    // No lint when both sides are const and of the same sign
++    1 % 2;
++    -1 % -2;
++    (1 + 2) % (-1 + 2);
++    (-1 - 2) % (1 - 2);
++
++    1u8 % 2u8;
++    1u16 % 2u16;
++    1u32 % 2u32;
++    1u64 % 2u64;
++    1u128 % 2u128;
++    1usize % 2usize;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..de328bb75fe9160e7811f6f2f26a72e9333297c9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,156 @@@
++error: you are using modulo operator on constants with different signs: `-1 % 2`
++  --> $DIR/modulo_arithmetic_integral_const.rs:13:5
++   |
++LL |     -1 % 2;
++   |     ^^^^^^
++   |
++   = note: `-D clippy::modulo-arithmetic` implied by `-D warnings`
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `1 % -2`
++  --> $DIR/modulo_arithmetic_integral_const.rs:14:5
++   |
++LL |     1 % -2;
++   |     ^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `-1 % 3`
++  --> $DIR/modulo_arithmetic_integral_const.rs:15:5
++   |
++LL |     (1 - 2) % (1 + 2);
++   |     ^^^^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `3 % -1`
++  --> $DIR/modulo_arithmetic_integral_const.rs:16:5
++   |
++LL |     (1 + 2) % (1 - 2);
++   |     ^^^^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `-35 % 300000`
++  --> $DIR/modulo_arithmetic_integral_const.rs:17:5
++   |
++LL |     35 * (7 - 4 * 2) % (-500 * -600);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `-1 % 2`
++  --> $DIR/modulo_arithmetic_integral_const.rs:19:5
++   |
++LL |     -1i8 % 2i8;
++   |     ^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `1 % -2`
++  --> $DIR/modulo_arithmetic_integral_const.rs:20:5
++   |
++LL |     1i8 % -2i8;
++   |     ^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `-1 % 2`
++  --> $DIR/modulo_arithmetic_integral_const.rs:21:5
++   |
++LL |     -1i16 % 2i16;
++   |     ^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `1 % -2`
++  --> $DIR/modulo_arithmetic_integral_const.rs:22:5
++   |
++LL |     1i16 % -2i16;
++   |     ^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `-1 % 2`
++  --> $DIR/modulo_arithmetic_integral_const.rs:23:5
++   |
++LL |     -1i32 % 2i32;
++   |     ^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `1 % -2`
++  --> $DIR/modulo_arithmetic_integral_const.rs:24:5
++   |
++LL |     1i32 % -2i32;
++   |     ^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `-1 % 2`
++  --> $DIR/modulo_arithmetic_integral_const.rs:25:5
++   |
++LL |     -1i64 % 2i64;
++   |     ^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `1 % -2`
++  --> $DIR/modulo_arithmetic_integral_const.rs:26:5
++   |
++LL |     1i64 % -2i64;
++   |     ^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `-1 % 2`
++  --> $DIR/modulo_arithmetic_integral_const.rs:27:5
++   |
++LL |     -1i128 % 2i128;
++   |     ^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `1 % -2`
++  --> $DIR/modulo_arithmetic_integral_const.rs:28:5
++   |
++LL |     1i128 % -2i128;
++   |     ^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `-1 % 2`
++  --> $DIR/modulo_arithmetic_integral_const.rs:29:5
++   |
++LL |     -1isize % 2isize;
++   |     ^^^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: you are using modulo operator on constants with different signs: `1 % -2`
++  --> $DIR/modulo_arithmetic_integral_const.rs:30:5
++   |
++LL |     1isize % -2isize;
++   |     ^^^^^^^^^^^^^^^^
++   |
++   = note: double check for expected result especially when interoperating with different languages
++   = note: or consider using `rem_euclid` or similar function
++
++error: aborting due to 17 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cc8c8e7cdaefd5a203b3e9839fe1db98dfdc9fc8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++#![warn(clippy::modulo_one)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation)]
++
++static STATIC_ONE: usize = 2 - 1;
++
++fn main() {
++    10 % 1;
++    10 % 2;
++
++    const ONE: u32 = 1 * 1;
++
++    2 % ONE;
++    5 % STATIC_ONE;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6bee68360b6fbde118d04883faa81f756cbd78a5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++error: any number modulo 1 will be 0
++  --> $DIR/modulo_one.rs:7:5
++   |
++LL |     10 % 1;
++   |     ^^^^^^
++   |
++   = note: `-D clippy::modulo-one` implied by `-D warnings`
++
++error: the operation is ineffective. Consider reducing it to `1`
++  --> $DIR/modulo_one.rs:10:22
++   |
++LL |     const ONE: u32 = 1 * 1;
++   |                      ^^^^^
++   |
++   = note: `-D clippy::identity-op` implied by `-D warnings`
++
++error: the operation is ineffective. Consider reducing it to `1`
++  --> $DIR/modulo_one.rs:10:22
++   |
++LL |     const ONE: u32 = 1 * 1;
++   |                      ^^^^^
++
++error: any number modulo 1 will be 0
++  --> $DIR/modulo_one.rs:12:5
++   |
++LL |     2 % ONE;
++   |     ^^^^^^^
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9556f6f82cc6313d39125487819635ce0b0eed7f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,93 @@@
++// run-rustfix
++#![feature(never_type)]
++#![allow(unused_mut, clippy::redundant_allocation)]
++#![warn(clippy::must_use_candidate)]
++use std::rc::Rc;
++use std::sync::atomic::{AtomicBool, Ordering};
++use std::sync::Arc;
++
++pub struct MyAtomic(AtomicBool);
++pub struct MyPure;
++
++#[must_use] pub fn pure(i: u8) -> u8 {
++    i
++}
++
++impl MyPure {
++    #[must_use] pub fn inherent_pure(&self) -> u8 {
++        0
++    }
++}
++
++pub trait MyPureTrait {
++    fn trait_pure(&self, i: u32) -> u32 {
++        self.trait_impl_pure(i) + 1
++    }
++
++    fn trait_impl_pure(&self, i: u32) -> u32;
++}
++
++impl MyPureTrait for MyPure {
++    fn trait_impl_pure(&self, i: u32) -> u32 {
++        i
++    }
++}
++
++pub fn without_result() {
++    // OK
++}
++
++pub fn impure_primitive(i: &mut u8) -> u8 {
++    *i
++}
++
++pub fn with_callback<F: Fn(u32) -> bool>(f: &F) -> bool {
++    f(0)
++}
++
++#[must_use] pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool {
++    true
++}
++
++pub fn quoth_the_raven(_more: !) -> u32 {
++    unimplemented!();
++}
++
++pub fn atomics(b: &AtomicBool) -> bool {
++    b.load(Ordering::SeqCst)
++}
++
++#[must_use] pub fn rcd(_x: Rc<u32>) -> bool {
++    true
++}
++
++pub fn rcmut(_x: Rc<&mut u32>) -> bool {
++    true
++}
++
++#[must_use] pub fn arcd(_x: Arc<u32>) -> bool {
++    false
++}
++
++pub fn inner_types(_m: &MyAtomic) -> bool {
++    true
++}
++
++static mut COUNTER: usize = 0;
++
++/// # Safety
++///
++/// Don't ever call this from multiple threads
++pub unsafe fn mutates_static() -> usize {
++    COUNTER += 1;
++    COUNTER
++}
++
++#[no_mangle]
++pub fn unmangled(i: bool) -> bool {
++    !i
++}
++
++fn main() {
++    assert_eq!(1, pure(1));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3732422017104ccaf788e747de9c07ac99eb758a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,93 @@@
++// run-rustfix
++#![feature(never_type)]
++#![allow(unused_mut, clippy::redundant_allocation)]
++#![warn(clippy::must_use_candidate)]
++use std::rc::Rc;
++use std::sync::atomic::{AtomicBool, Ordering};
++use std::sync::Arc;
++
++pub struct MyAtomic(AtomicBool);
++pub struct MyPure;
++
++pub fn pure(i: u8) -> u8 {
++    i
++}
++
++impl MyPure {
++    pub fn inherent_pure(&self) -> u8 {
++        0
++    }
++}
++
++pub trait MyPureTrait {
++    fn trait_pure(&self, i: u32) -> u32 {
++        self.trait_impl_pure(i) + 1
++    }
++
++    fn trait_impl_pure(&self, i: u32) -> u32;
++}
++
++impl MyPureTrait for MyPure {
++    fn trait_impl_pure(&self, i: u32) -> u32 {
++        i
++    }
++}
++
++pub fn without_result() {
++    // OK
++}
++
++pub fn impure_primitive(i: &mut u8) -> u8 {
++    *i
++}
++
++pub fn with_callback<F: Fn(u32) -> bool>(f: &F) -> bool {
++    f(0)
++}
++
++pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool {
++    true
++}
++
++pub fn quoth_the_raven(_more: !) -> u32 {
++    unimplemented!();
++}
++
++pub fn atomics(b: &AtomicBool) -> bool {
++    b.load(Ordering::SeqCst)
++}
++
++pub fn rcd(_x: Rc<u32>) -> bool {
++    true
++}
++
++pub fn rcmut(_x: Rc<&mut u32>) -> bool {
++    true
++}
++
++pub fn arcd(_x: Arc<u32>) -> bool {
++    false
++}
++
++pub fn inner_types(_m: &MyAtomic) -> bool {
++    true
++}
++
++static mut COUNTER: usize = 0;
++
++/// # Safety
++///
++/// Don't ever call this from multiple threads
++pub unsafe fn mutates_static() -> usize {
++    COUNTER += 1;
++    COUNTER
++}
++
++#[no_mangle]
++pub fn unmangled(i: bool) -> bool {
++    !i
++}
++
++fn main() {
++    assert_eq!(1, pure(1));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0fa3849d03bff662169944e0758e018895355b06
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++error: this function could have a `#[must_use]` attribute
++  --> $DIR/must_use_candidates.rs:12:1
++   |
++LL | pub fn pure(i: u8) -> u8 {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn pure(i: u8) -> u8`
++   |
++   = note: `-D clippy::must-use-candidate` implied by `-D warnings`
++
++error: this method could have a `#[must_use]` attribute
++  --> $DIR/must_use_candidates.rs:17:5
++   |
++LL |     pub fn inherent_pure(&self) -> u8 {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn inherent_pure(&self) -> u8`
++
++error: this function could have a `#[must_use]` attribute
++  --> $DIR/must_use_candidates.rs:48:1
++   |
++LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool`
++
++error: this function could have a `#[must_use]` attribute
++  --> $DIR/must_use_candidates.rs:60:1
++   |
++LL | pub fn rcd(_x: Rc<u32>) -> bool {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn rcd(_x: Rc<u32>) -> bool`
++
++error: this function could have a `#[must_use]` attribute
++  --> $DIR/must_use_candidates.rs:68:1
++   |
++LL | pub fn arcd(_x: Arc<u32>) -> bool {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn arcd(_x: Arc<u32>) -> bool`
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6c9aa434ac016f5eab268dacec51ff8ca0b0ca88
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++//run-rustfix
++// aux-build:macro_rules.rs
++
++#![warn(clippy::must_use_unit)]
++#![allow(clippy::unused_unit)]
++
++#[macro_use]
++extern crate macro_rules;
++
++
++pub fn must_use_default() {}
++
++
++pub fn must_use_unit() -> () {}
++
++
++pub fn must_use_with_note() {}
++
++fn main() {
++    must_use_default();
++    must_use_unit();
++    must_use_with_note();
++
++    // We should not lint in external macros
++    must_use_unit!();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8a395dc284db4f38b2c7c4e2002b1a9704f942a5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++//run-rustfix
++// aux-build:macro_rules.rs
++
++#![warn(clippy::must_use_unit)]
++#![allow(clippy::unused_unit)]
++
++#[macro_use]
++extern crate macro_rules;
++
++#[must_use]
++pub fn must_use_default() {}
++
++#[must_use]
++pub fn must_use_unit() -> () {}
++
++#[must_use = "With note"]
++pub fn must_use_with_note() {}
++
++fn main() {
++    must_use_default();
++    must_use_unit();
++    must_use_with_note();
++
++    // We should not lint in external macros
++    must_use_unit!();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..15e0906b66b5ec986eaa5a7df700401c4b54e4b8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: this unit-returning function has a `#[must_use]` attribute
++  --> $DIR/must_use_unit.rs:11:1
++   |
++LL | #[must_use]
++   | ----------- help: remove the attribute
++LL | pub fn must_use_default() {}
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::must-use-unit` implied by `-D warnings`
++
++error: this unit-returning function has a `#[must_use]` attribute
++  --> $DIR/must_use_unit.rs:14:1
++   |
++LL | #[must_use]
++   | ----------- help: remove the attribute
++LL | pub fn must_use_unit() -> () {}
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this unit-returning function has a `#[must_use]` attribute
++  --> $DIR/must_use_unit.rs:17:1
++   |
++LL | #[must_use = "With note"]
++   | ------------------------- help: remove the attribute
++LL | pub fn must_use_with_note() {}
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a9a04c8f56b945ca7c0e94b35a211119f7799a2d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++#![allow(unused)]
++#![warn(clippy::mut_from_ref)]
++
++struct Foo;
++
++impl Foo {
++    fn this_wont_hurt_a_bit(&self) -> &mut Foo {
++        unimplemented!()
++    }
++}
++
++trait Ouch {
++    fn ouch(x: &Foo) -> &mut Foo;
++}
++
++impl Ouch for Foo {
++    fn ouch(x: &Foo) -> &mut Foo {
++        unimplemented!()
++    }
++}
++
++fn fail(x: &u32) -> &mut u16 {
++    unimplemented!()
++}
++
++fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 {
++    unimplemented!()
++}
++
++fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
++    unimplemented!()
++}
++
++// this is OK, because the result borrows y
++fn works<'a>(x: &u32, y: &'a mut u32) -> &'a mut u32 {
++    unimplemented!()
++}
++
++// this is also OK, because the result could borrow y
++fn also_works<'a>(x: &'a u32, y: &'a mut u32) -> &'a mut u32 {
++    unimplemented!()
++}
++
++fn main() {
++    //TODO
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4787999920bc219fde94225fd530ba3fba2c3253
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++error: mutable borrow from immutable input(s)
++  --> $DIR/mut_from_ref.rs:7:39
++   |
++LL |     fn this_wont_hurt_a_bit(&self) -> &mut Foo {
++   |                                       ^^^^^^^^
++   |
++   = note: `-D clippy::mut-from-ref` implied by `-D warnings`
++note: immutable borrow here
++  --> $DIR/mut_from_ref.rs:7:29
++   |
++LL |     fn this_wont_hurt_a_bit(&self) -> &mut Foo {
++   |                             ^^^^^
++
++error: mutable borrow from immutable input(s)
++  --> $DIR/mut_from_ref.rs:13:25
++   |
++LL |     fn ouch(x: &Foo) -> &mut Foo;
++   |                         ^^^^^^^^
++   |
++note: immutable borrow here
++  --> $DIR/mut_from_ref.rs:13:16
++   |
++LL |     fn ouch(x: &Foo) -> &mut Foo;
++   |                ^^^^
++
++error: mutable borrow from immutable input(s)
++  --> $DIR/mut_from_ref.rs:22:21
++   |
++LL | fn fail(x: &u32) -> &mut u16 {
++   |                     ^^^^^^^^
++   |
++note: immutable borrow here
++  --> $DIR/mut_from_ref.rs:22:12
++   |
++LL | fn fail(x: &u32) -> &mut u16 {
++   |            ^^^^
++
++error: mutable borrow from immutable input(s)
++  --> $DIR/mut_from_ref.rs:26:50
++   |
++LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 {
++   |                                                  ^^^^^^^^^^^
++   |
++note: immutable borrow here
++  --> $DIR/mut_from_ref.rs:26:25
++   |
++LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 {
++   |                         ^^^^^^^
++
++error: mutable borrow from immutable input(s)
++  --> $DIR/mut_from_ref.rs:30:67
++   |
++LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
++   |                                                                   ^^^^^^^^^^^
++   |
++note: immutable borrow here
++  --> $DIR/mut_from_ref.rs:30:27
++   |
++LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
++   |                           ^^^^^^^     ^^^^^^^
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2d227e6654c36c0f45e6801205dd173f271d546a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++use std::collections::{HashMap, HashSet};
++use std::hash::{Hash, Hasher};
++use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
++
++struct Key(AtomicUsize);
++
++impl Clone for Key {
++    fn clone(&self) -> Self {
++        Key(AtomicUsize::new(self.0.load(Relaxed)))
++    }
++}
++
++impl PartialEq for Key {
++    fn eq(&self, other: &Self) -> bool {
++        self.0.load(Relaxed) == other.0.load(Relaxed)
++    }
++}
++
++impl Eq for Key {}
++
++impl Hash for Key {
++    fn hash<H: Hasher>(&self, h: &mut H) {
++        self.0.load(Relaxed).hash(h);
++    }
++}
++
++fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
++    let _other: HashMap<Key, bool> = HashMap::new();
++    m.keys().cloned().collect()
++}
++
++fn this_is_ok(_m: &mut HashMap<usize, Key>) {}
++
++#[allow(unused)]
++trait Trait {
++    type AssociatedType;
++
++    fn trait_fn(&self, set: std::collections::HashSet<Self::AssociatedType>);
++}
++
++fn generics_are_ok_too<K>(_m: &mut HashSet<K>) {
++    // nothing to see here, move along
++}
++
++fn tuples<U>(_m: &mut HashMap<((), U), ()>) {}
++
++fn tuples_bad<U>(_m: &mut HashMap<(Key, U), bool>) {}
++
++fn main() {
++    let _ = should_not_take_this_arg(&mut HashMap::new(), 1);
++    this_is_ok(&mut HashMap::new());
++    tuples::<Key>(&mut HashMap::new());
++    tuples::<()>(&mut HashMap::new());
++    tuples_bad::<()>(&mut HashMap::new());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8d6a259c7e385f307ada794ea2e9ca0f06f01853
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: mutable key type
++  --> $DIR/mut_key.rs:27:32
++   |
++LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
++   |                                ^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `#[deny(clippy::mutable_key_type)]` on by default
++
++error: mutable key type
++  --> $DIR/mut_key.rs:27:72
++   |
++LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
++   |                                                                        ^^^^^^^^^^^^
++
++error: mutable key type
++  --> $DIR/mut_key.rs:28:5
++   |
++LL |     let _other: HashMap<Key, bool> = HashMap::new();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: mutable key type
++  --> $DIR/mut_key.rs:47:22
++   |
++LL | fn tuples_bad<U>(_m: &mut HashMap<(Key, U), bool>) {}
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8965cef66deddc899b5e1779a7f8814ebf61849d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,49 @@@
++#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
++#![warn(clippy::mut_mut)]
++
++fn fun(x: &mut &mut u32) -> bool {
++    **x > 0
++}
++
++fn less_fun(x: *mut *mut u32) {
++    let y = x;
++}
++
++macro_rules! mut_ptr {
++    ($p:expr) => {
++        &mut $p
++    };
++}
++
++#[allow(unused_mut, unused_variables)]
++fn main() {
++    let mut x = &mut &mut 1u32;
++    {
++        let mut y = &mut x;
++    }
++
++    if fun(x) {
++        let y: &mut &mut u32 = &mut &mut 2;
++        **y + **x;
++    }
++
++    if fun(x) {
++        let y: &mut &mut &mut u32 = &mut &mut &mut 2;
++        ***y + **x;
++    }
++
++    let mut z = mut_ptr!(&mut 3u32);
++}
++
++fn issue939() {
++    let array = [5, 6, 7, 8, 9];
++    let mut args = array.iter().skip(2);
++    for &arg in &mut args {
++        println!("{}", arg);
++    }
++
++    let args = &mut args;
++    for arg in args {
++        println!(":{}", arg);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..44e81422714181250edb7b3338c7d4cf3e7fe7c8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++error: generally you want to avoid `&mut &mut _` if possible
++  --> $DIR/mut_mut.rs:4:11
++   |
++LL | fn fun(x: &mut &mut u32) -> bool {
++   |           ^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::mut-mut` implied by `-D warnings`
++
++error: generally you want to avoid `&mut &mut _` if possible
++  --> $DIR/mut_mut.rs:20:17
++   |
++LL |     let mut x = &mut &mut 1u32;
++   |                 ^^^^^^^^^^^^^^
++
++error: generally you want to avoid `&mut &mut _` if possible
++  --> $DIR/mut_mut.rs:14:9
++   |
++LL |         &mut $p
++   |         ^^^^^^^
++...
++LL |     let mut z = mut_ptr!(&mut 3u32);
++   |                 ------------------- in this macro invocation
++   |
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: this expression mutably borrows a mutable reference. Consider reborrowing
++  --> $DIR/mut_mut.rs:22:21
++   |
++LL |         let mut y = &mut x;
++   |                     ^^^^^^
++
++error: generally you want to avoid `&mut &mut _` if possible
++  --> $DIR/mut_mut.rs:26:32
++   |
++LL |         let y: &mut &mut u32 = &mut &mut 2;
++   |                                ^^^^^^^^^^^
++
++error: generally you want to avoid `&mut &mut _` if possible
++  --> $DIR/mut_mut.rs:26:16
++   |
++LL |         let y: &mut &mut u32 = &mut &mut 2;
++   |                ^^^^^^^^^^^^^
++
++error: generally you want to avoid `&mut &mut _` if possible
++  --> $DIR/mut_mut.rs:31:37
++   |
++LL |         let y: &mut &mut &mut u32 = &mut &mut &mut 2;
++   |                                     ^^^^^^^^^^^^^^^^
++
++error: generally you want to avoid `&mut &mut _` if possible
++  --> $DIR/mut_mut.rs:31:16
++   |
++LL |         let y: &mut &mut &mut u32 = &mut &mut &mut 2;
++   |                ^^^^^^^^^^^^^^^^^^
++
++error: generally you want to avoid `&mut &mut _` if possible
++  --> $DIR/mut_mut.rs:31:21
++   |
++LL |         let y: &mut &mut &mut u32 = &mut &mut &mut 2;
++   |                     ^^^^^^^^^^^^^
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1348dd2a3d8bb87a12c6a3af0008c6ae517cb93c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++#![allow(unused)]
++
++fn main() {
++    mut_range_bound_upper();
++    mut_range_bound_lower();
++    mut_range_bound_both();
++    mut_range_bound_no_mutation();
++    immut_range_bound();
++    mut_borrow_range_bound();
++    immut_borrow_range_bound();
++}
++
++fn mut_range_bound_upper() {
++    let mut m = 4;
++    for i in 0..m {
++        m = 5;
++    } // warning
++}
++
++fn mut_range_bound_lower() {
++    let mut m = 4;
++    for i in m..10 {
++        m *= 2;
++    } // warning
++}
++
++fn mut_range_bound_both() {
++    let mut m = 4;
++    let mut n = 6;
++    for i in m..n {
++        m = 5;
++        n = 7;
++    } // warning (1 for each mutated bound)
++}
++
++fn mut_range_bound_no_mutation() {
++    let mut m = 4;
++    for i in 0..m {
++        continue;
++    } // no warning
++}
++
++fn mut_borrow_range_bound() {
++    let mut m = 4;
++    for i in 0..m {
++        let n = &mut m; // warning
++        *n += 1;
++    }
++}
++
++fn immut_borrow_range_bound() {
++    let mut m = 4;
++    for i in 0..m {
++        let n = &m; // should be no warning?
++    }
++}
++
++fn immut_range_bound() {
++    let m = 4;
++    for i in 0..m {
++        continue;
++    } // no warning
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0eeb76e0ec5fd713656843aa48b6000fdd98803f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
++  --> $DIR/mut_range_bound.rs:16:9
++   |
++LL |         m = 5;
++   |         ^
++   |
++   = note: `-D clippy::mut-range-bound` implied by `-D warnings`
++
++error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
++  --> $DIR/mut_range_bound.rs:23:9
++   |
++LL |         m *= 2;
++   |         ^
++
++error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
++  --> $DIR/mut_range_bound.rs:31:9
++   |
++LL |         m = 5;
++   |         ^
++
++error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
++  --> $DIR/mut_range_bound.rs:32:9
++   |
++LL |         n = 7;
++   |         ^
++
++error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
++  --> $DIR/mut_range_bound.rs:46:22
++   |
++LL |         let n = &mut m; // warning
++   |                      ^
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..73906121c402efba40d6f13c9cc696d7f669c628
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++#![allow(unused_variables)]
++
++fn takes_an_immutable_reference(a: &i32) {}
++fn takes_a_mutable_reference(a: &mut i32) {}
++
++struct MyStruct;
++
++impl MyStruct {
++    fn takes_an_immutable_reference(&self, a: &i32) {}
++
++    fn takes_a_mutable_reference(&self, a: &mut i32) {}
++}
++
++#[warn(clippy::unnecessary_mut_passed)]
++fn main() {
++    // Functions
++    takes_an_immutable_reference(&mut 42);
++    let as_ptr: fn(&i32) = takes_an_immutable_reference;
++    as_ptr(&mut 42);
++
++    // Methods
++    let my_struct = MyStruct;
++    my_struct.takes_an_immutable_reference(&mut 42);
++
++    // No error
++
++    // Functions
++    takes_an_immutable_reference(&42);
++    let as_ptr: fn(&i32) = takes_an_immutable_reference;
++    as_ptr(&42);
++
++    takes_a_mutable_reference(&mut 42);
++    let as_ptr: fn(&mut i32) = takes_a_mutable_reference;
++    as_ptr(&mut 42);
++
++    let a = &mut 42;
++    takes_an_immutable_reference(a);
++
++    // Methods
++    my_struct.takes_an_immutable_reference(&42);
++    my_struct.takes_a_mutable_reference(&mut 42);
++    my_struct.takes_an_immutable_reference(a);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fa8c82ae0f34022fbc212f7ed8e60622e7bf74d5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference
++  --> $DIR/mut_reference.rs:17:34
++   |
++LL |     takes_an_immutable_reference(&mut 42);
++   |                                  ^^^^^^^
++   |
++   = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings`
++
++error: The function/method `as_ptr` doesn't need a mutable reference
++  --> $DIR/mut_reference.rs:19:12
++   |
++LL |     as_ptr(&mut 42);
++   |            ^^^^^^^
++
++error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference
++  --> $DIR/mut_reference.rs:23:44
++   |
++LL |     my_struct.takes_an_immutable_reference(&mut 42);
++   |                                            ^^^^^^^
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b9d78b7f47924840a10793dbd7b1df58ea759fd9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++#![warn(clippy::all)]
++#![warn(clippy::mutex_integer)]
++
++fn main() {
++    use std::sync::Mutex;
++    Mutex::new(true);
++    Mutex::new(5usize);
++    Mutex::new(9isize);
++    let mut x = 4u32;
++    Mutex::new(&x as *const u32);
++    Mutex::new(&mut x as *mut u32);
++    Mutex::new(0u32);
++    Mutex::new(0i32);
++    Mutex::new(0f32); // there are no float atomics, so this should not lint
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7dac086585548431a9c8b0cbd66a6376fecd504c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,48 @@@
++error: Consider using an `AtomicBool` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
++  --> $DIR/mutex_atomic.rs:6:5
++   |
++LL |     Mutex::new(true);
++   |     ^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::mutex-atomic` implied by `-D warnings`
++
++error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
++  --> $DIR/mutex_atomic.rs:7:5
++   |
++LL |     Mutex::new(5usize);
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
++  --> $DIR/mutex_atomic.rs:8:5
++   |
++LL |     Mutex::new(9isize);
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
++  --> $DIR/mutex_atomic.rs:10:5
++   |
++LL |     Mutex::new(&x as *const u32);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
++  --> $DIR/mutex_atomic.rs:11:5
++   |
++LL |     Mutex::new(&mut x as *mut u32);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
++  --> $DIR/mutex_atomic.rs:12:5
++   |
++LL |     Mutex::new(0u32);
++   |     ^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::mutex-integer` implied by `-D warnings`
++
++error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
++  --> $DIR/mutex_atomic.rs:13:5
++   |
++LL |     Mutex::new(0i32);
++   |     ^^^^^^^^^^^^^^^^
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..567dbc54100a64943337b9c02c390dd7801f7804
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,98 @@@
++// run-rustfix
++
++#![warn(clippy::needless_bool)]
++#![allow(
++    unused,
++    dead_code,
++    clippy::no_effect,
++    clippy::if_same_then_else,
++    clippy::needless_return
++)]
++
++use std::cell::Cell;
++
++macro_rules! bool_comparison_trigger {
++    ($($i:ident: $def:expr, $stb:expr );+  $(;)*) => (
++
++        #[derive(Clone)]
++        pub struct Trigger {
++            $($i: (Cell<bool>, bool, bool)),+
++        }
++
++        #[allow(dead_code)]
++        impl Trigger {
++            pub fn trigger(&self, key: &str) -> bool {
++                $(
++                    if let stringify!($i) = key {
++                        return self.$i.1 && self.$i.2 == $def;
++                    }
++                 )+
++                false
++            }
++        }
++    )
++}
++
++fn main() {
++    let x = true;
++    let y = false;
++    x;
++    !x;
++    !(x && y);
++    if x {
++        x
++    } else {
++        false
++    }; // would also be questionable, but we don't catch this yet
++    bool_ret3(x);
++    bool_ret4(x);
++    bool_ret5(x, x);
++    bool_ret6(x, x);
++    needless_bool(x);
++    needless_bool2(x);
++    needless_bool3(x);
++}
++
++fn bool_ret3(x: bool) -> bool {
++    return x;
++}
++
++fn bool_ret4(x: bool) -> bool {
++    return !x;
++}
++
++fn bool_ret5(x: bool, y: bool) -> bool {
++    return x && y;
++}
++
++fn bool_ret6(x: bool, y: bool) -> bool {
++    return !(x && y);
++}
++
++fn needless_bool(x: bool) {
++    if x {};
++}
++
++fn needless_bool2(x: bool) {
++    if !x {};
++}
++
++fn needless_bool3(x: bool) {
++    bool_comparison_trigger! {
++        test_one:   false, false;
++        test_three: false, false;
++        test_two:   true, true;
++    }
++
++    if x {};
++    if !x {};
++}
++
++fn needless_bool_in_the_suggestion_wraps_the_predicate_of_if_else_statement_in_brackets() {
++    let b = false;
++    let returns_bool = || false;
++
++    let x = if b {
++        true
++    } else { !returns_bool() };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..10126ad4dbb15aacd70053938e9e267dd42510ba
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,130 @@@
++// run-rustfix
++
++#![warn(clippy::needless_bool)]
++#![allow(
++    unused,
++    dead_code,
++    clippy::no_effect,
++    clippy::if_same_then_else,
++    clippy::needless_return
++)]
++
++use std::cell::Cell;
++
++macro_rules! bool_comparison_trigger {
++    ($($i:ident: $def:expr, $stb:expr );+  $(;)*) => (
++
++        #[derive(Clone)]
++        pub struct Trigger {
++            $($i: (Cell<bool>, bool, bool)),+
++        }
++
++        #[allow(dead_code)]
++        impl Trigger {
++            pub fn trigger(&self, key: &str) -> bool {
++                $(
++                    if let stringify!($i) = key {
++                        return self.$i.1 && self.$i.2 == $def;
++                    }
++                 )+
++                false
++            }
++        }
++    )
++}
++
++fn main() {
++    let x = true;
++    let y = false;
++    if x {
++        true
++    } else {
++        false
++    };
++    if x {
++        false
++    } else {
++        true
++    };
++    if x && y {
++        false
++    } else {
++        true
++    };
++    if x {
++        x
++    } else {
++        false
++    }; // would also be questionable, but we don't catch this yet
++    bool_ret3(x);
++    bool_ret4(x);
++    bool_ret5(x, x);
++    bool_ret6(x, x);
++    needless_bool(x);
++    needless_bool2(x);
++    needless_bool3(x);
++}
++
++fn bool_ret3(x: bool) -> bool {
++    if x {
++        return true;
++    } else {
++        return false;
++    };
++}
++
++fn bool_ret4(x: bool) -> bool {
++    if x {
++        return false;
++    } else {
++        return true;
++    };
++}
++
++fn bool_ret5(x: bool, y: bool) -> bool {
++    if x && y {
++        return true;
++    } else {
++        return false;
++    };
++}
++
++fn bool_ret6(x: bool, y: bool) -> bool {
++    if x && y {
++        return false;
++    } else {
++        return true;
++    };
++}
++
++fn needless_bool(x: bool) {
++    if x == true {};
++}
++
++fn needless_bool2(x: bool) {
++    if x == false {};
++}
++
++fn needless_bool3(x: bool) {
++    bool_comparison_trigger! {
++        test_one:   false, false;
++        test_three: false, false;
++        test_two:   true, true;
++    }
++
++    if x == true {};
++    if x == false {};
++}
++
++fn needless_bool_in_the_suggestion_wraps_the_predicate_of_if_else_statement_in_brackets() {
++    let b = false;
++    let returns_bool = || false;
++
++    let x = if b {
++        true
++    } else if returns_bool() {
++        false
++    } else {
++        true
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..25abfb2a472b621130483c7eb5629ef7f946fab1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,111 @@@
++error: this if-then-else expression returns a bool literal
++  --> $DIR/fixable.rs:39:5
++   |
++LL | /     if x {
++LL | |         true
++LL | |     } else {
++LL | |         false
++LL | |     };
++   | |_____^ help: you can reduce it to: `x`
++   |
++   = note: `-D clippy::needless-bool` implied by `-D warnings`
++
++error: this if-then-else expression returns a bool literal
++  --> $DIR/fixable.rs:44:5
++   |
++LL | /     if x {
++LL | |         false
++LL | |     } else {
++LL | |         true
++LL | |     };
++   | |_____^ help: you can reduce it to: `!x`
++
++error: this if-then-else expression returns a bool literal
++  --> $DIR/fixable.rs:49:5
++   |
++LL | /     if x && y {
++LL | |         false
++LL | |     } else {
++LL | |         true
++LL | |     };
++   | |_____^ help: you can reduce it to: `!(x && y)`
++
++error: this if-then-else expression returns a bool literal
++  --> $DIR/fixable.rs:69:5
++   |
++LL | /     if x {
++LL | |         return true;
++LL | |     } else {
++LL | |         return false;
++LL | |     };
++   | |_____^ help: you can reduce it to: `return x`
++
++error: this if-then-else expression returns a bool literal
++  --> $DIR/fixable.rs:77:5
++   |
++LL | /     if x {
++LL | |         return false;
++LL | |     } else {
++LL | |         return true;
++LL | |     };
++   | |_____^ help: you can reduce it to: `return !x`
++
++error: this if-then-else expression returns a bool literal
++  --> $DIR/fixable.rs:85:5
++   |
++LL | /     if x && y {
++LL | |         return true;
++LL | |     } else {
++LL | |         return false;
++LL | |     };
++   | |_____^ help: you can reduce it to: `return x && y`
++
++error: this if-then-else expression returns a bool literal
++  --> $DIR/fixable.rs:93:5
++   |
++LL | /     if x && y {
++LL | |         return false;
++LL | |     } else {
++LL | |         return true;
++LL | |     };
++   | |_____^ help: you can reduce it to: `return !(x && y)`
++
++error: equality checks against true are unnecessary
++  --> $DIR/fixable.rs:101:8
++   |
++LL |     if x == true {};
++   |        ^^^^^^^^^ help: try simplifying it as shown: `x`
++   |
++   = note: `-D clippy::bool-comparison` implied by `-D warnings`
++
++error: equality checks against false can be replaced by a negation
++  --> $DIR/fixable.rs:105:8
++   |
++LL |     if x == false {};
++   |        ^^^^^^^^^^ help: try simplifying it as shown: `!x`
++
++error: equality checks against true are unnecessary
++  --> $DIR/fixable.rs:115:8
++   |
++LL |     if x == true {};
++   |        ^^^^^^^^^ help: try simplifying it as shown: `x`
++
++error: equality checks against false can be replaced by a negation
++  --> $DIR/fixable.rs:116:8
++   |
++LL |     if x == false {};
++   |        ^^^^^^^^^^ help: try simplifying it as shown: `!x`
++
++error: this if-then-else expression returns a bool literal
++  --> $DIR/fixable.rs:125:12
++   |
++LL |       } else if returns_bool() {
++   |  ____________^
++LL | |         false
++LL | |     } else {
++LL | |         true
++LL | |     };
++   | |_____^ help: you can reduce it to: `{ !returns_bool() }`
++
++error: aborting due to 12 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e9f1428fc3a43b5ff407062cbfe9639fefaef47a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++#![warn(clippy::needless_bool)]
++#![allow(
++    unused,
++    dead_code,
++    clippy::no_effect,
++    clippy::if_same_then_else,
++    clippy::needless_return
++)]
++
++fn main() {
++    let x = true;
++    let y = false;
++    if x {
++        true
++    } else {
++        true
++    };
++    if x {
++        false
++    } else {
++        false
++    };
++    if x {
++        x
++    } else {
++        false
++    }; // would also be questionable, but we don't catch this yet
++    bool_ret(x);
++    bool_ret2(x);
++}
++
++fn bool_ret(x: bool) -> bool {
++    if x {
++        return true;
++    } else {
++        return true;
++    };
++}
++
++fn bool_ret2(x: bool) -> bool {
++    if x {
++        return false;
++    } else {
++        return false;
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c57a8a042fb88f95bf2374332034e9d4b5c26591
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++error: this if-then-else expression will always return true
++  --> $DIR/simple.rs:13:5
++   |
++LL | /     if x {
++LL | |         true
++LL | |     } else {
++LL | |         true
++LL | |     };
++   | |_____^
++   |
++   = note: `-D clippy::needless-bool` implied by `-D warnings`
++
++error: this if-then-else expression will always return false
++  --> $DIR/simple.rs:18:5
++   |
++LL | /     if x {
++LL | |         false
++LL | |     } else {
++LL | |         false
++LL | |     };
++   | |_____^
++
++error: this if-then-else expression will always return true
++  --> $DIR/simple.rs:33:5
++   |
++LL | /     if x {
++LL | |         return true;
++LL | |     } else {
++LL | |         return true;
++LL | |     };
++   | |_____^
++
++error: this if-then-else expression will always return false
++  --> $DIR/simple.rs:41:5
++   |
++LL | /     if x {
++LL | |         return false;
++LL | |     } else {
++LL | |         return false;
++LL | |     };
++   | |_____^
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5ae4a0e79b99d39a9ca852111c09ed7ed313fa51
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++// run-rustfix
++
++#![allow(clippy::needless_borrowed_reference)]
++
++fn x(y: &i32) -> i32 {
++    *y
++}
++
++#[warn(clippy::all, clippy::needless_borrow)]
++#[allow(unused_variables)]
++fn main() {
++    let a = 5;
++    let b = x(&a);
++    let c = x(&a);
++    let s = &String::from("hi");
++    let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
++    let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
++    let vec = Vec::new();
++    let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
++    h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
++    if let Some(cake) = Some(&5) {}
++    let garbl = match 42 {
++        44 => &a,
++        45 => {
++            println!("foo");
++            &&a // FIXME: this should lint, too
++        },
++        46 => &a,
++        _ => panic!(),
++    };
++}
++
++fn f<T: Copy>(y: &T) -> T {
++    *y
++}
++
++fn g(y: &[u8]) -> u8 {
++    y[0]
++}
++
++trait Trait {}
++
++impl<'a> Trait for &'a str {}
++
++fn h(_: &dyn Trait) {}
++#[warn(clippy::needless_borrow)]
++#[allow(dead_code)]
++fn issue_1432() {
++    let mut v = Vec::<String>::new();
++    let _ = v.iter_mut().filter(|&ref a| a.is_empty());
++    let _ = v.iter().filter(|&a| a.is_empty());
++
++    let _ = v.iter().filter(|&a| a.is_empty());
++}
++
++#[allow(dead_code)]
++#[warn(clippy::needless_borrow)]
++#[derive(Debug)]
++enum Foo<'a> {
++    Str(&'a str),
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1e281316c8a39c10e053d2d529896c93b19b1400
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++// run-rustfix
++
++#![allow(clippy::needless_borrowed_reference)]
++
++fn x(y: &i32) -> i32 {
++    *y
++}
++
++#[warn(clippy::all, clippy::needless_borrow)]
++#[allow(unused_variables)]
++fn main() {
++    let a = 5;
++    let b = x(&a);
++    let c = x(&&a);
++    let s = &String::from("hi");
++    let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
++    let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
++    let vec = Vec::new();
++    let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
++    h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
++    if let Some(ref cake) = Some(&5) {}
++    let garbl = match 42 {
++        44 => &a,
++        45 => {
++            println!("foo");
++            &&a // FIXME: this should lint, too
++        },
++        46 => &&a,
++        _ => panic!(),
++    };
++}
++
++fn f<T: Copy>(y: &T) -> T {
++    *y
++}
++
++fn g(y: &[u8]) -> u8 {
++    y[0]
++}
++
++trait Trait {}
++
++impl<'a> Trait for &'a str {}
++
++fn h(_: &dyn Trait) {}
++#[warn(clippy::needless_borrow)]
++#[allow(dead_code)]
++fn issue_1432() {
++    let mut v = Vec::<String>::new();
++    let _ = v.iter_mut().filter(|&ref a| a.is_empty());
++    let _ = v.iter().filter(|&ref a| a.is_empty());
++
++    let _ = v.iter().filter(|&a| a.is_empty());
++}
++
++#[allow(dead_code)]
++#[warn(clippy::needless_borrow)]
++#[derive(Debug)]
++enum Foo<'a> {
++    Str(&'a str),
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0bfeda7914db70182e39f4eef6d960251d208a8c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: this expression borrows a reference that is immediately dereferenced by the compiler
++  --> $DIR/needless_borrow.rs:14:15
++   |
++LL |     let c = x(&&a);
++   |               ^^^ help: change this to: `&a`
++   |
++   = note: `-D clippy::needless-borrow` implied by `-D warnings`
++
++error: this pattern creates a reference to a reference
++  --> $DIR/needless_borrow.rs:21:17
++   |
++LL |     if let Some(ref cake) = Some(&5) {}
++   |                 ^^^^^^^^ help: change this to: `cake`
++
++error: this expression borrows a reference that is immediately dereferenced by the compiler
++  --> $DIR/needless_borrow.rs:28:15
++   |
++LL |         46 => &&a,
++   |               ^^^ help: change this to: `&a`
++
++error: this pattern creates a reference to a reference
++  --> $DIR/needless_borrow.rs:51:31
++   |
++LL |     let _ = v.iter().filter(|&ref a| a.is_empty());
++   |                               ^^^^^ help: change this to: `a`
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a0937a2c5f62f5d3ffa28e9c2328d311e10a7833
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++// run-rustfix
++
++#[warn(clippy::needless_borrowed_reference)]
++#[allow(unused_variables)]
++fn main() {
++    let mut v = Vec::<String>::new();
++    let _ = v.iter_mut().filter(|a| a.is_empty());
++    //                            ^ should be linted
++
++    let var = 3;
++    let thingy = Some(&var);
++    if let Some(&ref v) = thingy {
++        //          ^ should be linted
++    }
++
++    let mut var2 = 5;
++    let thingy2 = Some(&mut var2);
++    if let Some(&mut ref mut v) = thingy2 {
++        //          ^ should **not** be linted
++        // v is borrowed as mutable.
++        *v = 10;
++    }
++    if let Some(&mut ref v) = thingy2 {
++        //          ^ should **not** be linted
++        // here, v is borrowed as immutable.
++        // can't do that:
++        //*v = 15;
++    }
++}
++
++#[allow(dead_code)]
++enum Animal {
++    Cat(u64),
++    Dog(u64),
++}
++
++#[allow(unused_variables)]
++#[allow(dead_code)]
++fn foo(a: &Animal, b: &Animal) {
++    match (a, b) {
++        (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref'
++        //                  ^    and   ^ should **not** be linted
++        (&Animal::Dog(ref a), &Animal::Dog(_)) => (), //              ^ should **not** be linted
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..500ac448f0d58c4da01ed449101803ba0d702f2f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++// run-rustfix
++
++#[warn(clippy::needless_borrowed_reference)]
++#[allow(unused_variables)]
++fn main() {
++    let mut v = Vec::<String>::new();
++    let _ = v.iter_mut().filter(|&ref a| a.is_empty());
++    //                            ^ should be linted
++
++    let var = 3;
++    let thingy = Some(&var);
++    if let Some(&ref v) = thingy {
++        //          ^ should be linted
++    }
++
++    let mut var2 = 5;
++    let thingy2 = Some(&mut var2);
++    if let Some(&mut ref mut v) = thingy2 {
++        //          ^ should **not** be linted
++        // v is borrowed as mutable.
++        *v = 10;
++    }
++    if let Some(&mut ref v) = thingy2 {
++        //          ^ should **not** be linted
++        // here, v is borrowed as immutable.
++        // can't do that:
++        //*v = 15;
++    }
++}
++
++#[allow(dead_code)]
++enum Animal {
++    Cat(u64),
++    Dog(u64),
++}
++
++#[allow(unused_variables)]
++#[allow(dead_code)]
++fn foo(a: &Animal, b: &Animal) {
++    match (a, b) {
++        (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref'
++        //                  ^    and   ^ should **not** be linted
++        (&Animal::Dog(ref a), &Animal::Dog(_)) => (), //              ^ should **not** be linted
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0a5cfb3db0b116fad26040dc10b589be15d19b4b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: this pattern takes a reference on something that is being de-referenced
++  --> $DIR/needless_borrowed_ref.rs:7:34
++   |
++LL |     let _ = v.iter_mut().filter(|&ref a| a.is_empty());
++   |                                  ^^^^^^ help: try removing the `&ref` part and just keep: `a`
++   |
++   = note: `-D clippy::needless-borrowed-reference` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b4227eaf2f8bbb8846e191bf888e2b99114619d9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++// run-rustfix
++
++#![allow(unused, clippy::suspicious_map)]
++
++use std::collections::{BTreeSet, HashMap, HashSet};
++
++#[warn(clippy::needless_collect)]
++#[allow(unused_variables, clippy::iter_cloned_collect)]
++fn main() {
++    let sample = [1; 5];
++    let len = sample.iter().count();
++    if sample.iter().next().is_none() {
++        // Empty
++    }
++    sample.iter().cloned().any(|x| x == 1);
++    sample.iter().map(|x| (x, x)).count();
++    // Notice the `HashSet`--this should not be linted
++    sample.iter().collect::<HashSet<_>>().len();
++    // Neither should this
++    sample.iter().collect::<BTreeSet<_>>().len();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7ee603afeb0778e74bc67e24d25919660c89ee87
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++// run-rustfix
++
++#![allow(unused, clippy::suspicious_map)]
++
++use std::collections::{BTreeSet, HashMap, HashSet};
++
++#[warn(clippy::needless_collect)]
++#[allow(unused_variables, clippy::iter_cloned_collect)]
++fn main() {
++    let sample = [1; 5];
++    let len = sample.iter().collect::<Vec<_>>().len();
++    if sample.iter().collect::<Vec<_>>().is_empty() {
++        // Empty
++    }
++    sample.iter().cloned().collect::<Vec<_>>().contains(&1);
++    sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().len();
++    // Notice the `HashSet`--this should not be linted
++    sample.iter().collect::<HashSet<_>>().len();
++    // Neither should this
++    sample.iter().collect::<BTreeSet<_>>().len();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8884c8e161293c11bed7b32cf2a5473fa85cc28a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect.rs:11:28
++   |
++LL |     let len = sample.iter().collect::<Vec<_>>().len();
++   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.count()`
++   |
++   = note: `-D clippy::needless-collect` implied by `-D warnings`
++
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect.rs:12:21
++   |
++LL |     if sample.iter().collect::<Vec<_>>().is_empty() {
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.next().is_none()`
++
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect.rs:15:27
++   |
++LL |     sample.iter().cloned().collect::<Vec<_>>().contains(&1);
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.any(|x| x == 1)`
++
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect.rs:16:34
++   |
++LL |     sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().len();
++   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.count()`
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5da95647f2c155d4bc7741d731b7561d095411bf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,115 @@@
++#![warn(clippy::needless_continue)]
++
++macro_rules! zero {
++    ($x:expr) => {
++        $x == 0
++    };
++}
++
++macro_rules! nonzero {
++    ($x:expr) => {
++        !zero!($x)
++    };
++}
++
++fn main() {
++    let mut i = 1;
++    while i < 10 {
++        i += 1;
++
++        if i % 2 == 0 && i % 3 == 0 {
++            println!("{}", i);
++            println!("{}", i + 1);
++            if i % 5 == 0 {
++                println!("{}", i + 2);
++            }
++            let i = 0;
++            println!("bar {} ", i);
++        } else {
++            continue;
++        }
++
++        println!("bleh");
++        {
++            println!("blah");
++        }
++
++        // some comments that also should ideally be included in the
++        // output of the lint suggestion if possible.
++        if !(!(i == 2) || !(i == 5)) {
++            println!("lama");
++        }
++
++        if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 {
++            continue;
++        } else {
++            println!("Blabber");
++            println!("Jabber");
++        }
++
++        println!("bleh");
++    }
++}
++
++mod issue_2329 {
++    fn condition() -> bool {
++        unimplemented!()
++    }
++    fn update_condition() {}
++
++    // only the outer loop has a label
++    fn foo() {
++        'outer: loop {
++            println!("Entry");
++            while condition() {
++                update_condition();
++                if condition() {
++                    println!("foo-1");
++                } else {
++                    continue 'outer; // should not lint here
++                }
++                println!("foo-2");
++
++                update_condition();
++                if condition() {
++                    continue 'outer; // should not lint here
++                } else {
++                    println!("foo-3");
++                }
++                println!("foo-4");
++            }
++        }
++    }
++
++    // both loops have labels
++    fn bar() {
++        'outer: loop {
++            println!("Entry");
++            'inner: while condition() {
++                update_condition();
++                if condition() {
++                    println!("bar-1");
++                } else {
++                    continue 'outer; // should not lint here
++                }
++                println!("bar-2");
++
++                update_condition();
++                if condition() {
++                    println!("bar-3");
++                } else {
++                    continue 'inner; // should lint here
++                }
++                println!("bar-4");
++
++                update_condition();
++                if condition() {
++                    continue; // should lint here
++                } else {
++                    println!("bar-5");
++                }
++                println!("bar-6");
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8d6a37df9601afcd1fdf9094a442b4422cf0c6fc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,99 @@@
++error: this `else` block is redundant
++  --> $DIR/needless_continue.rs:28:16
++   |
++LL |           } else {
++   |  ________________^
++LL | |             continue;
++LL | |         }
++   | |_________^
++   |
++   = note: `-D clippy::needless-continue` implied by `-D warnings`
++   = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block
++                   if i % 2 == 0 && i % 3 == 0 {
++                       println!("{}", i);
++                       println!("{}", i + 1);
++                       if i % 5 == 0 {
++                           println!("{}", i + 2);
++                       }
++                       let i = 0;
++                       println!("bar {} ", i);
++                       // merged code follows:
++                       println!("bleh");
++                       {
++                           println!("blah");
++                       }
++                       if !(!(i == 2) || !(i == 5)) {
++                           println!("lama");
++                       }
++                       if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 {
++                           continue;
++                       } else {
++                           println!("Blabber");
++                           println!("Jabber");
++                       }
++                       println!("bleh");
++                   }
++
++error: there is no need for an explicit `else` block for this `if` expression
++  --> $DIR/needless_continue.rs:43:9
++   |
++LL | /         if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 {
++LL | |             continue;
++LL | |         } else {
++LL | |             println!("Blabber");
++LL | |             println!("Jabber");
++LL | |         }
++   | |_________^
++   |
++   = help: consider dropping the `else` clause
++                   if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 {
++                       continue;
++                   }
++                   {
++                       println!("Blabber");
++                       println!("Jabber");
++                   }
++
++error: this `else` block is redundant
++  --> $DIR/needless_continue.rs:100:24
++   |
++LL |                   } else {
++   |  ________________________^
++LL | |                     continue 'inner; // should lint here
++LL | |                 }
++   | |_________________^
++   |
++   = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block
++                           if condition() {
++                               println!("bar-3");
++                               // merged code follows:
++                               println!("bar-4");
++                               update_condition();
++                               if condition() {
++                                   continue; // should lint here
++                               } else {
++                                   println!("bar-5");
++                               }
++                               println!("bar-6");
++                           }
++
++error: there is no need for an explicit `else` block for this `if` expression
++  --> $DIR/needless_continue.rs:106:17
++   |
++LL | /                 if condition() {
++LL | |                     continue; // should lint here
++LL | |                 } else {
++LL | |                     println!("bar-5");
++LL | |                 }
++   | |_________________^
++   |
++   = help: consider dropping the `else` clause
++                           if condition() {
++                               continue; // should lint here
++                           }
++                           {
++                               println!("bar-5");
++                           }
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..682d7b3c4ceb4f64b0f559f37138193ce3034da6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,74 @@@
++/// This is a test for needless `fn main()` in doctests.
++///
++/// # Examples
++///
++/// This should lint
++/// ```
++/// fn main() {
++///     unimplemented!();
++/// }
++/// ```
++///
++/// This should, too.
++///
++/// ```rust
++/// fn main() {
++///     unimplemented!();
++/// }
++/// ```
++///
++/// This one too.
++///
++/// ```no_run
++/// fn main() {
++///     unimplemented!();
++/// }
++/// ```
++fn bad_doctests() {}
++
++/// # Examples
++///
++/// This shouldn't lint, because the `main` is empty:
++/// ```
++/// fn main(){}
++/// ```
++///
++/// This shouldn't lint either, because there's a `static`:
++/// ```
++/// static ANSWER: i32 = 42;
++///
++/// fn main() {
++///     assert_eq!(42, ANSWER);
++/// }
++/// ```
++///
++/// Neither should this lint because of `extern crate`:
++/// ```
++/// #![feature(test)]
++/// extern crate test;
++/// fn main() {
++///     assert_eq(1u8, test::black_box(1));
++/// }
++/// ```
++///
++/// We should not lint ignored examples:
++///
++/// ```rust,ignore
++/// fn main() {
++///     unimplemented!();
++/// }
++/// ```
++///
++/// Or even non-rust examples:
++///
++/// ```text
++/// fn main() {
++///     is what starts the program
++/// }
++/// ```
++fn no_false_positives() {}
++
++fn main() {
++    bad_doctests();
++    no_false_positives();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..65d40ee6832f289d0130bf1d992b3f7b9f470d05
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: needless `fn main` in doctest
++  --> $DIR/needless_doc_main.rs:7:4
++   |
++LL | /// fn main() {
++   |    ^^^^^^^^^^^^
++   |
++   = note: `-D clippy::needless-doctest-main` implied by `-D warnings`
++
++error: needless `fn main` in doctest
++  --> $DIR/needless_doc_main.rs:15:4
++   |
++LL | /// fn main() {
++   |    ^^^^^^^^^^^^
++
++error: needless `fn main` in doctest
++  --> $DIR/needless_doc_main.rs:23:4
++   |
++LL | /// fn main() {
++   |    ^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..913cd004f19f4bd70941f49127930ec902573669
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,262 @@@
++#![warn(clippy::needless_lifetimes)]
++#![allow(dead_code, clippy::needless_pass_by_value)]
++
++fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
++
++fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {}
++
++// No error; same lifetime on two params.
++fn same_lifetime_on_input<'a>(_x: &'a u8, _y: &'a u8) {}
++
++// No error; static involved.
++fn only_static_on_input(_x: &u8, _y: &u8, _z: &'static u8) {}
++
++fn mut_and_static_input(_x: &mut u8, _y: &'static str) {}
++
++fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 {
++    x
++}
++
++// No error; multiple input refs.
++fn multiple_in_and_out_1<'a>(x: &'a u8, _y: &'a u8) -> &'a u8 {
++    x
++}
++
++// No error; multiple input refs.
++fn multiple_in_and_out_2<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 {
++    x
++}
++
++// No error; static involved.
++fn in_static_and_out<'a>(x: &'a u8, _y: &'static u8) -> &'a u8 {
++    x
++}
++
++// No error.
++fn deep_reference_1<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> {
++    Ok(x)
++}
++
++// No error; two input refs.
++fn deep_reference_2<'a>(x: Result<&'a u8, &'a u8>) -> &'a u8 {
++    x.unwrap()
++}
++
++fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> {
++    Ok(x)
++}
++
++// Where-clause, but without lifetimes.
++fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()>
++where
++    T: Copy,
++{
++    Ok(x)
++}
++
++type Ref<'r> = &'r u8;
++
++// No error; same lifetime on two params.
++fn lifetime_param_1<'a>(_x: Ref<'a>, _y: &'a u8) {}
++
++fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
++
++// No error; bounded lifetime.
++fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) {}
++
++// No error; bounded lifetime.
++fn lifetime_param_4<'a, 'b>(_x: Ref<'a>, _y: &'b u8)
++where
++    'b: 'a,
++{
++}
++
++struct Lt<'a, I: 'static> {
++    x: &'a I,
++}
++
++// No error; fn bound references `'a`.
++fn fn_bound<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
++where
++    F: Fn(Lt<'a, I>) -> Lt<'a, I>,
++{
++    unreachable!()
++}
++
++fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
++where
++    for<'x> F: Fn(Lt<'x, I>) -> Lt<'x, I>,
++{
++    unreachable!()
++}
++
++// No error; see below.
++fn fn_bound_3<'a, F: FnOnce(&'a i32)>(x: &'a i32, f: F) {
++    f(x);
++}
++
++fn fn_bound_3_cannot_elide() {
++    let x = 42;
++    let p = &x;
++    let mut q = &x;
++    // This will fail if we elide lifetimes of `fn_bound_3`.
++    fn_bound_3(p, |y| q = y);
++}
++
++// No error; multiple input refs.
++fn fn_bound_4<'a, F: FnOnce() -> &'a ()>(cond: bool, x: &'a (), f: F) -> &'a () {
++    if cond {
++        x
++    } else {
++        f()
++    }
++}
++
++struct X {
++    x: u8,
++}
++
++impl X {
++    fn self_and_out<'s>(&'s self) -> &'s u8 {
++        &self.x
++    }
++
++    // No error; multiple input refs.
++    fn self_and_in_out<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 {
++        &self.x
++    }
++
++    fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {}
++
++    // No error; same lifetimes on two params.
++    fn self_and_same_in<'s>(&'s self, _x: &'s u8) {}
++}
++
++struct Foo<'a>(&'a u8);
++
++impl<'a> Foo<'a> {
++    // No error; lifetime `'a` not defined in method.
++    fn self_shared_lifetime(&self, _: &'a u8) {}
++    // No error; bounds exist.
++    fn self_bound_lifetime<'b: 'a>(&self, _: &'b u8) {}
++}
++
++fn already_elided<'a>(_: &u8, _: &'a u8) -> &'a u8 {
++    unimplemented!()
++}
++
++fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
++    unimplemented!()
++}
++
++// No warning; two input lifetimes (named on the reference, anonymous on `Foo`).
++fn struct_with_lt2<'a>(_foo: &'a Foo) -> &'a str {
++    unimplemented!()
++}
++
++// No warning; two input lifetimes (anonymous on the reference, named on `Foo`).
++fn struct_with_lt3<'a>(_foo: &Foo<'a>) -> &'a str {
++    unimplemented!()
++}
++
++// No warning; two input lifetimes.
++fn struct_with_lt4<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str {
++    unimplemented!()
++}
++
++trait WithLifetime<'a> {}
++
++type WithLifetimeAlias<'a> = dyn WithLifetime<'a>;
++
++// Should not warn because it won't build without the lifetime.
++fn trait_obj_elided<'a>(_arg: &'a dyn WithLifetime) -> &'a str {
++    unimplemented!()
++}
++
++// Should warn because there is no lifetime on `Drop`, so this would be
++// unambiguous if we elided the lifetime.
++fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str {
++    unimplemented!()
++}
++
++type FooAlias<'a> = Foo<'a>;
++
++fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
++    unimplemented!()
++}
++
++// No warning; two input lifetimes (named on the reference, anonymous on `FooAlias`).
++fn alias_with_lt2<'a>(_foo: &'a FooAlias) -> &'a str {
++    unimplemented!()
++}
++
++// No warning; two input lifetimes (anonymous on the reference, named on `FooAlias`).
++fn alias_with_lt3<'a>(_foo: &FooAlias<'a>) -> &'a str {
++    unimplemented!()
++}
++
++// No warning; two input lifetimes.
++fn alias_with_lt4<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str {
++    unimplemented!()
++}
++
++fn named_input_elided_output<'a>(_arg: &'a str) -> &str {
++    unimplemented!()
++}
++
++fn elided_input_named_output<'a>(_arg: &str) -> &'a str {
++    unimplemented!()
++}
++
++fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) {
++    unimplemented!()
++}
++fn trait_bound<'a, T: WithLifetime<'a>>(_: &'a u8, _: T) {
++    unimplemented!()
++}
++
++// Don't warn on these; see issue #292.
++fn trait_bound_bug<'a, T: WithLifetime<'a>>() {
++    unimplemented!()
++}
++
++// See issue #740.
++struct Test {
++    vec: Vec<usize>,
++}
++
++impl Test {
++    fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = usize> + 'a> {
++        unimplemented!()
++    }
++}
++
++trait LintContext<'a> {}
++
++fn f<'a, T: LintContext<'a>>(_: &T) {}
++
++fn test<'a>(x: &'a [u8]) -> u8 {
++    let y: &'a u8 = &x[5];
++    *y
++}
++
++// Issue #3284: give hint regarding lifetime in return type.
++struct Cow<'a> {
++    x: &'a str,
++}
++fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
++    unimplemented!()
++}
++
++// Make sure we still warn on implementations
++mod issue4291 {
++    trait BadTrait {
++        fn needless_lt<'a>(x: &'a u8) {}
++    }
++
++    impl BadTrait for () {
++        fn needless_lt<'a>(_x: &'a u8) {}
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d3a360ed8b576d04bf95e0327c1854f81ad8fb4a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,106 @@@
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/needless_lifetimes.rs:4:1
++   |
++LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::needless-lifetimes` implied by `-D warnings`
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/needless_lifetimes.rs:6:1
++   |
++LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {}
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/needless_lifetimes.rs:16:1
++   |
++LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/needless_lifetimes.rs:45:1
++   |
++LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/needless_lifetimes.rs:50:1
++   |
++LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()>
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/needless_lifetimes.rs:62:1
++   |
++LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/needless_lifetimes.rs:86:1
++   |
++LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/needless_lifetimes.rs:120:5
++   |
++LL |     fn self_and_out<'s>(&'s self) -> &'s u8 {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/needless_lifetimes.rs:129:5
++   |
++LL |     fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {}
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/needless_lifetimes.rs:148:1
++   |
++LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/needless_lifetimes.rs:178:1
++   |
++LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/needless_lifetimes.rs:184:1
++   |
++LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/needless_lifetimes.rs:203:1
++   |
++LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/needless_lifetimes.rs:211:1
++   |
++LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/needless_lifetimes.rs:247:1
++   |
++LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/needless_lifetimes.rs:254:9
++   |
++LL |         fn needless_lt<'a>(x: &'a u8) {}
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++  --> $DIR/needless_lifetimes.rs:258:9
++   |
++LL |         fn needless_lt<'a>(_x: &'a u8) {}
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 17 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e93a7fe2985b3968357e8a69081abd6dd2f67eeb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,161 @@@
++#![warn(clippy::needless_pass_by_value)]
++#![allow(
++    dead_code,
++    clippy::single_match,
++    clippy::redundant_pattern_matching,
++    clippy::many_single_char_names,
++    clippy::option_option,
++    clippy::redundant_clone
++)]
++
++use std::borrow::Borrow;
++use std::collections::HashSet;
++use std::convert::AsRef;
++use std::mem::MaybeUninit;
++
++// `v` should be warned
++// `w`, `x` and `y` are allowed (moved or mutated)
++fn foo<T: Default>(v: Vec<T>, w: Vec<T>, mut x: Vec<T>, y: Vec<T>) -> Vec<T> {
++    assert_eq!(v.len(), 42);
++
++    consume(w);
++
++    x.push(T::default());
++
++    y
++}
++
++fn consume<T>(_: T) {}
++
++struct Wrapper(String);
++
++fn bar(x: String, y: Wrapper) {
++    assert_eq!(x.len(), 42);
++    assert_eq!(y.0.len(), 42);
++}
++
++// V implements `Borrow<V>`, but should be warned correctly
++fn test_borrow_trait<T: Borrow<str>, U: AsRef<str>, V>(t: T, u: U, v: V) {
++    println!("{}", t.borrow());
++    println!("{}", u.as_ref());
++    consume(&v);
++}
++
++// ok
++fn test_fn<F: Fn(i32) -> i32>(f: F) {
++    f(1);
++}
++
++// x should be warned, but y is ok
++fn test_match(x: Option<Option<String>>, y: Option<Option<String>>) {
++    match x {
++        Some(Some(_)) => 1, // not moved
++        _ => 0,
++    };
++
++    match y {
++        Some(Some(s)) => consume(s), // moved
++        _ => (),
++    };
++}
++
++// x and y should be warned, but z is ok
++fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
++    let Wrapper(s) = z; // moved
++    let Wrapper(ref t) = y; // not moved
++    let Wrapper(_) = y; // still not moved
++
++    assert_eq!(x.0.len(), s.len());
++    println!("{}", t);
++}
++
++trait Foo {}
++
++// `S: Serialize` is allowed to be passed by value, since a caller can pass `&S` instead
++trait Serialize {}
++impl<'a, T> Serialize for &'a T where T: Serialize {}
++impl Serialize for i32 {}
++
++fn test_blanket_ref<T: Foo, S: Serialize>(_foo: T, _serializable: S) {}
++
++fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
++    s.capacity();
++    let _ = t.clone();
++    u.capacity();
++    let _ = v.clone();
++}
++
++struct S<T, U>(T, U);
++
++impl<T: Serialize, U> S<T, U> {
++    fn foo(
++        self,
++        // taking `self` by value is always allowed
++        s: String,
++        t: String,
++    ) -> usize {
++        s.len() + t.capacity()
++    }
++
++    fn bar(_t: T, // Ok, since `&T: Serialize` too
++    ) {
++    }
++
++    fn baz(&self, _u: U, _s: Self) {}
++}
++
++trait FalsePositive {
++    fn visit_str(s: &str);
++    fn visit_string(s: String) {
++        Self::visit_str(&s);
++    }
++}
++
++// shouldn't warn on extern funcs
++extern "C" fn ext(x: MaybeUninit<usize>) -> usize {
++    unsafe { x.assume_init() }
++}
++
++// whitelist RangeArgument
++fn range<T: ::std::ops::RangeBounds<usize>>(range: T) {
++    let _ = range.start_bound();
++}
++
++struct CopyWrapper(u32);
++
++fn bar_copy(x: u32, y: CopyWrapper) {
++    assert_eq!(x, 42);
++    assert_eq!(y.0, 42);
++}
++
++// x and y should be warned, but z is ok
++fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
++    let CopyWrapper(s) = z; // moved
++    let CopyWrapper(ref t) = y; // not moved
++    let CopyWrapper(_) = y; // still not moved
++
++    assert_eq!(x.0, s);
++    println!("{}", t);
++}
++
++// The following 3 lines should not cause an ICE. See #2831
++trait Bar<'a, A> {}
++impl<'b, T> Bar<'b, T> for T {}
++fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {}
++
++// Also this should not cause an ICE. See #2831
++trait Club<'a, A> {}
++impl<T> Club<'static, T> for T {}
++fn more_fun(_item: impl Club<'static, i32>) {}
++
++fn is_sync<T>(_: T)
++where
++    T: Sync,
++{
++}
++
++fn main() {
++    // This should not cause an ICE either
++    // https://github.com/rust-lang/rust-clippy/issues/3144
++    is_sync(HashSet::<usize>::new());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9aa783bf904e1224a5d1361c0579fd01a2050c73
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,178 @@@
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:18:23
++   |
++LL | fn foo<T: Default>(v: Vec<T>, w: Vec<T>, mut x: Vec<T>, y: Vec<T>) -> Vec<T> {
++   |                       ^^^^^^ help: consider changing the type to: `&[T]`
++   |
++   = note: `-D clippy::needless-pass-by-value` implied by `-D warnings`
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:32:11
++   |
++LL | fn bar(x: String, y: Wrapper) {
++   |           ^^^^^^ help: consider changing the type to: `&str`
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:32:22
++   |
++LL | fn bar(x: String, y: Wrapper) {
++   |                      ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:38:71
++   |
++LL | fn test_borrow_trait<T: Borrow<str>, U: AsRef<str>, V>(t: T, u: U, v: V) {
++   |                                                                       ^ help: consider taking a reference instead: `&V`
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:50:18
++   |
++LL | fn test_match(x: Option<Option<String>>, y: Option<Option<String>>) {
++   |                  ^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&Option<Option<String>>`
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:63:24
++   |
++LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
++   |                        ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:63:36
++   |
++LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
++   |                                    ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:79:49
++   |
++LL | fn test_blanket_ref<T: Foo, S: Serialize>(_foo: T, _serializable: S) {}
++   |                                                 ^ help: consider taking a reference instead: `&T`
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:81:18
++   |
++LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
++   |                  ^^^^^^ help: consider taking a reference instead: `&String`
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:81:29
++   |
++LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
++   |                             ^^^^^^
++   |
++help: consider changing the type to
++   |
++LL | fn issue_2114(s: String, t: &str, u: Vec<i32>, v: Vec<i32>) {
++   |                             ^^^^
++help: change `t.clone()` to
++   |
++LL |     let _ = t.to_string();
++   |             ^^^^^^^^^^^^^
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:81:40
++   |
++LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
++   |                                        ^^^^^^^^ help: consider taking a reference instead: `&Vec<i32>`
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:81:53
++   |
++LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
++   |                                                     ^^^^^^^^
++   |
++help: consider changing the type to
++   |
++LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: &[i32]) {
++   |                                                     ^^^^^^
++help: change `v.clone()` to
++   |
++LL |     let _ = v.to_owned();
++   |             ^^^^^^^^^^^^
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:94:12
++   |
++LL |         s: String,
++   |            ^^^^^^ help: consider changing the type to: `&str`
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:95:12
++   |
++LL |         t: String,
++   |            ^^^^^^ help: consider taking a reference instead: `&String`
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:104:23
++   |
++LL |     fn baz(&self, _u: U, _s: Self) {}
++   |                       ^ help: consider taking a reference instead: `&U`
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:104:30
++   |
++LL |     fn baz(&self, _u: U, _s: Self) {}
++   |                              ^^^^ help: consider taking a reference instead: `&Self`
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:126:24
++   |
++LL | fn bar_copy(x: u32, y: CopyWrapper) {
++   |                        ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
++   |
++help: consider marking this type as `Copy`
++  --> $DIR/needless_pass_by_value.rs:124:1
++   |
++LL | struct CopyWrapper(u32);
++   | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:132:29
++   |
++LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
++   |                             ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
++   |
++help: consider marking this type as `Copy`
++  --> $DIR/needless_pass_by_value.rs:124:1
++   |
++LL | struct CopyWrapper(u32);
++   | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:132:45
++   |
++LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
++   |                                             ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
++   |
++help: consider marking this type as `Copy`
++  --> $DIR/needless_pass_by_value.rs:124:1
++   |
++LL | struct CopyWrapper(u32);
++   | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:132:61
++   |
++LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
++   |                                                             ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
++   |
++help: consider marking this type as `Copy`
++  --> $DIR/needless_pass_by_value.rs:124:1
++   |
++LL | struct CopyWrapper(u32);
++   | ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:144:40
++   |
++LL | fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {}
++   |                                        ^ help: consider taking a reference instead: `&S`
++
++error: this argument is passed by value, but not consumed in the function body
++  --> $DIR/needless_pass_by_value.rs:149:20
++   |
++LL | fn more_fun(_item: impl Club<'static, i32>) {}
++   |                    ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>`
++
++error: aborting due to 22 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..78a0e92d1797976c8a1fcfcdb28aa4d21f90e168
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++#![crate_type = "proc-macro"]
++#![warn(clippy::needless_pass_by_value)]
++
++extern crate proc_macro;
++
++use proc_macro::TokenStream;
++
++#[proc_macro_derive(Foo)]
++pub fn foo(_input: TokenStream) -> TokenStream {
++    unimplemented!()
++}
++
++#[proc_macro]
++pub fn bar(_input: TokenStream) -> TokenStream {
++    unimplemented!()
++}
++
++#[proc_macro_attribute]
++pub fn baz(_args: TokenStream, _input: TokenStream) -> TokenStream {
++    unimplemented!()
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3fce34367ae5040132e5bad3ed34924faf7178ad
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,95 @@@
++#![warn(clippy::needless_range_loop)]
++
++static STATIC: [usize; 4] = [0, 1, 8, 16];
++const CONST: [usize; 4] = [0, 1, 8, 16];
++const MAX_LEN: usize = 42;
++
++fn main() {
++    let mut vec = vec![1, 2, 3, 4];
++    let vec2 = vec![1, 2, 3, 4];
++    for i in 0..vec.len() {
++        println!("{}", vec[i]);
++    }
++
++    for i in 0..vec.len() {
++        let i = 42; // make a different `i`
++        println!("{}", vec[i]); // ok, not the `i` of the for-loop
++    }
++
++    for i in 0..vec.len() {
++        let _ = vec[i];
++    }
++
++    // ICE #746
++    for j in 0..4 {
++        println!("{:?}", STATIC[j]);
++    }
++
++    for j in 0..4 {
++        println!("{:?}", CONST[j]);
++    }
++
++    for i in 0..vec.len() {
++        println!("{} {}", vec[i], i);
++    }
++    for i in 0..vec.len() {
++        // not an error, indexing more than one variable
++        println!("{} {}", vec[i], vec2[i]);
++    }
++
++    for i in 0..vec.len() {
++        println!("{}", vec2[i]);
++    }
++
++    for i in 5..vec.len() {
++        println!("{}", vec[i]);
++    }
++
++    for i in 0..MAX_LEN {
++        println!("{}", vec[i]);
++    }
++
++    for i in 0..=MAX_LEN {
++        println!("{}", vec[i]);
++    }
++
++    for i in 5..10 {
++        println!("{}", vec[i]);
++    }
++
++    for i in 5..=10 {
++        println!("{}", vec[i]);
++    }
++
++    for i in 5..vec.len() {
++        println!("{} {}", vec[i], i);
++    }
++
++    for i in 5..10 {
++        println!("{} {}", vec[i], i);
++    }
++
++    // #2542
++    for i in 0..vec.len() {
++        vec[i] = Some(1).unwrap_or_else(|| panic!("error on {}", i));
++    }
++
++    // #3788
++    let test = Test {
++        inner: vec![1, 2, 3, 4],
++    };
++    for i in 0..2 {
++        println!("{}", test[i]);
++    }
++}
++
++struct Test {
++    inner: Vec<usize>,
++}
++
++impl std::ops::Index<usize> for Test {
++    type Output = usize;
++    fn index(&self, index: usize) -> &Self::Output {
++        &self.inner[index]
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c50c4931fb4cc8b68a438de09803e46e0c2015ec
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,157 @@@
++error: the loop variable `i` is only used to index `vec`.
++  --> $DIR/needless_range_loop.rs:10:14
++   |
++LL |     for i in 0..vec.len() {
++   |              ^^^^^^^^^^^^
++   |
++   = note: `-D clippy::needless-range-loop` implied by `-D warnings`
++help: consider using an iterator
++   |
++LL |     for <item> in &vec {
++   |         ^^^^^^    ^^^^
++
++error: the loop variable `i` is only used to index `vec`.
++  --> $DIR/needless_range_loop.rs:19:14
++   |
++LL |     for i in 0..vec.len() {
++   |              ^^^^^^^^^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for <item> in &vec {
++   |         ^^^^^^    ^^^^
++
++error: the loop variable `j` is only used to index `STATIC`.
++  --> $DIR/needless_range_loop.rs:24:14
++   |
++LL |     for j in 0..4 {
++   |              ^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for <item> in &STATIC {
++   |         ^^^^^^    ^^^^^^^
++
++error: the loop variable `j` is only used to index `CONST`.
++  --> $DIR/needless_range_loop.rs:28:14
++   |
++LL |     for j in 0..4 {
++   |              ^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for <item> in &CONST {
++   |         ^^^^^^    ^^^^^^
++
++error: the loop variable `i` is used to index `vec`
++  --> $DIR/needless_range_loop.rs:32:14
++   |
++LL |     for i in 0..vec.len() {
++   |              ^^^^^^^^^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for (i, <item>) in vec.iter().enumerate() {
++   |         ^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `vec2`.
++  --> $DIR/needless_range_loop.rs:40:14
++   |
++LL |     for i in 0..vec.len() {
++   |              ^^^^^^^^^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for <item> in vec2.iter().take(vec.len()) {
++   |         ^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `vec`.
++  --> $DIR/needless_range_loop.rs:44:14
++   |
++LL |     for i in 5..vec.len() {
++   |              ^^^^^^^^^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for <item> in vec.iter().skip(5) {
++   |         ^^^^^^    ^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `vec`.
++  --> $DIR/needless_range_loop.rs:48:14
++   |
++LL |     for i in 0..MAX_LEN {
++   |              ^^^^^^^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for <item> in vec.iter().take(MAX_LEN) {
++   |         ^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `vec`.
++  --> $DIR/needless_range_loop.rs:52:14
++   |
++LL |     for i in 0..=MAX_LEN {
++   |              ^^^^^^^^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for <item> in vec.iter().take(MAX_LEN + 1) {
++   |         ^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `vec`.
++  --> $DIR/needless_range_loop.rs:56:14
++   |
++LL |     for i in 5..10 {
++   |              ^^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for <item> in vec.iter().take(10).skip(5) {
++   |         ^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `vec`.
++  --> $DIR/needless_range_loop.rs:60:14
++   |
++LL |     for i in 5..=10 {
++   |              ^^^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for <item> in vec.iter().take(10 + 1).skip(5) {
++   |         ^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is used to index `vec`
++  --> $DIR/needless_range_loop.rs:64:14
++   |
++LL |     for i in 5..vec.len() {
++   |              ^^^^^^^^^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for (i, <item>) in vec.iter().enumerate().skip(5) {
++   |         ^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is used to index `vec`
++  --> $DIR/needless_range_loop.rs:68:14
++   |
++LL |     for i in 5..10 {
++   |              ^^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for (i, <item>) in vec.iter().enumerate().take(10).skip(5) {
++   |         ^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is used to index `vec`
++  --> $DIR/needless_range_loop.rs:73:14
++   |
++LL |     for i in 0..vec.len() {
++   |              ^^^^^^^^^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for (i, <item>) in vec.iter_mut().enumerate() {
++   |         ^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 14 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2ed1b09bece746d1beb7ef6a791946efeee0f681
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,85 @@@
++#![warn(clippy::needless_range_loop)]
++
++fn calc_idx(i: usize) -> usize {
++    (i + i + 20) % 4
++}
++
++fn main() {
++    let ns = vec![2, 3, 5, 7];
++
++    for i in 3..10 {
++        println!("{}", ns[i]);
++    }
++
++    for i in 3..10 {
++        println!("{}", ns[i % 4]);
++    }
++
++    for i in 3..10 {
++        println!("{}", ns[i % ns.len()]);
++    }
++
++    for i in 3..10 {
++        println!("{}", ns[calc_idx(i)]);
++    }
++
++    for i in 3..10 {
++        println!("{}", ns[calc_idx(i) % 4]);
++    }
++
++    let mut ms = vec![1, 2, 3, 4, 5, 6];
++    for i in 0..ms.len() {
++        ms[i] *= 2;
++    }
++    assert_eq!(ms, vec![2, 4, 6, 8, 10, 12]);
++
++    let mut ms = vec![1, 2, 3, 4, 5, 6];
++    for i in 0..ms.len() {
++        let x = &mut ms[i];
++        *x *= 2;
++    }
++    assert_eq!(ms, vec![2, 4, 6, 8, 10, 12]);
++
++    let g = vec![1, 2, 3, 4, 5, 6];
++    let glen = g.len();
++    for i in 0..glen {
++        let x: u32 = g[i + 1..].iter().sum();
++        println!("{}", g[i] + x);
++    }
++    assert_eq!(g, vec![20, 18, 15, 11, 6, 0]);
++
++    let mut g = vec![1, 2, 3, 4, 5, 6];
++    let glen = g.len();
++    for i in 0..glen {
++        g[i] = g[i + 1..].iter().sum();
++    }
++    assert_eq!(g, vec![20, 18, 15, 11, 6, 0]);
++
++    let x = 5;
++    let mut vec = vec![0; 9];
++
++    for i in x..x + 4 {
++        vec[i] += 1;
++    }
++
++    let x = 5;
++    let mut vec = vec![0; 10];
++
++    for i in x..=x + 4 {
++        vec[i] += 1;
++    }
++
++    let arr = [1, 2, 3];
++
++    for i in 0..3 {
++        println!("{}", arr[i]);
++    }
++
++    for i in 0..2 {
++        println!("{}", arr[i]);
++    }
++
++    for i in 1..3 {
++        println!("{}", arr[i]);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c54ab5ec9809ac0d26ab7b2cc4ccddd6c645f56b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,91 @@@
++error: the loop variable `i` is only used to index `ns`.
++  --> $DIR/needless_range_loop2.rs:10:14
++   |
++LL |     for i in 3..10 {
++   |              ^^^^^
++   |
++   = note: `-D clippy::needless-range-loop` implied by `-D warnings`
++help: consider using an iterator
++   |
++LL |     for <item> in ns.iter().take(10).skip(3) {
++   |         ^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `ms`.
++  --> $DIR/needless_range_loop2.rs:31:14
++   |
++LL |     for i in 0..ms.len() {
++   |              ^^^^^^^^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for <item> in &mut ms {
++   |         ^^^^^^    ^^^^^^^
++
++error: the loop variable `i` is only used to index `ms`.
++  --> $DIR/needless_range_loop2.rs:37:14
++   |
++LL |     for i in 0..ms.len() {
++   |              ^^^^^^^^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for <item> in &mut ms {
++   |         ^^^^^^    ^^^^^^^
++
++error: the loop variable `i` is only used to index `vec`.
++  --> $DIR/needless_range_loop2.rs:61:14
++   |
++LL |     for i in x..x + 4 {
++   |              ^^^^^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for <item> in vec.iter_mut().skip(x).take(4) {
++   |         ^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `vec`.
++  --> $DIR/needless_range_loop2.rs:68:14
++   |
++LL |     for i in x..=x + 4 {
++   |              ^^^^^^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for <item> in vec.iter_mut().skip(x).take(4 + 1) {
++   |         ^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `arr`.
++  --> $DIR/needless_range_loop2.rs:74:14
++   |
++LL |     for i in 0..3 {
++   |              ^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for <item> in &arr {
++   |         ^^^^^^    ^^^^
++
++error: the loop variable `i` is only used to index `arr`.
++  --> $DIR/needless_range_loop2.rs:78:14
++   |
++LL |     for i in 0..2 {
++   |              ^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for <item> in arr.iter().take(2) {
++   |         ^^^^^^    ^^^^^^^^^^^^^^^^^^
++
++error: the loop variable `i` is only used to index `arr`.
++  --> $DIR/needless_range_loop2.rs:82:14
++   |
++LL |     for i in 1..3 {
++   |              ^^^^
++   |
++help: consider using an iterator
++   |
++LL |     for <item> in arr.iter().skip(1) {
++   |         ^^^^^^    ^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ad20e2381073aae324d7f90474f89befe77aa24b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,78 @@@
++// run-rustfix
++
++#![allow(unused, clippy::needless_bool)]
++#![allow(clippy::if_same_then_else, clippy::single_match)]
++#![warn(clippy::needless_return)]
++
++macro_rules! the_answer {
++    () => {
++        42
++    };
++}
++
++fn test_end_of_fn() -> bool {
++    if true {
++        // no error!
++        return true;
++    }
++    true
++}
++
++fn test_no_semicolon() -> bool {
++    true
++}
++
++fn test_if_block() -> bool {
++    if true {
++        true
++    } else {
++        false
++    }
++}
++
++fn test_match(x: bool) -> bool {
++    match x {
++        true => false,
++        false => {
++            true
++        },
++    }
++}
++
++fn test_closure() {
++    let _ = || {
++        true
++    };
++    let _ = || true;
++}
++
++fn test_macro_call() -> i32 {
++    return the_answer!();
++}
++
++fn test_void_fun() {
++    
++}
++
++fn test_void_if_fun(b: bool) {
++    if b {
++        
++    } else {
++        
++    }
++}
++
++fn test_void_match(x: u32) {
++    match x {
++        0 => (),
++        _ => {},
++    }
++}
++
++fn main() {
++    let _ = test_end_of_fn();
++    let _ = test_no_semicolon();
++    let _ = test_if_block();
++    let _ = test_match(true);
++    test_closure();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..af0cdfb207ff568f9e0cbc29225437196be17868
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,78 @@@
++// run-rustfix
++
++#![allow(unused, clippy::needless_bool)]
++#![allow(clippy::if_same_then_else, clippy::single_match)]
++#![warn(clippy::needless_return)]
++
++macro_rules! the_answer {
++    () => {
++        42
++    };
++}
++
++fn test_end_of_fn() -> bool {
++    if true {
++        // no error!
++        return true;
++    }
++    return true;
++}
++
++fn test_no_semicolon() -> bool {
++    return true;
++}
++
++fn test_if_block() -> bool {
++    if true {
++        return true;
++    } else {
++        return false;
++    }
++}
++
++fn test_match(x: bool) -> bool {
++    match x {
++        true => return false,
++        false => {
++            return true;
++        },
++    }
++}
++
++fn test_closure() {
++    let _ = || {
++        return true;
++    };
++    let _ = || return true;
++}
++
++fn test_macro_call() -> i32 {
++    return the_answer!();
++}
++
++fn test_void_fun() {
++    return;
++}
++
++fn test_void_if_fun(b: bool) {
++    if b {
++        return;
++    } else {
++        return;
++    }
++}
++
++fn test_void_match(x: u32) {
++    match x {
++        0 => (),
++        _ => return,
++    }
++}
++
++fn main() {
++    let _ = test_end_of_fn();
++    let _ = test_no_semicolon();
++    let _ = test_if_block();
++    let _ = test_match(true);
++    test_closure();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c34eecbcbb639bc906ff845e5332e790cf3364ee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,76 @@@
++error: unneeded `return` statement
++  --> $DIR/needless_return.rs:18:5
++   |
++LL |     return true;
++   |     ^^^^^^^^^^^^ help: remove `return`: `true`
++   |
++   = note: `-D clippy::needless-return` implied by `-D warnings`
++
++error: unneeded `return` statement
++  --> $DIR/needless_return.rs:22:5
++   |
++LL |     return true;
++   |     ^^^^^^^^^^^^ help: remove `return`: `true`
++
++error: unneeded `return` statement
++  --> $DIR/needless_return.rs:27:9
++   |
++LL |         return true;
++   |         ^^^^^^^^^^^^ help: remove `return`: `true`
++
++error: unneeded `return` statement
++  --> $DIR/needless_return.rs:29:9
++   |
++LL |         return false;
++   |         ^^^^^^^^^^^^^ help: remove `return`: `false`
++
++error: unneeded `return` statement
++  --> $DIR/needless_return.rs:35:17
++   |
++LL |         true => return false,
++   |                 ^^^^^^^^^^^^ help: remove `return`: `false`
++
++error: unneeded `return` statement
++  --> $DIR/needless_return.rs:37:13
++   |
++LL |             return true;
++   |             ^^^^^^^^^^^^ help: remove `return`: `true`
++
++error: unneeded `return` statement
++  --> $DIR/needless_return.rs:44:9
++   |
++LL |         return true;
++   |         ^^^^^^^^^^^^ help: remove `return`: `true`
++
++error: unneeded `return` statement
++  --> $DIR/needless_return.rs:46:16
++   |
++LL |     let _ = || return true;
++   |                ^^^^^^^^^^^ help: remove `return`: `true`
++
++error: unneeded `return` statement
++  --> $DIR/needless_return.rs:54:5
++   |
++LL |     return;
++   |     ^^^^^^^ help: remove `return`
++
++error: unneeded `return` statement
++  --> $DIR/needless_return.rs:59:9
++   |
++LL |         return;
++   |         ^^^^^^^ help: remove `return`
++
++error: unneeded `return` statement
++  --> $DIR/needless_return.rs:61:9
++   |
++LL |         return;
++   |         ^^^^^^^ help: remove `return`
++
++error: unneeded `return` statement
++  --> $DIR/needless_return.rs:68:14
++   |
++LL |         _ => return,
++   |              ^^^^^^ help: replace `return` with an empty block: `{}`
++
++error: aborting due to 12 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bfa005a19f91073c5c31552f22a295b97975f9b0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++#![warn(clippy::needless_update)]
++#![allow(clippy::no_effect)]
++
++struct S {
++    pub a: i32,
++    pub b: i32,
++}
++
++fn main() {
++    let base = S { a: 0, b: 0 };
++    S { ..base }; // no error
++    S { a: 1, ..base }; // no error
++    S { a: 1, b: 1, ..base };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..133c834880dd907d3f797b03d79bbe7c7ecc6d45
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: struct update has no effect, all the fields in the struct have already been specified
++  --> $DIR/needless_update.rs:13:23
++   |
++LL |     S { a: 1, b: 1, ..base };
++   |                       ^^^^
++   |
++   = note: `-D clippy::needless-update` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..856a430ba2b55012c413b2506862addb039a23c7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++//! This test case utilizes `f64` an easy example for `PartialOrd` only types
++//! but the lint itself actually validates any expression where the left
++//! operand implements `PartialOrd` but not `Ord`.
++
++use std::cmp::Ordering;
++
++#[warn(clippy::neg_cmp_op_on_partial_ord)]
++fn main() {
++    let a_value = 1.0;
++    let another_value = 7.0;
++
++    // --- Bad ---
++
++    // Not Less but potentially Greater, Equal or Uncomparable.
++    let _not_less = !(a_value < another_value);
++
++    // Not Less or Equal but potentially Greater or Uncomparable.
++    let _not_less_or_equal = !(a_value <= another_value);
++
++    // Not Greater but potentially Less, Equal or Uncomparable.
++    let _not_greater = !(a_value > another_value);
++
++    // Not Greater or Equal but potentially Less or Uncomparable.
++    let _not_greater_or_equal = !(a_value >= another_value);
++
++    // --- Good ---
++
++    let _not_less = match a_value.partial_cmp(&another_value) {
++        None | Some(Ordering::Greater) | Some(Ordering::Equal) => true,
++        _ => false,
++    };
++    let _not_less_or_equal = match a_value.partial_cmp(&another_value) {
++        None | Some(Ordering::Greater) => true,
++        _ => false,
++    };
++    let _not_greater = match a_value.partial_cmp(&another_value) {
++        None | Some(Ordering::Less) | Some(Ordering::Equal) => true,
++        _ => false,
++    };
++    let _not_greater_or_equal = match a_value.partial_cmp(&another_value) {
++        None | Some(Ordering::Less) => true,
++        _ => false,
++    };
++
++    // --- Should not trigger ---
++
++    let _ = a_value < another_value;
++    let _ = a_value <= another_value;
++    let _ = a_value > another_value;
++    let _ = a_value >= another_value;
++
++    // --- regression tests ---
++
++    // Issue 2856: False positive on assert!()
++    //
++    // The macro always negates the result of the given comparison in its
++    // internal check which automatically triggered the lint. As it's an
++    // external macro there was no chance to do anything about it which led
++    // to a whitelisting of all external macros.
++    assert!(a_value < another_value);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d05fd34ce33be3f5a497fb868e89d69c55cabfba
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
++  --> $DIR/neg_cmp_op_on_partial_ord.rs:15:21
++   |
++LL |     let _not_less = !(a_value < another_value);
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::neg-cmp-op-on-partial-ord` implied by `-D warnings`
++
++error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
++  --> $DIR/neg_cmp_op_on_partial_ord.rs:18:30
++   |
++LL |     let _not_less_or_equal = !(a_value <= another_value);
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
++  --> $DIR/neg_cmp_op_on_partial_ord.rs:21:24
++   |
++LL |     let _not_greater = !(a_value > another_value);
++   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
++  --> $DIR/neg_cmp_op_on_partial_ord.rs:24:33
++   |
++LL |     let _not_greater_or_equal = !(a_value >= another_value);
++   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d4a20ce9db1c843a3419ca44107a1aaf5f52dacd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++#![warn(clippy::neg_multiply)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation)]
++
++use std::ops::Mul;
++
++struct X;
++
++impl Mul<isize> for X {
++    type Output = X;
++
++    fn mul(self, _r: isize) -> Self {
++        self
++    }
++}
++
++impl Mul<X> for isize {
++    type Output = X;
++
++    fn mul(self, _r: X) -> X {
++        X
++    }
++}
++
++fn main() {
++    let x = 0;
++
++    x * -1;
++
++    -1 * x;
++
++    -1 * -1; // should be ok
++
++    X * -1; // should be ok
++    -1 * X; // should also be ok
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f08bbd6a12c597e4b0912db1a23cbe0bd8bf9bd4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: Negation by multiplying with `-1`
++  --> $DIR/neg_multiply.rs:27:5
++   |
++LL |     x * -1;
++   |     ^^^^^^
++   |
++   = note: `-D clippy::neg-multiply` implied by `-D warnings`
++
++error: Negation by multiplying with `-1`
++  --> $DIR/neg_multiply.rs:29:5
++   |
++LL |     -1 * x;
++   |     ^^^^^^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cbc4ca39161681f3edd71b684a9a91a2d8722d0b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,204 @@@
++#![allow(
++    clippy::single_match,
++    unused_assignments,
++    unused_variables,
++    clippy::while_immutable_condition
++)]
++
++fn test1() {
++    let mut x = 0;
++    loop {
++        // clippy::never_loop
++        x += 1;
++        if x == 1 {
++            return;
++        }
++        break;
++    }
++}
++
++fn test2() {
++    let mut x = 0;
++    loop {
++        x += 1;
++        if x == 1 {
++            break;
++        }
++    }
++}
++
++fn test3() {
++    let mut x = 0;
++    loop {
++        // never loops
++        x += 1;
++        break;
++    }
++}
++
++fn test4() {
++    let mut x = 1;
++    loop {
++        x += 1;
++        match x {
++            5 => return,
++            _ => (),
++        }
++    }
++}
++
++fn test5() {
++    let i = 0;
++    loop {
++        // never loops
++        while i == 0 {
++            // never loops
++            break;
++        }
++        return;
++    }
++}
++
++fn test6() {
++    let mut x = 0;
++    'outer: loop {
++        x += 1;
++        loop {
++            // never loops
++            if x == 5 {
++                break;
++            }
++            continue 'outer;
++        }
++        return;
++    }
++}
++
++fn test7() {
++    let mut x = 0;
++    loop {
++        x += 1;
++        match x {
++            1 => continue,
++            _ => (),
++        }
++        return;
++    }
++}
++
++fn test8() {
++    let mut x = 0;
++    loop {
++        x += 1;
++        match x {
++            5 => return,
++            _ => continue,
++        }
++    }
++}
++
++fn test9() {
++    let x = Some(1);
++    while let Some(y) = x {
++        // never loops
++        return;
++    }
++}
++
++fn test10() {
++    for x in 0..10 {
++        // never loops
++        match x {
++            1 => break,
++            _ => return,
++        }
++    }
++}
++
++fn test11<F: FnMut() -> i32>(mut f: F) {
++    loop {
++        return match f() {
++            1 => continue,
++            _ => (),
++        };
++    }
++}
++
++pub fn test12(a: bool, b: bool) {
++    'label: loop {
++        loop {
++            if a {
++                continue 'label;
++            }
++            if b {
++                break;
++            }
++        }
++        break;
++    }
++}
++
++pub fn test13() {
++    let mut a = true;
++    loop {
++        // infinite loop
++        while a {
++            if true {
++                a = false;
++                continue;
++            }
++            return;
++        }
++    }
++}
++
++pub fn test14() {
++    let mut a = true;
++    'outer: while a {
++        // never loops
++        while a {
++            if a {
++                a = false;
++                continue;
++            }
++        }
++        break 'outer;
++    }
++}
++
++// Issue #1991: the outter loop should not warn.
++pub fn test15() {
++    'label: loop {
++        while false {
++            break 'label;
++        }
++    }
++}
++
++// Issue #4058: `continue` in `break` expression
++pub fn test16() {
++    let mut n = 1;
++    loop {
++        break if n != 5 {
++            n += 1;
++            continue;
++        };
++    }
++}
++
++fn main() {
++    test1();
++    test2();
++    test3();
++    test4();
++    test5();
++    test6();
++    test7();
++    test8();
++    test9();
++    test10();
++    test11(|| 0);
++    test12(true, false);
++    test13();
++    test14();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c00b4c78cf28b49182ec4441d84bcc9b10230ea9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,100 @@@
++error: this loop never actually loops
++  --> $DIR/never_loop.rs:10:5
++   |
++LL | /     loop {
++LL | |         // clippy::never_loop
++LL | |         x += 1;
++LL | |         if x == 1 {
++...  |
++LL | |         break;
++LL | |     }
++   | |_____^
++   |
++   = note: `#[deny(clippy::never_loop)]` on by default
++
++error: this loop never actually loops
++  --> $DIR/never_loop.rs:32:5
++   |
++LL | /     loop {
++LL | |         // never loops
++LL | |         x += 1;
++LL | |         break;
++LL | |     }
++   | |_____^
++
++error: this loop never actually loops
++  --> $DIR/never_loop.rs:52:5
++   |
++LL | /     loop {
++LL | |         // never loops
++LL | |         while i == 0 {
++LL | |             // never loops
++...  |
++LL | |         return;
++LL | |     }
++   | |_____^
++
++error: this loop never actually loops
++  --> $DIR/never_loop.rs:54:9
++   |
++LL | /         while i == 0 {
++LL | |             // never loops
++LL | |             break;
++LL | |         }
++   | |_________^
++
++error: this loop never actually loops
++  --> $DIR/never_loop.rs:66:9
++   |
++LL | /         loop {
++LL | |             // never loops
++LL | |             if x == 5 {
++LL | |                 break;
++LL | |             }
++LL | |             continue 'outer;
++LL | |         }
++   | |_________^
++
++error: this loop never actually loops
++  --> $DIR/never_loop.rs:102:5
++   |
++LL | /     while let Some(y) = x {
++LL | |         // never loops
++LL | |         return;
++LL | |     }
++   | |_____^
++
++error: this loop never actually loops
++  --> $DIR/never_loop.rs:109:5
++   |
++LL | /     for x in 0..10 {
++LL | |         // never loops
++LL | |         match x {
++LL | |             1 => break,
++LL | |             _ => return,
++LL | |         }
++LL | |     }
++   | |_____^
++
++error: this loop never actually loops
++  --> $DIR/never_loop.rs:157:5
++   |
++LL | /     'outer: while a {
++LL | |         // never loops
++LL | |         while a {
++LL | |             if a {
++...  |
++LL | |         break 'outer;
++LL | |     }
++   | |_____^
++
++error: this loop never actually loops
++  --> $DIR/never_loop.rs:172:9
++   |
++LL | /         while false {
++LL | |             break 'label;
++LL | |         }
++   | |_________^
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2c2d1e275893fae41e3965c9c4568f2afad8c6e8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,212 @@@
++#![warn(clippy::new_ret_no_self)]
++#![allow(dead_code)]
++
++fn main() {}
++
++trait R {
++    type Item;
++}
++
++trait Q {
++    type Item;
++    type Item2;
++}
++
++struct S;
++
++impl R for S {
++    type Item = Self;
++}
++
++impl S {
++    // should not trigger the lint
++    pub fn new() -> impl R<Item = Self> {
++        S
++    }
++}
++
++struct S2;
++
++impl R for S2 {
++    type Item = Self;
++}
++
++impl S2 {
++    // should not trigger the lint
++    pub fn new(_: String) -> impl R<Item = Self> {
++        S2
++    }
++}
++
++struct S3;
++
++impl R for S3 {
++    type Item = u32;
++}
++
++impl S3 {
++    // should trigger the lint
++    pub fn new(_: String) -> impl R<Item = u32> {
++        S3
++    }
++}
++
++struct S4;
++
++impl Q for S4 {
++    type Item = u32;
++    type Item2 = Self;
++}
++
++impl S4 {
++    // should not trigger the lint
++    pub fn new(_: String) -> impl Q<Item = u32, Item2 = Self> {
++        S4
++    }
++}
++
++struct T;
++
++impl T {
++    // should not trigger lint
++    pub fn new() -> Self {
++        unimplemented!();
++    }
++}
++
++struct U;
++
++impl U {
++    // should trigger lint
++    pub fn new() -> u32 {
++        unimplemented!();
++    }
++}
++
++struct V;
++
++impl V {
++    // should trigger lint
++    pub fn new(_: String) -> u32 {
++        unimplemented!();
++    }
++}
++
++struct TupleReturnerOk;
++
++impl TupleReturnerOk {
++    // should not trigger lint
++    pub fn new() -> (Self, u32) {
++        unimplemented!();
++    }
++}
++
++struct TupleReturnerOk2;
++
++impl TupleReturnerOk2 {
++    // should not trigger lint (it doesn't matter which element in the tuple is Self)
++    pub fn new() -> (u32, Self) {
++        unimplemented!();
++    }
++}
++
++struct TupleReturnerOk3;
++
++impl TupleReturnerOk3 {
++    // should not trigger lint (tuple can contain multiple Self)
++    pub fn new() -> (Self, Self) {
++        unimplemented!();
++    }
++}
++
++struct TupleReturnerBad;
++
++impl TupleReturnerBad {
++    // should trigger lint
++    pub fn new() -> (u32, u32) {
++        unimplemented!();
++    }
++}
++
++struct MutPointerReturnerOk;
++
++impl MutPointerReturnerOk {
++    // should not trigger lint
++    pub fn new() -> *mut Self {
++        unimplemented!();
++    }
++}
++
++struct MutPointerReturnerOk2;
++
++impl MutPointerReturnerOk2 {
++    // should not trigger lint
++    pub fn new() -> *const Self {
++        unimplemented!();
++    }
++}
++
++struct MutPointerReturnerBad;
++
++impl MutPointerReturnerBad {
++    // should trigger lint
++    pub fn new() -> *mut V {
++        unimplemented!();
++    }
++}
++
++struct GenericReturnerOk;
++
++impl GenericReturnerOk {
++    // should not trigger lint
++    pub fn new() -> Option<Self> {
++        unimplemented!();
++    }
++}
++
++struct GenericReturnerBad;
++
++impl GenericReturnerBad {
++    // should trigger lint
++    pub fn new() -> Option<u32> {
++        unimplemented!();
++    }
++}
++
++struct NestedReturnerOk;
++
++impl NestedReturnerOk {
++    // should not trigger lint
++    pub fn new() -> (Option<Self>, u32) {
++        unimplemented!();
++    }
++}
++
++struct NestedReturnerOk2;
++
++impl NestedReturnerOk2 {
++    // should not trigger lint
++    pub fn new() -> ((Self, u32), u32) {
++        unimplemented!();
++    }
++}
++
++struct NestedReturnerOk3;
++
++impl NestedReturnerOk3 {
++    // should not trigger lint
++    pub fn new() -> Option<(Self, u32)> {
++        unimplemented!();
++    }
++}
++
++struct WithLifetime<'a> {
++    cat: &'a str,
++}
++
++impl<'a> WithLifetime<'a> {
++    // should not trigger the lint, because the lifetimes are different
++    pub fn new<'b: 'a>(s: &'b str) -> WithLifetime<'b> {
++        unimplemented!();
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dd5a24bcbe7aed4c1ba4b792942ecac8f8cf9ea2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++error: methods called `new` usually return `Self`
++  --> $DIR/new_ret_no_self.rs:49:5
++   |
++LL | /     pub fn new(_: String) -> impl R<Item = u32> {
++LL | |         S3
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::new-ret-no-self` implied by `-D warnings`
++
++error: methods called `new` usually return `Self`
++  --> $DIR/new_ret_no_self.rs:81:5
++   |
++LL | /     pub fn new() -> u32 {
++LL | |         unimplemented!();
++LL | |     }
++   | |_____^
++
++error: methods called `new` usually return `Self`
++  --> $DIR/new_ret_no_self.rs:90:5
++   |
++LL | /     pub fn new(_: String) -> u32 {
++LL | |         unimplemented!();
++LL | |     }
++   | |_____^
++
++error: methods called `new` usually return `Self`
++  --> $DIR/new_ret_no_self.rs:126:5
++   |
++LL | /     pub fn new() -> (u32, u32) {
++LL | |         unimplemented!();
++LL | |     }
++   | |_____^
++
++error: methods called `new` usually return `Self`
++  --> $DIR/new_ret_no_self.rs:153:5
++   |
++LL | /     pub fn new() -> *mut V {
++LL | |         unimplemented!();
++LL | |     }
++   | |_____^
++
++error: methods called `new` usually return `Self`
++  --> $DIR/new_ret_no_self.rs:171:5
++   |
++LL | /     pub fn new() -> Option<u32> {
++LL | |         unimplemented!();
++LL | |     }
++   | |_____^
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..781ea7bb152836eda343116409d8c84406266540
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,151 @@@
++#![feature(const_fn)]
++#![allow(dead_code, clippy::missing_safety_doc)]
++#![warn(clippy::new_without_default)]
++
++pub struct Foo;
++
++impl Foo {
++    pub fn new() -> Foo {
++        Foo
++    }
++}
++
++pub struct Bar;
++
++impl Bar {
++    pub fn new() -> Self {
++        Bar
++    }
++}
++
++pub struct Ok;
++
++impl Ok {
++    pub fn new() -> Self {
++        Ok
++    }
++}
++
++impl Default for Ok {
++    fn default() -> Self {
++        Ok
++    }
++}
++
++pub struct Params;
++
++impl Params {
++    pub fn new(_: u32) -> Self {
++        Params
++    }
++}
++
++pub struct GenericsOk<T> {
++    bar: T,
++}
++
++impl<U> Default for GenericsOk<U> {
++    fn default() -> Self {
++        unimplemented!();
++    }
++}
++
++impl<'c, V> GenericsOk<V> {
++    pub fn new() -> GenericsOk<V> {
++        unimplemented!()
++    }
++}
++
++pub struct LtOk<'a> {
++    foo: &'a bool,
++}
++
++impl<'b> Default for LtOk<'b> {
++    fn default() -> Self {
++        unimplemented!();
++    }
++}
++
++impl<'c> LtOk<'c> {
++    pub fn new() -> LtOk<'c> {
++        unimplemented!()
++    }
++}
++
++pub struct LtKo<'a> {
++    foo: &'a bool,
++}
++
++impl<'c> LtKo<'c> {
++    pub fn new() -> LtKo<'c> {
++        unimplemented!()
++    }
++    // FIXME: that suggestion is missing lifetimes
++}
++
++struct Private;
++
++impl Private {
++    fn new() -> Private {
++        unimplemented!()
++    } // We don't lint private items
++}
++
++struct Const;
++
++impl Const {
++    pub const fn new() -> Const {
++        Const
++    } // const fns can't be implemented via Default
++}
++
++pub struct IgnoreGenericNew;
++
++impl IgnoreGenericNew {
++    pub fn new<T>() -> Self {
++        IgnoreGenericNew
++    } // the derived Default does not make sense here as the result depends on T
++}
++
++pub trait TraitWithNew: Sized {
++    fn new() -> Self {
++        panic!()
++    }
++}
++
++pub struct IgnoreUnsafeNew;
++
++impl IgnoreUnsafeNew {
++    pub unsafe fn new() -> Self {
++        IgnoreUnsafeNew
++    }
++}
++
++#[derive(Default)]
++pub struct OptionRefWrapper<'a, T>(Option<&'a T>);
++
++impl<'a, T> OptionRefWrapper<'a, T> {
++    pub fn new() -> Self {
++        OptionRefWrapper(None)
++    }
++}
++
++pub struct Allow(Foo);
++
++impl Allow {
++    #[allow(clippy::new_without_default)]
++    pub fn new() -> Self {
++        unimplemented!()
++    }
++}
++
++pub struct AllowDerive;
++
++impl AllowDerive {
++    #[allow(clippy::new_without_default)]
++    pub fn new() -> Self {
++        unimplemented!()
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5e485d40663f3e9ffb3540ec912a470ed2cffa3f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++error: you should consider deriving a `Default` implementation for `Foo`
++  --> $DIR/new_without_default.rs:8:5
++   |
++LL | /     pub fn new() -> Foo {
++LL | |         Foo
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::new-without-default` implied by `-D warnings`
++help: try this
++   |
++LL | #[derive(Default)]
++   |
++
++error: you should consider deriving a `Default` implementation for `Bar`
++  --> $DIR/new_without_default.rs:16:5
++   |
++LL | /     pub fn new() -> Self {
++LL | |         Bar
++LL | |     }
++   | |_____^
++   |
++help: try this
++   |
++LL | #[derive(Default)]
++   |
++
++error: you should consider adding a `Default` implementation for `LtKo<'c>`
++  --> $DIR/new_without_default.rs:80:5
++   |
++LL | /     pub fn new() -> LtKo<'c> {
++LL | |         unimplemented!()
++LL | |     }
++   | |_____^
++   |
++help: try this
++   |
++LL | impl Default for LtKo<'c> {
++LL |     fn default() -> Self {
++LL |         Self::new()
++LL |     }
++LL | }
++   |
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8fbfcb79860a58aa889b872a4c1b663bcf33ed64
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,102 @@@
++#![feature(box_syntax)]
++#![warn(clippy::no_effect)]
++#![allow(dead_code)]
++#![allow(path_statements)]
++#![allow(clippy::deref_addrof)]
++#![allow(clippy::redundant_field_names)]
++#![feature(untagged_unions)]
++
++struct Unit;
++struct Tuple(i32);
++struct Struct {
++    field: i32,
++}
++enum Enum {
++    Tuple(i32),
++    Struct { field: i32 },
++}
++struct DropUnit;
++impl Drop for DropUnit {
++    fn drop(&mut self) {}
++}
++struct DropStruct {
++    field: i32,
++}
++impl Drop for DropStruct {
++    fn drop(&mut self) {}
++}
++struct DropTuple(i32);
++impl Drop for DropTuple {
++    fn drop(&mut self) {}
++}
++enum DropEnum {
++    Tuple(i32),
++    Struct { field: i32 },
++}
++impl Drop for DropEnum {
++    fn drop(&mut self) {}
++}
++struct FooString {
++    s: String,
++}
++union Union {
++    a: u8,
++    b: f64,
++}
++
++fn get_number() -> i32 {
++    0
++}
++fn get_struct() -> Struct {
++    Struct { field: 0 }
++}
++fn get_drop_struct() -> DropStruct {
++    DropStruct { field: 0 }
++}
++
++unsafe fn unsafe_fn() -> i32 {
++    0
++}
++
++fn main() {
++    let s = get_struct();
++    let s2 = get_struct();
++
++    0;
++    s2;
++    Unit;
++    Tuple(0);
++    Struct { field: 0 };
++    Struct { ..s };
++    Union { a: 0 };
++    Enum::Tuple(0);
++    Enum::Struct { field: 0 };
++    5 + 6;
++    *&42;
++    &6;
++    (5, 6, 7);
++    box 42;
++    ..;
++    5..;
++    ..5;
++    5..6;
++    5..=6;
++    [42, 55];
++    [42, 55][1];
++    (42, 55).1;
++    [42; 55];
++    [42; 55][13];
++    let mut x = 0;
++    || x += 5;
++    let s: String = "foo".into();
++    FooString { s: s };
++
++    // Do not warn
++    get_number();
++    unsafe { unsafe_fn() };
++    DropUnit;
++    DropStruct { field: 0 };
++    DropTuple(0);
++    DropEnum::Tuple(0);
++    DropEnum::Struct { field: 0 };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..834b9056e311df0c0847e6c2e7afb79f6647d9f5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,154 @@@
++error: statement with no effect
++  --> $DIR/no_effect.rs:65:5
++   |
++LL |     0;
++   |     ^^
++   |
++   = note: `-D clippy::no-effect` implied by `-D warnings`
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:66:5
++   |
++LL |     s2;
++   |     ^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:67:5
++   |
++LL |     Unit;
++   |     ^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:68:5
++   |
++LL |     Tuple(0);
++   |     ^^^^^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:69:5
++   |
++LL |     Struct { field: 0 };
++   |     ^^^^^^^^^^^^^^^^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:70:5
++   |
++LL |     Struct { ..s };
++   |     ^^^^^^^^^^^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:71:5
++   |
++LL |     Union { a: 0 };
++   |     ^^^^^^^^^^^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:72:5
++   |
++LL |     Enum::Tuple(0);
++   |     ^^^^^^^^^^^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:73:5
++   |
++LL |     Enum::Struct { field: 0 };
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:74:5
++   |
++LL |     5 + 6;
++   |     ^^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:75:5
++   |
++LL |     *&42;
++   |     ^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:76:5
++   |
++LL |     &6;
++   |     ^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:77:5
++   |
++LL |     (5, 6, 7);
++   |     ^^^^^^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:78:5
++   |
++LL |     box 42;
++   |     ^^^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:79:5
++   |
++LL |     ..;
++   |     ^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:80:5
++   |
++LL |     5..;
++   |     ^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:81:5
++   |
++LL |     ..5;
++   |     ^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:82:5
++   |
++LL |     5..6;
++   |     ^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:84:5
++   |
++LL |     [42, 55];
++   |     ^^^^^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:85:5
++   |
++LL |     [42, 55][1];
++   |     ^^^^^^^^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:86:5
++   |
++LL |     (42, 55).1;
++   |     ^^^^^^^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:87:5
++   |
++LL |     [42; 55];
++   |     ^^^^^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:88:5
++   |
++LL |     [42; 55][13];
++   |     ^^^^^^^^^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:90:5
++   |
++LL |     || x += 5;
++   |     ^^^^^^^^^^
++
++error: statement with no effect
++  --> $DIR/no_effect.rs:92:5
++   |
++LL |     FooString { s: s };
++   |     ^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 25 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..58415b4aede260b668dbd3db7a0f9a56bc56ecd8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++#![warn(clippy::all)]
++#![allow(unused, clippy::println_empty_string)]
++
++#[derive(Clone, Debug)]
++enum MaybeInst {
++    Split,
++    Split1(usize),
++    Split2(usize),
++}
++
++struct InstSplit {
++    uiae: usize,
++}
++
++impl MaybeInst {
++    fn fill(&mut self) {
++        let filled = match *self {
++            MaybeInst::Split1(goto1) => panic!(1),
++            MaybeInst::Split2(goto2) => panic!(2),
++            _ => unimplemented!(),
++        };
++        unimplemented!()
++    }
++}
++
++fn underscores_and_numbers() {
++    let _1 = 1; //~ERROR Consider a more descriptive name
++    let ____1 = 1; //~ERROR Consider a more descriptive name
++    let __1___2 = 12; //~ERROR Consider a more descriptive name
++    let _1_ok = 1;
++}
++
++fn issue2927() {
++    let args = 1;
++    format!("{:?}", 2);
++}
++
++fn issue3078() {
++    match "a" {
++        stringify!(a) => {},
++        _ => {},
++    }
++}
++
++struct Bar;
++
++impl Bar {
++    fn bar() {
++        let _1 = 1;
++        let ____1 = 1;
++        let __1___2 = 12;
++        let _1_ok = 1;
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a0ca46f0efc602cd0a6bccc6c35b48dee5697e5e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: consider choosing a more descriptive name
++  --> $DIR/non_expressive_names.rs:27:9
++   |
++LL |     let _1 = 1; //~ERROR Consider a more descriptive name
++   |         ^^
++   |
++   = note: `-D clippy::just-underscores-and-digits` implied by `-D warnings`
++
++error: consider choosing a more descriptive name
++  --> $DIR/non_expressive_names.rs:28:9
++   |
++LL |     let ____1 = 1; //~ERROR Consider a more descriptive name
++   |         ^^^^^
++
++error: consider choosing a more descriptive name
++  --> $DIR/non_expressive_names.rs:29:9
++   |
++LL |     let __1___2 = 12; //~ERROR Consider a more descriptive name
++   |         ^^^^^^^
++
++error: consider choosing a more descriptive name
++  --> $DIR/non_expressive_names.rs:49:13
++   |
++LL |         let _1 = 1;
++   |             ^^
++
++error: consider choosing a more descriptive name
++  --> $DIR/non_expressive_names.rs:50:13
++   |
++LL |         let ____1 = 1;
++   |             ^^^^^
++
++error: consider choosing a more descriptive name
++  --> $DIR/non_expressive_names.rs:51:13
++   |
++LL |         let __1___2 = 12;
++   |             ^^^^^^^
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644 (file)
--- /dev/null
--- /dev/null
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7ea154cb9b0187346be5e41da300559a6050866d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++#![allow(unused, clippy::many_single_char_names)]
++#![warn(clippy::nonminimal_bool)]
++
++fn main() {
++    let a: bool = unimplemented!();
++    let b: bool = unimplemented!();
++    let c: bool = unimplemented!();
++    let d: bool = unimplemented!();
++    let e: bool = unimplemented!();
++    let _ = !true;
++    let _ = !false;
++    let _ = !!a;
++    let _ = false || a;
++    // don't lint on cfgs
++    let _ = cfg!(you_shall_not_not_pass) && a;
++    let _ = a || !b || !c || !d || !e;
++    let _ = !(!a && b);
++    let _ = !(!a || b);
++    let _ = !a && !(b && c);
++}
++
++fn equality_stuff() {
++    let a: i32 = unimplemented!();
++    let b: i32 = unimplemented!();
++    let c: i32 = unimplemented!();
++    let d: i32 = unimplemented!();
++    let _ = a == b && c == 5 && a == b;
++    let _ = a == b || c == 5 || a == b;
++    let _ = a == b && c == 5 && b == a;
++    let _ = a != b || !(a != b || c == d);
++    let _ = a != b && !(a != b && c == d);
++}
++
++fn issue3847(a: u32, b: u32) -> bool {
++    const THRESHOLD: u32 = 1_000;
++
++    if a < THRESHOLD && b >= THRESHOLD || a >= THRESHOLD && b < THRESHOLD {
++        return false;
++    }
++    true
++}
++
++fn issue4548() {
++    fn f(_i: u32, _j: u32) -> u32 {
++        unimplemented!();
++    }
++
++    let i = 0;
++    let j = 0;
++
++    if i != j && f(i, j) != 0 || i == j && f(i, j) != 1 {}
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d34d106cb2fbb1a11d245224b2ad622a287c8f8b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,111 @@@
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool.rs:10:13
++   |
++LL |     let _ = !true;
++   |             ^^^^^ help: try: `false`
++   |
++   = note: `-D clippy::nonminimal-bool` implied by `-D warnings`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool.rs:11:13
++   |
++LL |     let _ = !false;
++   |             ^^^^^^ help: try: `true`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool.rs:12:13
++   |
++LL |     let _ = !!a;
++   |             ^^^ help: try: `a`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool.rs:13:13
++   |
++LL |     let _ = false || a;
++   |             ^^^^^^^^^^ help: try: `a`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool.rs:17:13
++   |
++LL |     let _ = !(!a && b);
++   |             ^^^^^^^^^^ help: try: `a || !b`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool.rs:18:13
++   |
++LL |     let _ = !(!a || b);
++   |             ^^^^^^^^^^ help: try: `a && !b`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool.rs:19:13
++   |
++LL |     let _ = !a && !(b && c);
++   |             ^^^^^^^^^^^^^^^ help: try: `!(a || b && c)`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool.rs:27:13
++   |
++LL |     let _ = a == b && c == 5 && a == b;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: try
++   |
++LL |     let _ = a == b && c == 5;
++   |             ^^^^^^^^^^^^^^^^
++LL |     let _ = !(a != b || c != 5);
++   |             ^^^^^^^^^^^^^^^^^^^
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool.rs:28:13
++   |
++LL |     let _ = a == b || c == 5 || a == b;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: try
++   |
++LL |     let _ = a == b || c == 5;
++   |             ^^^^^^^^^^^^^^^^
++LL |     let _ = !(a != b && c != 5);
++   |             ^^^^^^^^^^^^^^^^^^^
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool.rs:29:13
++   |
++LL |     let _ = a == b && c == 5 && b == a;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: try
++   |
++LL |     let _ = a == b && c == 5;
++   |             ^^^^^^^^^^^^^^^^
++LL |     let _ = !(a != b || c != 5);
++   |             ^^^^^^^^^^^^^^^^^^^
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool.rs:30:13
++   |
++LL |     let _ = a != b || !(a != b || c == d);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: try
++   |
++LL |     let _ = a != b || c != d;
++   |             ^^^^^^^^^^^^^^^^
++LL |     let _ = !(a == b && c == d);
++   |             ^^^^^^^^^^^^^^^^^^^
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool.rs:31:13
++   |
++LL |     let _ = a != b && !(a != b && c == d);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: try
++   |
++LL |     let _ = a != b && c != d;
++   |             ^^^^^^^^^^^^^^^^
++LL |     let _ = !(a == b || c == d);
++   |             ^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 12 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4de48cd0879a1cda58502936a3533b149567d379
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,110 @@@
++#![allow(unused, clippy::many_single_char_names)]
++#![warn(clippy::nonminimal_bool)]
++
++fn methods_with_negation() {
++    let a: Option<i32> = unimplemented!();
++    let b: Result<i32, i32> = unimplemented!();
++    let _ = a.is_some();
++    let _ = !a.is_some();
++    let _ = a.is_none();
++    let _ = !a.is_none();
++    let _ = b.is_err();
++    let _ = !b.is_err();
++    let _ = b.is_ok();
++    let _ = !b.is_ok();
++    let c = false;
++    let _ = !(a.is_some() && !c);
++    let _ = !(a.is_some() || !c);
++    let _ = !(!c ^ c) || !a.is_some();
++    let _ = (!c ^ c) || !a.is_some();
++    let _ = !c ^ c || !a.is_some();
++}
++
++// Simplified versions of https://github.com/rust-lang/rust-clippy/issues/2638
++// clippy::nonminimal_bool should only check the built-in Result and Some type, not
++// any other types like the following.
++enum CustomResultOk<E> {
++    Ok,
++    Err(E),
++}
++enum CustomResultErr<E> {
++    Ok,
++    Err(E),
++}
++enum CustomSomeSome<T> {
++    Some(T),
++    None,
++}
++enum CustomSomeNone<T> {
++    Some(T),
++    None,
++}
++
++impl<E> CustomResultOk<E> {
++    pub fn is_ok(&self) -> bool {
++        true
++    }
++}
++
++impl<E> CustomResultErr<E> {
++    pub fn is_err(&self) -> bool {
++        true
++    }
++}
++
++impl<T> CustomSomeSome<T> {
++    pub fn is_some(&self) -> bool {
++        true
++    }
++}
++
++impl<T> CustomSomeNone<T> {
++    pub fn is_none(&self) -> bool {
++        true
++    }
++}
++
++fn dont_warn_for_custom_methods_with_negation() {
++    let res = CustomResultOk::Err("Error");
++    // Should not warn and suggest 'is_err()' because the type does not
++    // implement is_err().
++    if !res.is_ok() {}
++
++    let res = CustomResultErr::Err("Error");
++    // Should not warn and suggest 'is_ok()' because the type does not
++    // implement is_ok().
++    if !res.is_err() {}
++
++    let res = CustomSomeSome::Some("thing");
++    // Should not warn and suggest 'is_none()' because the type does not
++    // implement is_none().
++    if !res.is_some() {}
++
++    let res = CustomSomeNone::Some("thing");
++    // Should not warn and suggest 'is_some()' because the type does not
++    // implement is_some().
++    if !res.is_none() {}
++}
++
++// Only Built-in Result and Some types should suggest the negated alternative
++fn warn_for_built_in_methods_with_negation() {
++    let res: Result<usize, usize> = Ok(1);
++    if !res.is_ok() {}
++    if !res.is_err() {}
++
++    let res = Some(1);
++    if !res.is_some() {}
++    if !res.is_none() {}
++}
++
++#[allow(clippy::neg_cmp_op_on_partial_ord)]
++fn dont_warn_for_negated_partial_ord_comparison() {
++    let a: f64 = unimplemented!();
++    let b: f64 = unimplemented!();
++    let _ = !(a < b);
++    let _ = !(a <= b);
++    let _ = !(a > b);
++    let _ = !(a >= b);
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a2df889d6230215dd44547e788991a47e61f6782
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,82 @@@
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool_methods.rs:8:13
++   |
++LL |     let _ = !a.is_some();
++   |             ^^^^^^^^^^^^ help: try: `a.is_none()`
++   |
++   = note: `-D clippy::nonminimal-bool` implied by `-D warnings`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool_methods.rs:10:13
++   |
++LL |     let _ = !a.is_none();
++   |             ^^^^^^^^^^^^ help: try: `a.is_some()`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool_methods.rs:12:13
++   |
++LL |     let _ = !b.is_err();
++   |             ^^^^^^^^^^^ help: try: `b.is_ok()`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool_methods.rs:14:13
++   |
++LL |     let _ = !b.is_ok();
++   |             ^^^^^^^^^^ help: try: `b.is_err()`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool_methods.rs:16:13
++   |
++LL |     let _ = !(a.is_some() && !c);
++   |             ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() || c`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool_methods.rs:17:13
++   |
++LL |     let _ = !(a.is_some() || !c);
++   |             ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() && c`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool_methods.rs:18:26
++   |
++LL |     let _ = !(!c ^ c) || !a.is_some();
++   |                          ^^^^^^^^^^^^ help: try: `a.is_none()`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool_methods.rs:19:25
++   |
++LL |     let _ = (!c ^ c) || !a.is_some();
++   |                         ^^^^^^^^^^^^ help: try: `a.is_none()`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool_methods.rs:20:23
++   |
++LL |     let _ = !c ^ c || !a.is_some();
++   |                       ^^^^^^^^^^^^ help: try: `a.is_none()`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool_methods.rs:92:8
++   |
++LL |     if !res.is_ok() {}
++   |        ^^^^^^^^^^^^ help: try: `res.is_err()`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool_methods.rs:93:8
++   |
++LL |     if !res.is_err() {}
++   |        ^^^^^^^^^^^^^ help: try: `res.is_ok()`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool_methods.rs:96:8
++   |
++LL |     if !res.is_some() {}
++   |        ^^^^^^^^^^^^^^ help: try: `res.is_none()`
++
++error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool_methods.rs:97:8
++   |
++LL |     if !res.is_none() {}
++   |        ^^^^^^^^^^^^^^ help: try: `res.is_some()`
++
++error: aborting due to 13 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ff68d38c73bf52a1feae44a01d06e63937f2748b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++use std::io;
++
++struct MyError(()); // doesn't implement Debug
++
++#[derive(Debug)]
++struct MyErrorWithParam<T> {
++    x: T,
++}
++
++fn main() {
++    let res: Result<i32, ()> = Ok(0);
++    let _ = res.unwrap();
++
++    res.ok().expect("disaster!");
++    // the following should not warn, since `expect` isn't implemented unless
++    // the error type implements `Debug`
++    let res2: Result<i32, MyError> = Ok(0);
++    res2.ok().expect("oh noes!");
++    let res3: Result<u32, MyErrorWithParam<u8>> = Ok(0);
++    res3.ok().expect("whoof");
++    let res4: Result<u32, io::Error> = Ok(0);
++    res4.ok().expect("argh");
++    let res5: io::Result<u32> = Ok(0);
++    res5.ok().expect("oops");
++    let res6: Result<u32, &str> = Ok(0);
++    res6.ok().expect("meh");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b02b28e7f68c8a5e8bb1def1e2dd8a2c9722a8e3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++error: called `ok().expect()` on a `Result` value
++  --> $DIR/ok_expect.rs:14:5
++   |
++LL |     res.ok().expect("disaster!");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::ok-expect` implied by `-D warnings`
++   = help: you can call `expect()` directly on the `Result`
++
++error: called `ok().expect()` on a `Result` value
++  --> $DIR/ok_expect.rs:20:5
++   |
++LL |     res3.ok().expect("whoof");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: you can call `expect()` directly on the `Result`
++
++error: called `ok().expect()` on a `Result` value
++  --> $DIR/ok_expect.rs:22:5
++   |
++LL |     res4.ok().expect("argh");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: you can call `expect()` directly on the `Result`
++
++error: called `ok().expect()` on a `Result` value
++  --> $DIR/ok_expect.rs:24:5
++   |
++LL |     res5.ok().expect("oops");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: you can call `expect()` directly on the `Result`
++
++error: called `ok().expect()` on a `Result` value
++  --> $DIR/ok_expect.rs:26:5
++   |
++LL |     res6.ok().expect("meh");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: you can call `expect()` directly on the `Result`
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6605c967c8e7e6323d659f0a7c645e0cd59832c2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++#![allow(unused_variables, clippy::blacklisted_name)]
++#![warn(clippy::op_ref)]
++#![allow(clippy::many_single_char_names)]
++use std::collections::HashSet;
++use std::ops::BitAnd;
++
++fn main() {
++    let tracked_fds: HashSet<i32> = HashSet::new();
++    let new_fds = HashSet::new();
++    let unwanted = &tracked_fds - &new_fds;
++
++    let foo = &5 - &6;
++
++    let bar = String::new();
++    let bar = "foo" == &bar;
++
++    let a = "a".to_string();
++    let b = "a";
++
++    if b < &a {
++        println!("OK");
++    }
++
++    struct X(i32);
++    impl BitAnd for X {
++        type Output = X;
++        fn bitand(self, rhs: X) -> X {
++            X(self.0 & rhs.0)
++        }
++    }
++    impl<'a> BitAnd<&'a X> for X {
++        type Output = X;
++        fn bitand(self, rhs: &'a X) -> X {
++            X(self.0 & rhs.0)
++        }
++    }
++    let x = X(1);
++    let y = X(2);
++    let z = x & &y;
++
++    #[derive(Copy, Clone)]
++    struct Y(i32);
++    impl BitAnd for Y {
++        type Output = Y;
++        fn bitand(self, rhs: Y) -> Y {
++            Y(self.0 & rhs.0)
++        }
++    }
++    impl<'a> BitAnd<&'a Y> for Y {
++        type Output = Y;
++        fn bitand(self, rhs: &'a Y) -> Y {
++            Y(self.0 & rhs.0)
++        }
++    }
++    let x = Y(1);
++    let y = Y(2);
++    let z = x & &y;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a3a9adcc483551aa12d80c7423fa241625234e56
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: needlessly taken reference of both operands
++  --> $DIR/op_ref.rs:12:15
++   |
++LL |     let foo = &5 - &6;
++   |               ^^^^^^^
++   |
++   = note: `-D clippy::op-ref` implied by `-D warnings`
++help: use the values directly
++   |
++LL |     let foo = 5 - 6;
++   |               ^   ^
++
++error: taken reference of right operand
++  --> $DIR/op_ref.rs:57:13
++   |
++LL |     let z = x & &y;
++   |             ^^^^--
++   |                 |
++   |                 help: use the right value directly: `y`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9063fafbcd040a5c26f9cf043ffd30419f287071
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++use std::fs::OpenOptions;
++
++#[allow(unused_must_use)]
++#[warn(clippy::nonsensical_open_options)]
++fn main() {
++    OpenOptions::new().read(true).truncate(true).open("foo.txt");
++    OpenOptions::new().append(true).truncate(true).open("foo.txt");
++
++    OpenOptions::new().read(true).read(false).open("foo.txt");
++    OpenOptions::new().create(true).create(false).open("foo.txt");
++    OpenOptions::new().write(true).write(false).open("foo.txt");
++    OpenOptions::new().append(true).append(false).open("foo.txt");
++    OpenOptions::new().truncate(true).truncate(false).open("foo.txt");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..26fe9f6fb206f86433da3592d13f6adf30771c1d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++error: file opened with `truncate` and `read`
++  --> $DIR/open_options.rs:6:5
++   |
++LL |     OpenOptions::new().read(true).truncate(true).open("foo.txt");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::nonsensical-open-options` implied by `-D warnings`
++
++error: file opened with `append` and `truncate`
++  --> $DIR/open_options.rs:7:5
++   |
++LL |     OpenOptions::new().append(true).truncate(true).open("foo.txt");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the method `read` is called more than once
++  --> $DIR/open_options.rs:9:5
++   |
++LL |     OpenOptions::new().read(true).read(false).open("foo.txt");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the method `create` is called more than once
++  --> $DIR/open_options.rs:10:5
++   |
++LL |     OpenOptions::new().create(true).create(false).open("foo.txt");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the method `write` is called more than once
++  --> $DIR/open_options.rs:11:5
++   |
++LL |     OpenOptions::new().write(true).write(false).open("foo.txt");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the method `append` is called more than once
++  --> $DIR/open_options.rs:12:5
++   |
++LL |     OpenOptions::new().append(true).append(false).open("foo.txt");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the method `truncate` is called more than once
++  --> $DIR/open_options.rs:13:5
++   |
++LL |     OpenOptions::new().truncate(true).truncate(false).open("foo.txt");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..035bc1e54656b3281c1d322e99e85ed559a9c90f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++// run-rustfix
++#![deny(clippy::option_and_then_some)]
++
++// need a main anyway, use it get rid of unused warnings too
++pub fn main() {
++    let x = Some(5);
++    // the easiest cases
++    let _ = x;
++    let _ = x.map(|o| o + 1);
++    // and an easy counter-example
++    let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
++
++    // Different type
++    let x: Result<u32, &str> = Ok(1);
++    let _ = x.and_then(Ok);
++}
++
++pub fn foo() -> Option<String> {
++    let x = Some(String::from("hello"));
++    Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?)))
++}
++
++pub fn example2(x: bool) -> Option<&'static str> {
++    Some("a").and_then(|s| Some(if x { s } else { return None }))
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d49da7813c6e6bef944eec7038abf3cec79efb95
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++// run-rustfix
++#![deny(clippy::option_and_then_some)]
++
++// need a main anyway, use it get rid of unused warnings too
++pub fn main() {
++    let x = Some(5);
++    // the easiest cases
++    let _ = x.and_then(Some);
++    let _ = x.and_then(|o| Some(o + 1));
++    // and an easy counter-example
++    let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
++
++    // Different type
++    let x: Result<u32, &str> = Ok(1);
++    let _ = x.and_then(Ok);
++}
++
++pub fn foo() -> Option<String> {
++    let x = Some(String::from("hello"));
++    Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?)))
++}
++
++pub fn example2(x: bool) -> Option<&'static str> {
++    Some("a").and_then(|s| Some(if x { s } else { return None }))
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4782549176566e91af3710f83a50a59ea9e9f25c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++error: using `Option.and_then(Some)`, which is a no-op
++  --> $DIR/option_and_then_some.rs:8:13
++   |
++LL |     let _ = x.and_then(Some);
++   |             ^^^^^^^^^^^^^^^^ help: use the expression directly: `x`
++   |
++note: the lint level is defined here
++  --> $DIR/option_and_then_some.rs:2:9
++   |
++LL | #![deny(clippy::option_and_then_some)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`
++  --> $DIR/option_and_then_some.rs:9:13
++   |
++LL |     let _ = x.and_then(|o| Some(o + 1));
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.map(|o| o + 1)`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..076692e6445175da398e3ae788c803c5b57ba901
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++// run-rustfix
++
++#![allow(unused_imports, clippy::redundant_clone)]
++#![warn(clippy::option_as_ref_deref)]
++
++use std::ffi::{CString, OsString};
++use std::ops::{Deref, DerefMut};
++use std::path::PathBuf;
++
++fn main() {
++    let mut opt = Some(String::from("123"));
++
++    let _ = opt.clone().as_deref().map(str::len);
++
++    #[rustfmt::skip]
++    let _ = opt.clone().as_deref()
++        .map(str::len);
++
++    let _ = opt.as_deref_mut();
++
++    let _ = opt.as_deref();
++    let _ = opt.as_deref();
++    let _ = opt.as_deref_mut();
++    let _ = opt.as_deref_mut();
++    let _ = Some(CString::new(vec![]).unwrap()).as_deref();
++    let _ = Some(OsString::new()).as_deref();
++    let _ = Some(PathBuf::new()).as_deref();
++    let _ = Some(Vec::<()>::new()).as_deref();
++    let _ = Some(Vec::<()>::new()).as_deref_mut();
++
++    let _ = opt.as_deref();
++    let _ = opt.clone().as_deref_mut().map(|x| x.len());
++
++    let vc = vec![String::new()];
++    let _ = Some(1_usize).as_ref().map(|x| vc[*x].as_str()); // should not be linted
++
++    let _: Option<&str> = Some(&String::new()).as_ref().map(|x| x.as_str()); // should not be linted
++
++    let _ = opt.as_deref();
++    let _ = opt.as_deref_mut();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3bf5f715f8339525b8ad547d75eea77a96b0c1f2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++// run-rustfix
++
++#![allow(unused_imports, clippy::redundant_clone)]
++#![warn(clippy::option_as_ref_deref)]
++
++use std::ffi::{CString, OsString};
++use std::ops::{Deref, DerefMut};
++use std::path::PathBuf;
++
++fn main() {
++    let mut opt = Some(String::from("123"));
++
++    let _ = opt.clone().as_ref().map(Deref::deref).map(str::len);
++
++    #[rustfmt::skip]
++    let _ = opt.clone()
++        .as_ref().map(
++            Deref::deref
++        )
++        .map(str::len);
++
++    let _ = opt.as_mut().map(DerefMut::deref_mut);
++
++    let _ = opt.as_ref().map(String::as_str);
++    let _ = opt.as_ref().map(|x| x.as_str());
++    let _ = opt.as_mut().map(String::as_mut_str);
++    let _ = opt.as_mut().map(|x| x.as_mut_str());
++    let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str);
++    let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str);
++    let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path);
++    let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice);
++    let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice);
++
++    let _ = opt.as_ref().map(|x| x.deref());
++    let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len());
++
++    let vc = vec![String::new()];
++    let _ = Some(1_usize).as_ref().map(|x| vc[*x].as_str()); // should not be linted
++
++    let _: Option<&str> = Some(&String::new()).as_ref().map(|x| x.as_str()); // should not be linted
++
++    let _ = opt.as_ref().map(|x| &**x);
++    let _ = opt.as_mut().map(|x| &mut **x);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a106582a633232b4bfcdc95c78308440d2480137
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,104 @@@
++error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead
++  --> $DIR/option_as_ref_deref.rs:13:13
++   |
++LL |     let _ = opt.clone().as_ref().map(Deref::deref).map(str::len);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()`
++   |
++   = note: `-D clippy::option-as-ref-deref` implied by `-D warnings`
++
++error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead
++  --> $DIR/option_as_ref_deref.rs:16:13
++   |
++LL |       let _ = opt.clone()
++   |  _____________^
++LL | |         .as_ref().map(
++LL | |             Deref::deref
++LL | |         )
++   | |_________^ help: try using as_deref instead: `opt.clone().as_deref()`
++
++error: called `.as_mut().map(DerefMut::deref_mut)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
++  --> $DIR/option_as_ref_deref.rs:22:13
++   |
++LL |     let _ = opt.as_mut().map(DerefMut::deref_mut);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
++
++error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
++  --> $DIR/option_as_ref_deref.rs:24:13
++   |
++LL |     let _ = opt.as_ref().map(String::as_str);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
++
++error: called `.as_ref().map(|x| x.as_str())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
++  --> $DIR/option_as_ref_deref.rs:25:13
++   |
++LL |     let _ = opt.as_ref().map(|x| x.as_str());
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
++
++error: called `.as_mut().map(String::as_mut_str)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
++  --> $DIR/option_as_ref_deref.rs:26:13
++   |
++LL |     let _ = opt.as_mut().map(String::as_mut_str);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
++
++error: called `.as_mut().map(|x| x.as_mut_str())` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
++  --> $DIR/option_as_ref_deref.rs:27:13
++   |
++LL |     let _ = opt.as_mut().map(|x| x.as_mut_str());
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
++
++error: called `.as_ref().map(CString::as_c_str)` on an Option value. This can be done more directly by calling `Some(CString::new(vec![]).unwrap()).as_deref()` instead
++  --> $DIR/option_as_ref_deref.rs:28:13
++   |
++LL |     let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()`
++
++error: called `.as_ref().map(OsString::as_os_str)` on an Option value. This can be done more directly by calling `Some(OsString::new()).as_deref()` instead
++  --> $DIR/option_as_ref_deref.rs:29:13
++   |
++LL |     let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()`
++
++error: called `.as_ref().map(PathBuf::as_path)` on an Option value. This can be done more directly by calling `Some(PathBuf::new()).as_deref()` instead
++  --> $DIR/option_as_ref_deref.rs:30:13
++   |
++LL |     let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()`
++
++error: called `.as_ref().map(Vec::as_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref()` instead
++  --> $DIR/option_as_ref_deref.rs:31:13
++   |
++LL |     let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()`
++
++error: called `.as_mut().map(Vec::as_mut_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref_mut()` instead
++  --> $DIR/option_as_ref_deref.rs:32:13
++   |
++LL |     let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()`
++
++error: called `.as_ref().map(|x| x.deref())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
++  --> $DIR/option_as_ref_deref.rs:34:13
++   |
++LL |     let _ = opt.as_ref().map(|x| x.deref());
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
++
++error: called `.as_mut().map(|x| x.deref_mut())` on an Option value. This can be done more directly by calling `opt.clone().as_deref_mut()` instead
++  --> $DIR/option_as_ref_deref.rs:35:13
++   |
++LL |     let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len());
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()`
++
++error: called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead
++  --> $DIR/option_as_ref_deref.rs:42:13
++   |
++LL |     let _ = opt.as_ref().map(|x| &**x);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
++
++error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead
++  --> $DIR/option_as_ref_deref.rs:43:13
++   |
++LL |     let _ = opt.as_mut().map(|x| &mut **x);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()`
++
++error: aborting due to 16 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..642c77460a3407076ed9be98fa17eeb94feb1196
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++// aux-build:macro_rules.rs
++#![warn(clippy::option_env_unwrap)]
++
++#[macro_use]
++extern crate macro_rules;
++
++macro_rules! option_env_unwrap {
++    ($env: expr) => {
++        option_env!($env).unwrap()
++    };
++    ($env: expr, $message: expr) => {
++        option_env!($env).expect($message)
++    };
++}
++
++fn main() {
++    let _ = option_env!("PATH").unwrap();
++    let _ = option_env!("PATH").expect("environment variable PATH isn't set");
++    let _ = option_env_unwrap!("PATH");
++    let _ = option_env_unwrap!("PATH", "environment variable PATH isn't set");
++    let _ = option_env_unwrap_external!("PATH");
++    let _ = option_env_unwrap_external!("PATH", "environment variable PATH isn't set");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8de9c8a9d29e0a1da2b4c59bef6ccdd48b8b7491
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++error: this will panic at run-time if the environment variable doesn't exist at compile-time
++  --> $DIR/option_env_unwrap.rs:17:13
++   |
++LL |     let _ = option_env!("PATH").unwrap();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::option-env-unwrap` implied by `-D warnings`
++   = help: consider using the `env!` macro instead
++
++error: this will panic at run-time if the environment variable doesn't exist at compile-time
++  --> $DIR/option_env_unwrap.rs:18:13
++   |
++LL |     let _ = option_env!("PATH").expect("environment variable PATH isn't set");
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using the `env!` macro instead
++
++error: this will panic at run-time if the environment variable doesn't exist at compile-time
++  --> $DIR/option_env_unwrap.rs:9:9
++   |
++LL |         option_env!($env).unwrap()
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
++...
++LL |     let _ = option_env_unwrap!("PATH");
++   |             -------------------------- in this macro invocation
++   |
++   = help: consider using the `env!` macro instead
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: this will panic at run-time if the environment variable doesn't exist at compile-time
++  --> $DIR/option_env_unwrap.rs:12:9
++   |
++LL |         option_env!($env).expect($message)
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++...
++LL |     let _ = option_env_unwrap!("PATH", "environment variable PATH isn't set");
++   |             ----------------------------------------------------------------- in this macro invocation
++   |
++   = help: consider using the `env!` macro instead
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: this will panic at run-time if the environment variable doesn't exist at compile-time
++  --> $DIR/option_env_unwrap.rs:21:13
++   |
++LL |     let _ = option_env_unwrap_external!("PATH");
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using the `env!` macro instead
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: this will panic at run-time if the environment variable doesn't exist at compile-time
++  --> $DIR/option_env_unwrap.rs:22:13
++   |
++LL |     let _ = option_env_unwrap_external!("PATH", "environment variable PATH isn't set");
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using the `env!` macro instead
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..decbae4e5af9aabb7285ba7519845f48071af8f6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++// run-rustfix
++
++#![allow(clippy::option_and_then_some)]
++
++fn main() {
++    let opt = Some(1);
++
++    // Check `OPTION_MAP_OR_NONE`.
++    // Single line case.
++    let _ = opt.and_then(|x| Some(x + 1));
++    // Multi-line case.
++    #[rustfmt::skip]
++    let _ = opt.and_then(|x| {
++                        Some(x + 1)
++                       });
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0f1d2218d5d3889efd3af6f482e6e149bee4381f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++// run-rustfix
++
++#![allow(clippy::option_and_then_some)]
++
++fn main() {
++    let opt = Some(1);
++
++    // Check `OPTION_MAP_OR_NONE`.
++    // Single line case.
++    let _ = opt.map_or(None, |x| Some(x + 1));
++    // Multi-line case.
++    #[rustfmt::skip]
++    let _ = opt.map_or(None, |x| {
++                        Some(x + 1)
++                       });
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6f707987dbcaafd6ce879830f8c46b492de69428
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++error: called `map_or(None, f)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead
++  --> $DIR/option_map_or_none.rs:10:13
++   |
++LL |     let _ = opt.map_or(None, |x| Some(x + 1));
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `opt.and_then(|x| Some(x + 1))`
++   |
++   = note: `-D clippy::option-map-or-none` implied by `-D warnings`
++
++error: called `map_or(None, f)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead
++  --> $DIR/option_map_or_none.rs:13:13
++   |
++LL |       let _ = opt.map_or(None, |x| {
++   |  _____________^
++LL | |                         Some(x + 1)
++LL | |                        });
++   | |_________________________^
++   |
++help: try using `and_then` instead
++   |
++LL |     let _ = opt.and_then(|x| {
++LL |                         Some(x + 1)
++LL |                        });
++   |
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9a0da404cb6dbbbb65aa13f54fbd14587ccf149d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,84 @@@
++// run-rustfix
++
++#![warn(clippy::option_map_unit_fn)]
++#![allow(unused)]
++
++fn do_nothing<T>(_: T) {}
++
++fn diverge<T>(_: T) -> ! {
++    panic!()
++}
++
++fn plus_one(value: usize) -> usize {
++    value + 1
++}
++
++fn option() -> Option<usize> {
++    Some(10)
++}
++
++struct HasOption {
++    field: Option<usize>,
++}
++
++impl HasOption {
++    fn do_option_nothing(self: &Self, value: usize) {}
++
++    fn do_option_plus_one(self: &Self, value: usize) -> usize {
++        value + 1
++    }
++}
++#[rustfmt::skip]
++fn option_map_unit_fn() {
++    let x = HasOption { field: Some(10) };
++
++    x.field.map(plus_one);
++    let _ : Option<()> = x.field.map(do_nothing);
++
++    if let Some(x_field) = x.field { do_nothing(x_field) }
++
++    if let Some(x_field) = x.field { do_nothing(x_field) }
++
++    if let Some(x_field) = x.field { diverge(x_field) }
++
++    let captured = 10;
++    if let Some(value) = x.field { do_nothing(value + captured) };
++    let _ : Option<()> = x.field.map(|value| do_nothing(value + captured));
++
++    if let Some(value) = x.field { x.do_option_nothing(value + captured) }
++
++    if let Some(value) = x.field { x.do_option_plus_one(value + captured); }
++
++
++    if let Some(value) = x.field { do_nothing(value + captured) }
++
++    if let Some(value) = x.field { do_nothing(value + captured) }
++
++    if let Some(value) = x.field { do_nothing(value + captured); }
++
++    if let Some(value) = x.field { do_nothing(value + captured); }
++
++
++    if let Some(value) = x.field { diverge(value + captured) }
++
++    if let Some(value) = x.field { diverge(value + captured) }
++
++    if let Some(value) = x.field { diverge(value + captured); }
++
++    if let Some(value) = x.field { diverge(value + captured); }
++
++
++    x.field.map(|value| plus_one(value + captured));
++    x.field.map(|value| { plus_one(value + captured) });
++    if let Some(value) = x.field { let y = plus_one(value + captured); }
++
++    if let Some(value) = x.field { plus_one(value + captured); }
++
++    if let Some(value) = x.field { plus_one(value + captured); }
++
++
++    if let Some(ref value) = x.field { do_nothing(value + captured) }
++
++    if let Some(a) = option() { do_nothing(a) }}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..58041b62df35a6b564e1850be6782d8d50ce2808
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,84 @@@
++// run-rustfix
++
++#![warn(clippy::option_map_unit_fn)]
++#![allow(unused)]
++
++fn do_nothing<T>(_: T) {}
++
++fn diverge<T>(_: T) -> ! {
++    panic!()
++}
++
++fn plus_one(value: usize) -> usize {
++    value + 1
++}
++
++fn option() -> Option<usize> {
++    Some(10)
++}
++
++struct HasOption {
++    field: Option<usize>,
++}
++
++impl HasOption {
++    fn do_option_nothing(self: &Self, value: usize) {}
++
++    fn do_option_plus_one(self: &Self, value: usize) -> usize {
++        value + 1
++    }
++}
++#[rustfmt::skip]
++fn option_map_unit_fn() {
++    let x = HasOption { field: Some(10) };
++
++    x.field.map(plus_one);
++    let _ : Option<()> = x.field.map(do_nothing);
++
++    x.field.map(do_nothing);
++
++    x.field.map(do_nothing);
++
++    x.field.map(diverge);
++
++    let captured = 10;
++    if let Some(value) = x.field { do_nothing(value + captured) };
++    let _ : Option<()> = x.field.map(|value| do_nothing(value + captured));
++
++    x.field.map(|value| x.do_option_nothing(value + captured));
++
++    x.field.map(|value| { x.do_option_plus_one(value + captured); });
++
++
++    x.field.map(|value| do_nothing(value + captured));
++
++    x.field.map(|value| { do_nothing(value + captured) });
++
++    x.field.map(|value| { do_nothing(value + captured); });
++
++    x.field.map(|value| { { do_nothing(value + captured); } });
++
++
++    x.field.map(|value| diverge(value + captured));
++
++    x.field.map(|value| { diverge(value + captured) });
++
++    x.field.map(|value| { diverge(value + captured); });
++
++    x.field.map(|value| { { diverge(value + captured); } });
++
++
++    x.field.map(|value| plus_one(value + captured));
++    x.field.map(|value| { plus_one(value + captured) });
++    x.field.map(|value| { let y = plus_one(value + captured); });
++
++    x.field.map(|value| { plus_one(value + captured); });
++
++    x.field.map(|value| { { plus_one(value + captured); } });
++
++
++    x.field.map(|ref value| { do_nothing(value + captured) });
++
++    option().map(do_nothing);}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1312c70b6d5929be6678ec97af153c78f7da93e1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,148 @@@
++error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:38:5
++   |
++LL |     x.field.map(do_nothing);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }`
++   |
++   = note: `-D clippy::option-map-unit-fn` implied by `-D warnings`
++
++error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:40:5
++   |
++LL |     x.field.map(do_nothing);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }`
++
++error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:42:5
++   |
++LL |     x.field.map(diverge);
++   |     ^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(x_field) = x.field { diverge(x_field) }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:48:5
++   |
++LL |     x.field.map(|value| x.do_option_nothing(value + captured));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:50:5
++   |
++LL |     x.field.map(|value| { x.do_option_plus_one(value + captured); });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:53:5
++   |
++LL |     x.field.map(|value| do_nothing(value + captured));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:55:5
++   |
++LL |     x.field.map(|value| { do_nothing(value + captured) });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:57:5
++   |
++LL |     x.field.map(|value| { do_nothing(value + captured); });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:59:5
++   |
++LL |     x.field.map(|value| { { do_nothing(value + captured); } });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:62:5
++   |
++LL |     x.field.map(|value| diverge(value + captured));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(value) = x.field { diverge(value + captured) }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:64:5
++   |
++LL |     x.field.map(|value| { diverge(value + captured) });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(value) = x.field { diverge(value + captured) }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:66:5
++   |
++LL |     x.field.map(|value| { diverge(value + captured); });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(value) = x.field { diverge(value + captured); }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:68:5
++   |
++LL |     x.field.map(|value| { { diverge(value + captured); } });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(value) = x.field { diverge(value + captured); }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:73:5
++   |
++LL |     x.field.map(|value| { let y = plus_one(value + captured); });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(value) = x.field { let y = plus_one(value + captured); }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:75:5
++   |
++LL |     x.field.map(|value| { plus_one(value + captured); });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(value) = x.field { plus_one(value + captured); }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:77:5
++   |
++LL |     x.field.map(|value| { { plus_one(value + captured); } });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(value) = x.field { plus_one(value + captured); }`
++
++error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:80:5
++   |
++LL |     x.field.map(|ref value| { do_nothing(value + captured) });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(ref value) = x.field { do_nothing(value + captured) }`
++
++error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type
++  --> $DIR/option_map_unit_fn_fixable.rs:82:5
++   |
++LL |     option().map(do_nothing);}
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Some(a) = option() { do_nothing(a) }`
++
++error: aborting due to 18 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..20e6c15b18d5f97717dd8331308be29b6f16b8b9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++#![warn(clippy::option_map_unit_fn)]
++#![allow(unused)]
++
++fn do_nothing<T>(_: T) {}
++
++fn diverge<T>(_: T) -> ! {
++    panic!()
++}
++
++fn plus_one(value: usize) -> usize {
++    value + 1
++}
++
++#[rustfmt::skip]
++fn option_map_unit_fn() {
++
++    x.field.map(|value| { do_nothing(value); do_nothing(value) });
++
++    x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) });
++
++    // Suggestion for the let block should be `{ ... }` as it's too difficult to build a
++    // proper suggestion for these cases
++    x.field.map(|value| {
++        do_nothing(value);
++        do_nothing(value)
++    });
++    x.field.map(|value| { do_nothing(value); do_nothing(value); });
++
++    // The following should suggest `if let Some(_X) ...` as it's difficult to generate a proper let variable name for them
++    Some(42).map(diverge);
++    "12".parse::<i32>().ok().map(diverge);
++    Some(plus_one(1)).map(do_nothing);
++
++    // Should suggest `if let Some(_y) ...` to not override the existing foo variable
++    let y = Some(42);
++    y.map(do_nothing);
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a53f5889c58dac2b355a74739bd4fcc3a85003bc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++error[E0425]: cannot find value `x` in this scope
++  --> $DIR/option_map_unit_fn_unfixable.rs:17:5
++   |
++LL |     x.field.map(|value| { do_nothing(value); do_nothing(value) });
++   |     ^ not found in this scope
++
++error[E0425]: cannot find value `x` in this scope
++  --> $DIR/option_map_unit_fn_unfixable.rs:19:5
++   |
++LL |     x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) });
++   |     ^ not found in this scope
++
++error[E0425]: cannot find value `x` in this scope
++  --> $DIR/option_map_unit_fn_unfixable.rs:23:5
++   |
++LL |     x.field.map(|value| {
++   |     ^ not found in this scope
++
++error[E0425]: cannot find value `x` in this scope
++  --> $DIR/option_map_unit_fn_unfixable.rs:27:5
++   |
++LL |     x.field.map(|value| { do_nothing(value); do_nothing(value); });
++   |     ^ not found in this scope
++
++error: aborting due to 4 previous errors
++
++For more information about this error, try `rustc --explain E0425`.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0364d83663a06c18ac84049a46b6fe09204bc2c6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,88 @@@
++// FIXME: Add "run-rustfix" once it's supported for multipart suggestions
++// aux-build:option_helpers.rs
++
++#![warn(clippy::option_map_unwrap_or, clippy::option_map_unwrap_or_else)]
++
++#[macro_use]
++extern crate option_helpers;
++
++use std::collections::HashMap;
++
++/// Checks implementation of the following lints:
++/// * `OPTION_MAP_UNWRAP_OR`
++/// * `OPTION_MAP_UNWRAP_OR_ELSE`
++#[rustfmt::skip]
++fn option_methods() {
++    let opt = Some(1);
++
++    // Check `OPTION_MAP_UNWRAP_OR`.
++    // Single line case.
++    let _ = opt.map(|x| x + 1)
++        // Should lint even though this call is on a separate line.
++        .unwrap_or(0);
++    // Multi-line cases.
++    let _ = opt.map(|x| {
++        x + 1
++    }
++    ).unwrap_or(0);
++    let _ = opt.map(|x| x + 1)
++        .unwrap_or({
++            0
++        });
++    // Single line `map(f).unwrap_or(None)` case.
++    let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
++    // Multi-line `map(f).unwrap_or(None)` cases.
++    let _ = opt.map(|x| {
++        Some(x + 1)
++    }
++    ).unwrap_or(None);
++    let _ = opt
++        .map(|x| Some(x + 1))
++        .unwrap_or(None);
++    // macro case
++    let _ = opt_map!(opt, |x| x + 1).unwrap_or(0); // should not lint
++
++    // Should not lint if not copyable
++    let id: String = "identifier".to_string();
++    let _ = Some("prefix").map(|p| format!("{}.{}", p, id)).unwrap_or(id);
++    // ...but DO lint if the `unwrap_or` argument is not used in the `map`
++    let id: String = "identifier".to_string();
++    let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
++
++    // Check OPTION_MAP_UNWRAP_OR_ELSE
++    // single line case
++    let _ = opt.map(|x| x + 1)
++        // Should lint even though this call is on a separate line.
++        .unwrap_or_else(|| 0);
++    // Multi-line cases.
++    let _ = opt.map(|x| {
++        x + 1
++    }
++    ).unwrap_or_else(|| 0);
++    let _ = opt.map(|x| x + 1)
++        .unwrap_or_else(||
++            0
++        );
++    // Macro case.
++    // Should not lint.
++    let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0);
++
++    // Issue #4144
++    {
++        let mut frequencies = HashMap::new();
++        let word = "foo";
++
++        frequencies
++            .get_mut(word)
++            .map(|count| {
++                *count += 1;
++            })
++            .unwrap_or_else(|| {
++                frequencies.insert(word.to_owned(), 1);
++            });
++    }
++}
++
++fn main() {
++    option_methods();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f05f2893de23ad69ee78ad7a827aa8a1b2f86265
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,138 @@@
++error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead
++  --> $DIR/option_map_unwrap_or.rs:20:13
++   |
++LL |       let _ = opt.map(|x| x + 1)
++   |  _____________^
++LL | |         // Should lint even though this call is on a separate line.
++LL | |         .unwrap_or(0);
++   | |_____________________^
++   |
++   = note: `-D clippy::option-map-unwrap-or` implied by `-D warnings`
++help: use `map_or(a, f)` instead
++   |
++LL |     let _ = opt.map_or(0, |x| x + 1);
++   |                 ^^^^^^ ^^          --
++
++error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead
++  --> $DIR/option_map_unwrap_or.rs:24:13
++   |
++LL |       let _ = opt.map(|x| {
++   |  _____________^
++LL | |         x + 1
++LL | |     }
++LL | |     ).unwrap_or(0);
++   | |__________________^
++   |
++help: use `map_or(a, f)` instead
++   |
++LL |     let _ = opt.map_or(0, |x| {
++LL |         x + 1
++LL |     }
++LL |     );
++   |
++
++error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead
++  --> $DIR/option_map_unwrap_or.rs:28:13
++   |
++LL |       let _ = opt.map(|x| x + 1)
++   |  _____________^
++LL | |         .unwrap_or({
++LL | |             0
++LL | |         });
++   | |__________^
++   |
++help: use `map_or(a, f)` instead
++   |
++LL |     let _ = opt.map_or({
++LL |             0
++LL |         }, |x| x + 1);
++   |
++
++error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead
++  --> $DIR/option_map_unwrap_or.rs:33:13
++   |
++LL |     let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: use `and_then(f)` instead
++   |
++LL |     let _ = opt.and_then(|x| Some(x + 1));
++   |                 ^^^^^^^^                --
++
++error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead
++  --> $DIR/option_map_unwrap_or.rs:35:13
++   |
++LL |       let _ = opt.map(|x| {
++   |  _____________^
++LL | |         Some(x + 1)
++LL | |     }
++LL | |     ).unwrap_or(None);
++   | |_____________________^
++   |
++help: use `and_then(f)` instead
++   |
++LL |     let _ = opt.and_then(|x| {
++LL |         Some(x + 1)
++LL |     }
++LL |     );
++   |
++
++error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead
++  --> $DIR/option_map_unwrap_or.rs:39:13
++   |
++LL |       let _ = opt
++   |  _____________^
++LL | |         .map(|x| Some(x + 1))
++LL | |         .unwrap_or(None);
++   | |________________________^
++   |
++help: use `and_then(f)` instead
++   |
++LL |         .and_then(|x| Some(x + 1));
++   |          ^^^^^^^^                --
++
++error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead
++  --> $DIR/option_map_unwrap_or.rs:50:13
++   |
++LL |     let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: use `map_or(a, f)` instead
++   |
++LL |     let _ = Some("prefix").map_or(id, |p| format!("{}.", p));
++   |                            ^^^^^^ ^^^                      --
++
++error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead
++  --> $DIR/option_map_unwrap_or.rs:54:13
++   |
++LL |       let _ = opt.map(|x| x + 1)
++   |  _____________^
++LL | |         // Should lint even though this call is on a separate line.
++LL | |         .unwrap_or_else(|| 0);
++   | |_____________________________^
++   |
++   = note: `-D clippy::option-map-unwrap-or-else` implied by `-D warnings`
++   = note: replace `map(|x| x + 1).unwrap_or_else(|| 0)` with `map_or_else(|| 0, |x| x + 1)`
++
++error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead
++  --> $DIR/option_map_unwrap_or.rs:58:13
++   |
++LL |       let _ = opt.map(|x| {
++   |  _____________^
++LL | |         x + 1
++LL | |     }
++LL | |     ).unwrap_or_else(|| 0);
++   | |__________________________^
++
++error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead
++  --> $DIR/option_map_unwrap_or.rs:62:13
++   |
++LL |       let _ = opt.map(|x| x + 1)
++   |  _____________^
++LL | |         .unwrap_or_else(||
++LL | |             0
++LL | |         );
++   | |_________^
++
++error: aborting due to 10 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..904c50e14039a4067cce2899b7b1f5a7e7232fb0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++#![deny(clippy::option_option)]
++
++fn input(_: Option<Option<u8>>) {}
++
++fn output() -> Option<Option<u8>> {
++    None
++}
++
++fn output_nested() -> Vec<Option<Option<u8>>> {
++    vec![None]
++}
++
++// The lint only generates one warning for this
++fn output_nested_nested() -> Option<Option<Option<u8>>> {
++    None
++}
++
++struct Struct {
++    x: Option<Option<u8>>,
++}
++
++impl Struct {
++    fn struct_fn() -> Option<Option<u8>> {
++        None
++    }
++}
++
++trait Trait {
++    fn trait_fn() -> Option<Option<u8>>;
++}
++
++enum Enum {
++    Tuple(Option<Option<u8>>),
++    Struct { x: Option<Option<u8>> },
++}
++
++// The lint allows this
++type OptionOption = Option<Option<u32>>;
++
++// The lint allows this
++fn output_type_alias() -> OptionOption {
++    None
++}
++
++// The line allows this
++impl Trait for Struct {
++    fn trait_fn() -> Option<Option<u8>> {
++        None
++    }
++}
++
++fn main() {
++    input(None);
++    output();
++    output_nested();
++
++    // The lint allows this
++    let local: Option<Option<u8>> = None;
++
++    // The lint allows this
++    let expr = Some(Some(true));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..79db186d7ea77128aef51c329c99711a4f0e8752
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++  --> $DIR/option_option.rs:3:13
++   |
++LL | fn input(_: Option<Option<u8>>) {}
++   |             ^^^^^^^^^^^^^^^^^^
++   |
++note: the lint level is defined here
++  --> $DIR/option_option.rs:1:9
++   |
++LL | #![deny(clippy::option_option)]
++   |         ^^^^^^^^^^^^^^^^^^^^^
++
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++  --> $DIR/option_option.rs:5:16
++   |
++LL | fn output() -> Option<Option<u8>> {
++   |                ^^^^^^^^^^^^^^^^^^
++
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++  --> $DIR/option_option.rs:9:27
++   |
++LL | fn output_nested() -> Vec<Option<Option<u8>>> {
++   |                           ^^^^^^^^^^^^^^^^^^
++
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++  --> $DIR/option_option.rs:14:30
++   |
++LL | fn output_nested_nested() -> Option<Option<Option<u8>>> {
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++  --> $DIR/option_option.rs:19:8
++   |
++LL |     x: Option<Option<u8>>,
++   |        ^^^^^^^^^^^^^^^^^^
++
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++  --> $DIR/option_option.rs:23:23
++   |
++LL |     fn struct_fn() -> Option<Option<u8>> {
++   |                       ^^^^^^^^^^^^^^^^^^
++
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++  --> $DIR/option_option.rs:29:22
++   |
++LL |     fn trait_fn() -> Option<Option<u8>>;
++   |                      ^^^^^^^^^^^^^^^^^^
++
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++  --> $DIR/option_option.rs:33:11
++   |
++LL |     Tuple(Option<Option<u8>>),
++   |           ^^^^^^^^^^^^^^^^^^
++
++error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
++  --> $DIR/option_option.rs:34:17
++   |
++LL |     Struct { x: Option<Option<u8>> },
++   |                 ^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8ea03fe42616c3339b09befee410f49e71fcd30a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,110 @@@
++// run-rustfix
++
++#![warn(clippy::or_fun_call)]
++#![allow(dead_code)]
++
++use std::collections::BTreeMap;
++use std::collections::HashMap;
++use std::time::Duration;
++
++/// Checks implementation of the `OR_FUN_CALL` lint.
++fn or_fun_call() {
++    struct Foo;
++
++    impl Foo {
++        fn new() -> Foo {
++            Foo
++        }
++    }
++
++    enum Enum {
++        A(i32),
++    }
++
++    fn make<T>() -> T {
++        unimplemented!();
++    }
++
++    let with_enum = Some(Enum::A(1));
++    with_enum.unwrap_or(Enum::A(5));
++
++    let with_const_fn = Some(Duration::from_secs(1));
++    with_const_fn.unwrap_or(Duration::from_secs(5));
++
++    let with_constructor = Some(vec![1]);
++    with_constructor.unwrap_or_else(make);
++
++    let with_new = Some(vec![1]);
++    with_new.unwrap_or_default();
++
++    let with_const_args = Some(vec![1]);
++    with_const_args.unwrap_or_else(|| Vec::with_capacity(12));
++
++    let with_err: Result<_, ()> = Ok(vec![1]);
++    with_err.unwrap_or_else(|_| make());
++
++    let with_err_args: Result<_, ()> = Ok(vec![1]);
++    with_err_args.unwrap_or_else(|_| Vec::with_capacity(12));
++
++    let with_default_trait = Some(1);
++    with_default_trait.unwrap_or_default();
++
++    let with_default_type = Some(1);
++    with_default_type.unwrap_or_default();
++
++    let with_vec = Some(vec![1]);
++    with_vec.unwrap_or_default();
++
++    let without_default = Some(Foo);
++    without_default.unwrap_or_else(Foo::new);
++
++    let mut map = HashMap::<u64, String>::new();
++    map.entry(42).or_insert_with(String::new);
++
++    let mut btree = BTreeMap::<u64, String>::new();
++    btree.entry(42).or_insert_with(String::new);
++
++    let stringy = Some(String::from(""));
++    let _ = stringy.unwrap_or_else(|| "".to_owned());
++
++    let opt = Some(1);
++    let hello = "Hello";
++    let _ = opt.ok_or(format!("{} world.", hello));
++}
++
++struct Foo(u8);
++struct Bar(String, Duration);
++#[rustfmt::skip]
++fn test_or_with_ctors() {
++    let opt = Some(1);
++    let opt_opt = Some(Some(1));
++    // we also test for const promotion, this makes sure we don't hit that
++    let two = 2;
++
++    let _ = opt_opt.unwrap_or(Some(2));
++    let _ = opt_opt.unwrap_or(Some(two));
++    let _ = opt.ok_or(Some(2));
++    let _ = opt.ok_or(Some(two));
++    let _ = opt.ok_or(Foo(2));
++    let _ = opt.ok_or(Foo(two));
++    let _ = opt.or(Some(2));
++    let _ = opt.or(Some(two));
++
++    let _ = Some("a".to_string()).or_else(|| Some("b".to_string()));
++
++    let b = "b".to_string();
++    let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
++        .or(Some(Bar(b, Duration::from_secs(2))));
++}
++
++// Issue 4514 - early return
++fn f() -> Option<()> {
++    let a = Some(1);
++    let b = 1i32;
++
++    let _ = a.unwrap_or(b.checked_mul(3)?.min(240));
++
++    Some(())
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7599b945a9137ed63a21cc06ad70aeed0f5f6532
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,110 @@@
++// run-rustfix
++
++#![warn(clippy::or_fun_call)]
++#![allow(dead_code)]
++
++use std::collections::BTreeMap;
++use std::collections::HashMap;
++use std::time::Duration;
++
++/// Checks implementation of the `OR_FUN_CALL` lint.
++fn or_fun_call() {
++    struct Foo;
++
++    impl Foo {
++        fn new() -> Foo {
++            Foo
++        }
++    }
++
++    enum Enum {
++        A(i32),
++    }
++
++    fn make<T>() -> T {
++        unimplemented!();
++    }
++
++    let with_enum = Some(Enum::A(1));
++    with_enum.unwrap_or(Enum::A(5));
++
++    let with_const_fn = Some(Duration::from_secs(1));
++    with_const_fn.unwrap_or(Duration::from_secs(5));
++
++    let with_constructor = Some(vec![1]);
++    with_constructor.unwrap_or(make());
++
++    let with_new = Some(vec![1]);
++    with_new.unwrap_or(Vec::new());
++
++    let with_const_args = Some(vec![1]);
++    with_const_args.unwrap_or(Vec::with_capacity(12));
++
++    let with_err: Result<_, ()> = Ok(vec![1]);
++    with_err.unwrap_or(make());
++
++    let with_err_args: Result<_, ()> = Ok(vec![1]);
++    with_err_args.unwrap_or(Vec::with_capacity(12));
++
++    let with_default_trait = Some(1);
++    with_default_trait.unwrap_or(Default::default());
++
++    let with_default_type = Some(1);
++    with_default_type.unwrap_or(u64::default());
++
++    let with_vec = Some(vec![1]);
++    with_vec.unwrap_or(vec![]);
++
++    let without_default = Some(Foo);
++    without_default.unwrap_or(Foo::new());
++
++    let mut map = HashMap::<u64, String>::new();
++    map.entry(42).or_insert(String::new());
++
++    let mut btree = BTreeMap::<u64, String>::new();
++    btree.entry(42).or_insert(String::new());
++
++    let stringy = Some(String::from(""));
++    let _ = stringy.unwrap_or("".to_owned());
++
++    let opt = Some(1);
++    let hello = "Hello";
++    let _ = opt.ok_or(format!("{} world.", hello));
++}
++
++struct Foo(u8);
++struct Bar(String, Duration);
++#[rustfmt::skip]
++fn test_or_with_ctors() {
++    let opt = Some(1);
++    let opt_opt = Some(Some(1));
++    // we also test for const promotion, this makes sure we don't hit that
++    let two = 2;
++
++    let _ = opt_opt.unwrap_or(Some(2));
++    let _ = opt_opt.unwrap_or(Some(two));
++    let _ = opt.ok_or(Some(2));
++    let _ = opt.ok_or(Some(two));
++    let _ = opt.ok_or(Foo(2));
++    let _ = opt.ok_or(Foo(two));
++    let _ = opt.or(Some(2));
++    let _ = opt.or(Some(two));
++
++    let _ = Some("a".to_string()).or(Some("b".to_string()));
++
++    let b = "b".to_string();
++    let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
++        .or(Some(Bar(b, Duration::from_secs(2))));
++}
++
++// Issue 4514 - early return
++fn f() -> Option<()> {
++    let a = Some(1);
++    let b = 1i32;
++
++    let _ = a.unwrap_or(b.checked_mul(3)?.min(240));
++
++    Some(())
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..96d55771e6cef8c236e26836d2564c0a297a1ca3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,82 @@@
++error: use of `unwrap_or` followed by a function call
++  --> $DIR/or_fun_call.rs:35:22
++   |
++LL |     with_constructor.unwrap_or(make());
++   |                      ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)`
++   |
++   = note: `-D clippy::or-fun-call` implied by `-D warnings`
++
++error: use of `unwrap_or` followed by a call to `new`
++  --> $DIR/or_fun_call.rs:38:5
++   |
++LL |     with_new.unwrap_or(Vec::new());
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_new.unwrap_or_default()`
++
++error: use of `unwrap_or` followed by a function call
++  --> $DIR/or_fun_call.rs:41:21
++   |
++LL |     with_const_args.unwrap_or(Vec::with_capacity(12));
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Vec::with_capacity(12))`
++
++error: use of `unwrap_or` followed by a function call
++  --> $DIR/or_fun_call.rs:44:14
++   |
++LL |     with_err.unwrap_or(make());
++   |              ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| make())`
++
++error: use of `unwrap_or` followed by a function call
++  --> $DIR/or_fun_call.rs:47:19
++   |
++LL |     with_err_args.unwrap_or(Vec::with_capacity(12));
++   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))`
++
++error: use of `unwrap_or` followed by a call to `default`
++  --> $DIR/or_fun_call.rs:50:5
++   |
++LL |     with_default_trait.unwrap_or(Default::default());
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_trait.unwrap_or_default()`
++
++error: use of `unwrap_or` followed by a call to `default`
++  --> $DIR/or_fun_call.rs:53:5
++   |
++LL |     with_default_type.unwrap_or(u64::default());
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_type.unwrap_or_default()`
++
++error: use of `unwrap_or` followed by a call to `new`
++  --> $DIR/or_fun_call.rs:56:5
++   |
++LL |     with_vec.unwrap_or(vec![]);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_vec.unwrap_or_default()`
++
++error: use of `unwrap_or` followed by a function call
++  --> $DIR/or_fun_call.rs:59:21
++   |
++LL |     without_default.unwrap_or(Foo::new());
++   |                     ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)`
++
++error: use of `or_insert` followed by a function call
++  --> $DIR/or_fun_call.rs:62:19
++   |
++LL |     map.entry(42).or_insert(String::new());
++   |                   ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)`
++
++error: use of `or_insert` followed by a function call
++  --> $DIR/or_fun_call.rs:65:21
++   |
++LL |     btree.entry(42).or_insert(String::new());
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)`
++
++error: use of `unwrap_or` followed by a function call
++  --> $DIR/or_fun_call.rs:68:21
++   |
++LL |     let _ = stringy.unwrap_or("".to_owned());
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())`
++
++error: use of `or` followed by a function call
++  --> $DIR/or_fun_call.rs:93:35
++   |
++LL |     let _ = Some("a".to_string()).or(Some("b".to_string()));
++   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))`
++
++error: aborting due to 13 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f20a0ede1137cb76a6aab1b362be07ef25e2179e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++#![warn(clippy::out_of_bounds_indexing)]
++#![allow(clippy::no_effect, const_err)]
++
++fn main() {
++    let x = [1, 2, 3, 4];
++
++    // issue 3102
++    let num = 1;
++    &x[num..10]; // should trigger out of bounds error
++    &x[10..num]; // should trigger out of bounds error
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..516c1df40be0acfc0edcabcdba0aa5b4c1bbc33a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: range is out of bounds
++  --> $DIR/issue-3102.rs:9:13
++   |
++LL |     &x[num..10]; // should trigger out of bounds error
++   |             ^^
++   |
++   = note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings`
++
++error: range is out of bounds
++  --> $DIR/issue-3102.rs:10:8
++   |
++LL |     &x[10..num]; // should trigger out of bounds error
++   |        ^^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..590e578d758ea7ab3a9d53e546a9c9cc7b10c055
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++#![warn(clippy::out_of_bounds_indexing)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation, const_err)]
++
++fn main() {
++    let x = [1, 2, 3, 4];
++
++    &x[..=4];
++    &x[1..5];
++    &x[5..];
++    &x[..5];
++    &x[5..].iter().map(|x| 2 * x).collect::<Vec<i32>>();
++    &x[0..=4];
++
++    &x[4..]; // Ok, should not produce stderr.
++    &x[..4]; // Ok, should not produce stderr.
++    &x[..]; // Ok, should not produce stderr.
++    &x[1..]; // Ok, should not produce stderr.
++    &x[2..].iter().map(|x| 2 * x).collect::<Vec<i32>>(); // Ok, should not produce stderr.
++
++    &x[0..].get(..3); // Ok, should not produce stderr.
++    &x[0..3]; // Ok, should not produce stderr.
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3d95afcdab23397d94debef7f27842c149a6eb8f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: range is out of bounds
++  --> $DIR/simple.rs:7:11
++   |
++LL |     &x[..=4];
++   |           ^
++   |
++   = note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings`
++
++error: range is out of bounds
++  --> $DIR/simple.rs:8:11
++   |
++LL |     &x[1..5];
++   |           ^
++
++error: range is out of bounds
++  --> $DIR/simple.rs:9:8
++   |
++LL |     &x[5..];
++   |        ^
++
++error: range is out of bounds
++  --> $DIR/simple.rs:10:10
++   |
++LL |     &x[..5];
++   |          ^
++
++error: range is out of bounds
++  --> $DIR/simple.rs:11:8
++   |
++LL |     &x[5..].iter().map(|x| 2 * x).collect::<Vec<i32>>();
++   |        ^
++
++error: range is out of bounds
++  --> $DIR/simple.rs:12:12
++   |
++LL |     &x[0..=4];
++   |            ^
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..999a19b289e1816c3ec20af42aa8dc39fa3bbd23
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++// run-rustfix
++
++#![deny(clippy::internal)]
++#![feature(rustc_private)]
++
++extern crate rustc_hir;
++extern crate rustc_lint;
++extern crate rustc_middle;
++#[macro_use]
++extern crate rustc_session;
++use rustc_hir::Expr;
++use rustc_lint::{LateContext, LateLintPass};
++
++declare_lint! {
++    pub TEST_LINT,
++    Warn,
++    ""
++}
++
++declare_lint_pass!(Pass => [TEST_LINT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
++    fn check_expr(&mut self, _cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
++        let _ = expr.span.ctxt().outer_expn_data();
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5405d475d1accd4b1a49dd4bfea06bcf5464b3dc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++// run-rustfix
++
++#![deny(clippy::internal)]
++#![feature(rustc_private)]
++
++extern crate rustc_hir;
++extern crate rustc_lint;
++extern crate rustc_middle;
++#[macro_use]
++extern crate rustc_session;
++use rustc_hir::Expr;
++use rustc_lint::{LateContext, LateLintPass};
++
++declare_lint! {
++    pub TEST_LINT,
++    Warn,
++    ""
++}
++
++declare_lint_pass!(Pass => [TEST_LINT]);
++
++impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
++    fn check_expr(&mut self, _cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
++        let _ = expr.span.ctxt().outer_expn().expn_data();
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..56b6ce1f78ea4de38c7f1b1d0ffb666565c1f05a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++error: usage of `outer_expn().expn_data()`
++  --> $DIR/outer_expn_data.rs:24:34
++   |
++LL |         let _ = expr.span.ctxt().outer_expn().expn_data();
++   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `outer_expn_data()`
++   |
++note: the lint level is defined here
++  --> $DIR/outer_expn_data.rs:3:9
++   |
++LL | #![deny(clippy::internal)]
++   |         ^^^^^^^^^^^^^^^^
++   = note: `#[deny(clippy::outer_expn_expn_data)]` implied by `#[deny(clippy::internal)]`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..84332040dbadbdb1e55bd34e2c696d5a1d7f2ba1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++#![allow(clippy::many_single_char_names)]
++#![warn(clippy::overflow_check_conditional)]
++
++fn main() {
++    let a: u32 = 1;
++    let b: u32 = 2;
++    let c: u32 = 3;
++    if a + b < a {}
++    if a > a + b {}
++    if a + b < b {}
++    if b > a + b {}
++    if a - b > b {}
++    if b < a - b {}
++    if a - b > a {}
++    if a < a - b {}
++    if a + b < c {}
++    if c > a + b {}
++    if a - b < c {}
++    if c > a - b {}
++    let i = 1.1;
++    let j = 2.2;
++    if i + j < i {}
++    if i - j < i {}
++    if i > i + j {}
++    if i - j < i {}
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ad66135d326bdcf51538f817b48a4455a396dc7c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++error: You are trying to use classic C overflow conditions that will fail in Rust.
++  --> $DIR/overflow_check_conditional.rs:8:8
++   |
++LL |     if a + b < a {}
++   |        ^^^^^^^^^
++   |
++   = note: `-D clippy::overflow-check-conditional` implied by `-D warnings`
++
++error: You are trying to use classic C overflow conditions that will fail in Rust.
++  --> $DIR/overflow_check_conditional.rs:9:8
++   |
++LL |     if a > a + b {}
++   |        ^^^^^^^^^
++
++error: You are trying to use classic C overflow conditions that will fail in Rust.
++  --> $DIR/overflow_check_conditional.rs:10:8
++   |
++LL |     if a + b < b {}
++   |        ^^^^^^^^^
++
++error: You are trying to use classic C overflow conditions that will fail in Rust.
++  --> $DIR/overflow_check_conditional.rs:11:8
++   |
++LL |     if b > a + b {}
++   |        ^^^^^^^^^
++
++error: You are trying to use classic C underflow conditions that will fail in Rust.
++  --> $DIR/overflow_check_conditional.rs:12:8
++   |
++LL |     if a - b > b {}
++   |        ^^^^^^^^^
++
++error: You are trying to use classic C underflow conditions that will fail in Rust.
++  --> $DIR/overflow_check_conditional.rs:13:8
++   |
++LL |     if b < a - b {}
++   |        ^^^^^^^^^
++
++error: You are trying to use classic C underflow conditions that will fail in Rust.
++  --> $DIR/overflow_check_conditional.rs:14:8
++   |
++LL |     if a - b > a {}
++   |        ^^^^^^^^^
++
++error: You are trying to use classic C underflow conditions that will fail in Rust.
++  --> $DIR/overflow_check_conditional.rs:15:8
++   |
++LL |     if a < a - b {}
++   |        ^^^^^^^^^
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6e004aa9a924ffff0d7160eec504a5140051c4b0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++#![warn(clippy::panic_params)]
++#![allow(clippy::assertions_on_constants)]
++fn missing() {
++    if true {
++        panic!("{}");
++    } else if false {
++        panic!("{:?}");
++    } else {
++        assert!(true, "here be missing values: {}");
++    }
++
++    panic!("{{{this}}}");
++}
++
++fn ok_single() {
++    panic!("foo bar");
++}
++
++fn ok_inner() {
++    // Test for #768
++    assert!("foo bar".contains(&format!("foo {}", "bar")));
++}
++
++fn ok_multiple() {
++    panic!("{}", "This is {ok}");
++}
++
++fn ok_bracket() {
++    match 42 {
++        1337 => panic!("{so is this"),
++        666 => panic!("so is this}"),
++        _ => panic!("}so is that{"),
++    }
++}
++
++const ONE: u32 = 1;
++
++fn ok_nomsg() {
++    assert!({ 1 == ONE });
++    assert!(if 1 == ONE { ONE == 1 } else { false });
++}
++
++fn ok_escaped() {
++    panic!("{{ why should this not be ok? }}");
++    panic!(" or {{ that ?");
++    panic!(" or }} this ?");
++    panic!(" {or {{ that ?");
++    panic!(" }or }} this ?");
++    panic!("{{ test }");
++    panic!("{case }}");
++}
++
++fn main() {
++    missing();
++    ok_single();
++    ok_multiple();
++    ok_bracket();
++    ok_inner();
++    ok_nomsg();
++    ok_escaped();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1f8ff8ccf55750577675303eb2bcec693119bdda
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: you probably are missing some parameter in your format string
++  --> $DIR/panic.rs:5:16
++   |
++LL |         panic!("{}");
++   |                ^^^^
++   |
++   = note: `-D clippy::panic-params` implied by `-D warnings`
++
++error: you probably are missing some parameter in your format string
++  --> $DIR/panic.rs:7:16
++   |
++LL |         panic!("{:?}");
++   |                ^^^^^^
++
++error: you probably are missing some parameter in your format string
++  --> $DIR/panic.rs:9:23
++   |
++LL |         assert!(true, "here be missing values: {}");
++   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: you probably are missing some parameter in your format string
++  --> $DIR/panic.rs:12:12
++   |
++LL |     panic!("{{{this}}}");
++   |            ^^^^^^^^^^^^
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dabb695368dba2b485cdc7de2b26defb1cdee55e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)]
++#![allow(clippy::assertions_on_constants)]
++
++fn panic() {
++    let a = 2;
++    panic!();
++    let b = a + 2;
++}
++
++fn todo() {
++    let a = 2;
++    todo!();
++    let b = a + 2;
++}
++
++fn unimplemented() {
++    let a = 2;
++    unimplemented!();
++    let b = a + 2;
++}
++
++fn unreachable() {
++    let a = 2;
++    unreachable!();
++    let b = a + 2;
++}
++
++fn main() {
++    panic();
++    todo();
++    unimplemented();
++    unreachable();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..72319bc7e45846014e6ad49e6669a7cf194e4e8a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++error: `panic` should not be present in production code
++  --> $DIR/panicking_macros.rs:6:5
++   |
++LL |     panic!();
++   |     ^^^^^^^^^
++   |
++   = note: `-D clippy::panic` implied by `-D warnings`
++
++error: `todo` should not be present in production code
++  --> $DIR/panicking_macros.rs:12:5
++   |
++LL |     todo!();
++   |     ^^^^^^^^
++   |
++   = note: `-D clippy::todo` implied by `-D warnings`
++
++error: `unimplemented` should not be present in production code
++  --> $DIR/panicking_macros.rs:18:5
++   |
++LL |     unimplemented!();
++   |     ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::unimplemented` implied by `-D warnings`
++
++error: `unreachable` should not be present in production code
++  --> $DIR/panicking_macros.rs:24:5
++   |
++LL |     unreachable!();
++   |     ^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::unreachable` implied by `-D warnings`
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1338d3c74d5540117bb54666b5fa35ce56ed1a25
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++#![allow(dead_code)]
++
++struct Foo;
++
++impl PartialEq for Foo {
++    fn eq(&self, _: &Foo) -> bool {
++        true
++    }
++    fn ne(&self, _: &Foo) -> bool {
++        false
++    }
++}
++
++struct Bar;
++
++impl PartialEq for Bar {
++    fn eq(&self, _: &Bar) -> bool {
++        true
++    }
++    #[allow(clippy::partialeq_ne_impl)]
++    fn ne(&self, _: &Bar) -> bool {
++        false
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b92da4511b48d03ee50bc6a91d23ac40aef285e3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++error: re-implementing `PartialEq::ne` is unnecessary
++  --> $DIR/partialeq_ne_impl.rs:9:5
++   |
++LL | /     fn ne(&self, _: &Foo) -> bool {
++LL | |         false
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::partialeq-ne-impl` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ef8856830fc9949f94c638642a68efb31fb2d80d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++// run-rustfix
++use std::path::PathBuf;
++
++#[warn(clippy::all, clippy::path_buf_push_overwrite)]
++fn main() {
++    let mut x = PathBuf::from("/foo");
++    x.push("bar");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6e2d483f45410973371a46cf241cd123c715ec54
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++// run-rustfix
++use std::path::PathBuf;
++
++#[warn(clippy::all, clippy::path_buf_push_overwrite)]
++fn main() {
++    let mut x = PathBuf::from("/foo");
++    x.push("/bar");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..09b18d71baf93bcc77f8edc51b73fadc54520a8d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: Calling `push` with '/' or '/' (file system root) will overwrite the previous path definition
++  --> $DIR/path_buf_push_overwrite.rs:7:12
++   |
++LL |     x.push("/bar");
++   |            ^^^^^^ help: try: `"bar"`
++   |
++   = note: `-D clippy::path-buf-push-overwrite` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f22388154499a3d6f56a26883642487c13548f9e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++// run-rustfix
++#![allow(unused)]
++#![warn(clippy::all)]
++
++fn main() {
++    let v = Some(true);
++    let s = [0, 1, 2, 3, 4];
++    match v {
++        Some(x) => (),
++        y => (),
++    }
++    match v {
++        Some(x) => (),
++        y @ None => (), // no error
++    }
++    match s {
++        [x, inside @ .., y] => (), // no error
++        [..] => (),
++    }
++
++    let mut mutv = vec![1, 2, 3];
++
++    // required "ref" left out in suggestion: #5271
++    match mutv {
++        ref mut x => {
++            x.push(4);
++            println!("vec: {:?}", x);
++        },
++        ref y if y == &vec![0] => (),
++    }
++
++    match mutv {
++        ref x => println!("vec: {:?}", x),
++        ref y if y == &vec![0] => (),
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5848ecd38d98db5e846e652a042c0c0e51bce09d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++// run-rustfix
++#![allow(unused)]
++#![warn(clippy::all)]
++
++fn main() {
++    let v = Some(true);
++    let s = [0, 1, 2, 3, 4];
++    match v {
++        Some(x) => (),
++        y @ _ => (),
++    }
++    match v {
++        Some(x) => (),
++        y @ None => (), // no error
++    }
++    match s {
++        [x, inside @ .., y] => (), // no error
++        [..] => (),
++    }
++
++    let mut mutv = vec![1, 2, 3];
++
++    // required "ref" left out in suggestion: #5271
++    match mutv {
++        ref mut x @ _ => {
++            x.push(4);
++            println!("vec: {:?}", x);
++        },
++        ref y if y == &vec![0] => (),
++    }
++
++    match mutv {
++        ref x @ _ => println!("vec: {:?}", x),
++        ref y if y == &vec![0] => (),
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..af067580688b5bc4168c70a0028d16d7e75cf0df
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: the `y @ _` pattern can be written as just `y`
++  --> $DIR/patterns.rs:10:9
++   |
++LL |         y @ _ => (),
++   |         ^^^^^ help: try: `y`
++   |
++   = note: `-D clippy::redundant-pattern` implied by `-D warnings`
++
++error: the `x @ _` pattern can be written as just `x`
++  --> $DIR/patterns.rs:25:9
++   |
++LL |         ref mut x @ _ => {
++   |         ^^^^^^^^^^^^^ help: try: `ref mut x`
++
++error: the `x @ _` pattern can be written as just `x`
++  --> $DIR/patterns.rs:33:9
++   |
++LL |         ref x @ _ => println!("vec: {:?}", x),
++   |         ^^^^^^^^^ help: try: `ref x`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..17b1f1bd0bf300171fd79a797ff0df0a765f1697
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++// run-rustfix
++#![warn(clippy::precedence)]
++#![allow(unused_must_use, clippy::no_effect, clippy::unnecessary_operation)]
++#![allow(clippy::identity_op)]
++#![allow(clippy::eq_op)]
++
++macro_rules! trip {
++    ($a:expr) => {
++        match $a & 0b1111_1111u8 {
++            0 => println!("a is zero ({})", $a),
++            _ => println!("a is {}", $a),
++        }
++    };
++}
++
++fn main() {
++    1 << (2 + 3);
++    (1 + 2) << 3;
++    4 >> (1 + 1);
++    (1 + 3) >> 2;
++    1 ^ (1 - 1);
++    3 | (2 - 1);
++    3 & (5 - 2);
++    -(1i32.abs());
++    -(1f32.abs());
++
++    // These should not trigger an error
++    let _ = (-1i32).abs();
++    let _ = (-1f32).abs();
++    let _ = -(1i32).abs();
++    let _ = -(1f32).abs();
++    let _ = -(1i32.abs());
++    let _ = -(1f32.abs());
++
++    // Odd functions shoud not trigger an error
++    let _ = -1f64.asin();
++    let _ = -1f64.asinh();
++    let _ = -1f64.atan();
++    let _ = -1f64.atanh();
++    let _ = -1f64.cbrt();
++    let _ = -1f64.fract();
++    let _ = -1f64.round();
++    let _ = -1f64.signum();
++    let _ = -1f64.sin();
++    let _ = -1f64.sinh();
++    let _ = -1f64.tan();
++    let _ = -1f64.tanh();
++    let _ = -1f64.to_degrees();
++    let _ = -1f64.to_radians();
++
++    let b = 3;
++    trip!(b * 8);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2d0891fd3c20c1c90631e6a582d14f50eafcdf95
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++// run-rustfix
++#![warn(clippy::precedence)]
++#![allow(unused_must_use, clippy::no_effect, clippy::unnecessary_operation)]
++#![allow(clippy::identity_op)]
++#![allow(clippy::eq_op)]
++
++macro_rules! trip {
++    ($a:expr) => {
++        match $a & 0b1111_1111u8 {
++            0 => println!("a is zero ({})", $a),
++            _ => println!("a is {}", $a),
++        }
++    };
++}
++
++fn main() {
++    1 << 2 + 3;
++    1 + 2 << 3;
++    4 >> 1 + 1;
++    1 + 3 >> 2;
++    1 ^ 1 - 1;
++    3 | 2 - 1;
++    3 & 5 - 2;
++    -1i32.abs();
++    -1f32.abs();
++
++    // These should not trigger an error
++    let _ = (-1i32).abs();
++    let _ = (-1f32).abs();
++    let _ = -(1i32).abs();
++    let _ = -(1f32).abs();
++    let _ = -(1i32.abs());
++    let _ = -(1f32.abs());
++
++    // Odd functions shoud not trigger an error
++    let _ = -1f64.asin();
++    let _ = -1f64.asinh();
++    let _ = -1f64.atan();
++    let _ = -1f64.atanh();
++    let _ = -1f64.cbrt();
++    let _ = -1f64.fract();
++    let _ = -1f64.round();
++    let _ = -1f64.signum();
++    let _ = -1f64.sin();
++    let _ = -1f64.sinh();
++    let _ = -1f64.tan();
++    let _ = -1f64.tanh();
++    let _ = -1f64.to_degrees();
++    let _ = -1f64.to_radians();
++
++    let b = 3;
++    trip!(b * 8);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a2ed5392bfc7c643593dbf24434ab8c05d1d3c93
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++error: operator precedence can trip the unwary
++  --> $DIR/precedence.rs:17:5
++   |
++LL |     1 << 2 + 3;
++   |     ^^^^^^^^^^ help: consider parenthesizing your expression: `1 << (2 + 3)`
++   |
++   = note: `-D clippy::precedence` implied by `-D warnings`
++
++error: operator precedence can trip the unwary
++  --> $DIR/precedence.rs:18:5
++   |
++LL |     1 + 2 << 3;
++   |     ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 2) << 3`
++
++error: operator precedence can trip the unwary
++  --> $DIR/precedence.rs:19:5
++   |
++LL |     4 >> 1 + 1;
++   |     ^^^^^^^^^^ help: consider parenthesizing your expression: `4 >> (1 + 1)`
++
++error: operator precedence can trip the unwary
++  --> $DIR/precedence.rs:20:5
++   |
++LL |     1 + 3 >> 2;
++   |     ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 3) >> 2`
++
++error: operator precedence can trip the unwary
++  --> $DIR/precedence.rs:21:5
++   |
++LL |     1 ^ 1 - 1;
++   |     ^^^^^^^^^ help: consider parenthesizing your expression: `1 ^ (1 - 1)`
++
++error: operator precedence can trip the unwary
++  --> $DIR/precedence.rs:22:5
++   |
++LL |     3 | 2 - 1;
++   |     ^^^^^^^^^ help: consider parenthesizing your expression: `3 | (2 - 1)`
++
++error: operator precedence can trip the unwary
++  --> $DIR/precedence.rs:23:5
++   |
++LL |     3 & 5 - 2;
++   |     ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)`
++
++error: unary minus has lower precedence than method call
++  --> $DIR/precedence.rs:24:5
++   |
++LL |     -1i32.abs();
++   |     ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1i32.abs())`
++
++error: unary minus has lower precedence than method call
++  --> $DIR/precedence.rs:25:5
++   |
++LL |     -1f32.abs();
++   |     ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1f32.abs())`
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..366ccc2b3bd584bb79d53cf918631a9496c7e053
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++#![allow(clippy::print_literal, clippy::write_literal)]
++#![warn(clippy::print_stdout, clippy::use_debug)]
++
++use std::fmt::{Debug, Display, Formatter, Result};
++
++#[allow(dead_code)]
++struct Foo;
++
++impl Display for Foo {
++    fn fmt(&self, f: &mut Formatter) -> Result {
++        write!(f, "{:?}", 43.1415)
++    }
++}
++
++impl Debug for Foo {
++    fn fmt(&self, f: &mut Formatter) -> Result {
++        // ok, we can use `Debug` formatting in `Debug` implementations
++        write!(f, "{:?}", 42.718)
++    }
++}
++
++fn main() {
++    println!("Hello");
++    print!("Hello");
++
++    print!("Hello {}", "World");
++
++    print!("Hello {:?}", "World");
++
++    print!("Hello {:#?}", "#orld");
++
++    assert_eq!(42, 1337);
++
++    vec![1, 2];
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..208d95326285133812d440de7d0740216a34ee6e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,54 @@@
++error: use of `Debug`-based formatting
++  --> $DIR/print.rs:11:19
++   |
++LL |         write!(f, "{:?}", 43.1415)
++   |                   ^^^^^^
++   |
++   = note: `-D clippy::use-debug` implied by `-D warnings`
++
++error: use of `println!`
++  --> $DIR/print.rs:23:5
++   |
++LL |     println!("Hello");
++   |     ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::print-stdout` implied by `-D warnings`
++
++error: use of `print!`
++  --> $DIR/print.rs:24:5
++   |
++LL |     print!("Hello");
++   |     ^^^^^^^^^^^^^^^
++
++error: use of `print!`
++  --> $DIR/print.rs:26:5
++   |
++LL |     print!("Hello {}", "World");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: use of `print!`
++  --> $DIR/print.rs:28:5
++   |
++LL |     print!("Hello {:?}", "World");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: use of `Debug`-based formatting
++  --> $DIR/print.rs:28:12
++   |
++LL |     print!("Hello {:?}", "World");
++   |            ^^^^^^^^^^^^
++
++error: use of `print!`
++  --> $DIR/print.rs:30:5
++   |
++LL |     print!("Hello {:#?}", "#orld");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: use of `Debug`-based formatting
++  --> $DIR/print.rs:30:12
++   |
++LL |     print!("Hello {:#?}", "#orld");
++   |            ^^^^^^^^^^^^^
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..40ed18e93026353f8830f163b65609e408aa0a0d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++#![warn(clippy::print_literal)]
++
++fn main() {
++    // these should be fine
++    print!("Hello");
++    println!("Hello");
++    let world = "world";
++    println!("Hello {}", world);
++    println!("Hello {world}", world = world);
++    println!("3 in hex is {:X}", 3);
++    println!("2 + 1 = {:.4}", 3);
++    println!("2 + 1 = {:5.4}", 3);
++    println!("Debug test {:?}", "hello, world");
++    println!("{0:8} {1:>8}", "hello", "world");
++    println!("{1:8} {0:>8}", "hello", "world");
++    println!("{foo:8} {bar:>8}", foo = "hello", bar = "world");
++    println!("{bar:8} {foo:>8}", foo = "hello", bar = "world");
++    println!("{number:>width$}", number = 1, width = 6);
++    println!("{number:>0width$}", number = 1, width = 6);
++
++    // these should throw warnings
++    println!("{} of {:b} people know binary, the other half doesn't", 1, 2);
++    print!("Hello {}", "world");
++    println!("Hello {} {}", world, "world");
++    println!("Hello {}", "world");
++    println!("10 / 4 is {}", 2.5);
++    println!("2 + 1 = {}", 3);
++
++    // positional args don't change the fact
++    // that we're using a literal -- this should
++    // throw a warning
++    println!("{0} {1}", "hello", "world");
++    println!("{1} {0}", "hello", "world");
++
++    // named args shouldn't change anything either
++    println!("{foo} {bar}", foo = "hello", bar = "world");
++    println!("{bar} {foo}", foo = "hello", bar = "world");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fc502e9f71d525858d414699d5184b0c98cb8093
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,88 @@@
++error: literal with an empty format string
++  --> $DIR/print_literal.rs:22:71
++   |
++LL |     println!("{} of {:b} people know binary, the other half doesn't", 1, 2);
++   |                                                                       ^
++   |
++   = note: `-D clippy::print-literal` implied by `-D warnings`
++
++error: literal with an empty format string
++  --> $DIR/print_literal.rs:23:24
++   |
++LL |     print!("Hello {}", "world");
++   |                        ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/print_literal.rs:24:36
++   |
++LL |     println!("Hello {} {}", world, "world");
++   |                                    ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/print_literal.rs:25:26
++   |
++LL |     println!("Hello {}", "world");
++   |                          ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/print_literal.rs:26:30
++   |
++LL |     println!("10 / 4 is {}", 2.5);
++   |                              ^^^
++
++error: literal with an empty format string
++  --> $DIR/print_literal.rs:27:28
++   |
++LL |     println!("2 + 1 = {}", 3);
++   |                            ^
++
++error: literal with an empty format string
++  --> $DIR/print_literal.rs:32:25
++   |
++LL |     println!("{0} {1}", "hello", "world");
++   |                         ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/print_literal.rs:32:34
++   |
++LL |     println!("{0} {1}", "hello", "world");
++   |                                  ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/print_literal.rs:33:25
++   |
++LL |     println!("{1} {0}", "hello", "world");
++   |                         ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/print_literal.rs:33:34
++   |
++LL |     println!("{1} {0}", "hello", "world");
++   |                                  ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/print_literal.rs:36:35
++   |
++LL |     println!("{foo} {bar}", foo = "hello", bar = "world");
++   |                                   ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/print_literal.rs:36:50
++   |
++LL |     println!("{foo} {bar}", foo = "hello", bar = "world");
++   |                                                  ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/print_literal.rs:37:35
++   |
++LL |     println!("{bar} {foo}", foo = "hello", bar = "world");
++   |                                   ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/print_literal.rs:37:50
++   |
++LL |     println!("{bar} {foo}", foo = "hello", bar = "world");
++   |                                                  ^^^^^^^
++
++error: aborting due to 14 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3f710540e9038f1347da79b04555773d239219bd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934
++// // run-rustfix
++
++#![allow(clippy::print_literal)]
++#![warn(clippy::print_with_newline)]
++
++fn main() {
++    print!("Hello\n");
++    print!("Hello {}\n", "world");
++    print!("Hello {} {}\n", "world", "#2");
++    print!("{}\n", 1265);
++
++    // these are all fine
++    print!("");
++    print!("Hello");
++    println!("Hello");
++    println!("Hello\n");
++    println!("Hello {}\n", "world");
++    print!("Issue\n{}", 1265);
++    print!("{}", 1265);
++    print!("\n{}", 1275);
++    print!("\n\n");
++    print!("like eof\n\n");
++    print!("Hello {} {}\n\n", "world", "#2");
++    println!("\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126
++    println!("\nbla\n\n"); // #3126
++
++    // Escaping
++    print!("\\n"); // #3514
++    print!("\\\n"); // should fail
++    print!("\\\\n");
++
++    // Raw strings
++    print!(r"\n"); // #3778
++
++    // Literal newlines should also fail
++    print!(
++        "
++"
++    );
++    print!(
++        r"
++"
++    );
++
++    // Don't warn on CRLF (#4208)
++    print!("\r\n");
++    print!("foo\r\n");
++    print!("\\r\n"); //~ ERROR
++    print!("foo\rbar\n") // ~ ERROR
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..05fe88915d6efc40bcc5921efb894d83e0642820
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,110 @@@
++error: using `print!()` with a format string that ends in a single newline
++  --> $DIR/print_with_newline.rs:8:5
++   |
++LL |     print!("Hello/n");
++   |     ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::print-with-newline` implied by `-D warnings`
++help: use `println!` instead
++   |
++LL |     println!("Hello");
++   |     ^^^^^^^       --
++
++error: using `print!()` with a format string that ends in a single newline
++  --> $DIR/print_with_newline.rs:9:5
++   |
++LL |     print!("Hello {}/n", "world");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: use `println!` instead
++   |
++LL |     println!("Hello {}", "world");
++   |     ^^^^^^^          --
++
++error: using `print!()` with a format string that ends in a single newline
++  --> $DIR/print_with_newline.rs:10:5
++   |
++LL |     print!("Hello {} {}/n", "world", "#2");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: use `println!` instead
++   |
++LL |     println!("Hello {} {}", "world", "#2");
++   |     ^^^^^^^             --
++
++error: using `print!()` with a format string that ends in a single newline
++  --> $DIR/print_with_newline.rs:11:5
++   |
++LL |     print!("{}/n", 1265);
++   |     ^^^^^^^^^^^^^^^^^^^^
++   |
++help: use `println!` instead
++   |
++LL |     println!("{}", 1265);
++   |     ^^^^^^^    --
++
++error: using `print!()` with a format string that ends in a single newline
++  --> $DIR/print_with_newline.rs:30:5
++   |
++LL |     print!("//n"); // should fail
++   |     ^^^^^^^^^^^^^^
++   |
++help: use `println!` instead
++   |
++LL |     println!("/"); // should fail
++   |     ^^^^^^^    --
++
++error: using `print!()` with a format string that ends in a single newline
++  --> $DIR/print_with_newline.rs:37:5
++   |
++LL | /     print!(
++LL | |         "
++LL | | "
++LL | |     );
++   | |_____^
++   |
++help: use `println!` instead
++   |
++LL |     println!(
++LL |         ""
++   |
++
++error: using `print!()` with a format string that ends in a single newline
++  --> $DIR/print_with_newline.rs:41:5
++   |
++LL | /     print!(
++LL | |         r"
++LL | | "
++LL | |     );
++   | |_____^
++   |
++help: use `println!` instead
++   |
++LL |     println!(
++LL |         r""
++   |
++
++error: using `print!()` with a format string that ends in a single newline
++  --> $DIR/print_with_newline.rs:49:5
++   |
++LL |     print!("/r/n"); //~ ERROR
++   |     ^^^^^^^^^^^^^^^
++   |
++help: use `println!` instead
++   |
++LL |     println!("/r"); //~ ERROR
++   |     ^^^^^^^     --
++
++error: using `print!()` with a format string that ends in a single newline
++  --> $DIR/print_with_newline.rs:50:5
++   |
++LL |     print!("foo/rbar/n") // ~ ERROR
++   |     ^^^^^^^^^^^^^^^^^^^^
++   |
++help: use `println!` instead
++   |
++LL |     println!("foo/rbar") // ~ ERROR
++   |     ^^^^^^^          --
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2b889b62ea991b1adb023046529925fcd306320a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++// run-rustfix
++#![allow(clippy::match_single_binding)]
++
++fn main() {
++    println!();
++    println!();
++
++    match "a" {
++        _ => println!(),
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..890f5f68476031be617714b7faea400cc8b66e95
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++// run-rustfix
++#![allow(clippy::match_single_binding)]
++
++fn main() {
++    println!();
++    println!("");
++
++    match "a" {
++        _ => println!(""),
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..23112b8816893894760e77f5a9f09e3abd30a22c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: using `println!("")`
++  --> $DIR/println_empty_string.rs:6:5
++   |
++LL |     println!("");
++   |     ^^^^^^^^^^^^ help: replace it with: `println!()`
++   |
++   = note: `-D clippy::println-empty-string` implied by `-D warnings`
++
++error: using `println!("")`
++  --> $DIR/println_empty_string.rs:9:14
++   |
++LL |         _ => println!(""),
++   |              ^^^^^^^^^^^^ help: replace it with: `println!()`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..59914b8b8f62729804879715e92d77c43a41a101
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++//! Check that we correctly lint procedural macros.
++#![crate_type = "proc-macro"]
++
++extern crate proc_macro;
++
++use proc_macro::TokenStream;
++
++#[allow(dead_code)]
++fn f() {
++    let _x = 3.14;
++}
++
++#[proc_macro]
++pub fn mybangmacro(t: TokenStream) -> TokenStream {
++    t
++}
++
++#[proc_macro_derive(MyDerivedTrait)]
++pub fn myderive(t: TokenStream) -> TokenStream {
++    t
++}
++
++#[proc_macro_attribute]
++pub fn myattribute(t: TokenStream, a: TokenStream) -> TokenStream {
++    t
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..872cbc66af622669d21daa5ae1883d0ac4b97c40
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
++  --> $DIR/proc_macro.rs:10:14
++   |
++LL |     let _x = 3.14;
++   |              ^^^^
++   |
++   = note: `#[deny(clippy::approx_constant)]` on by default
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..30f39e9b06398519302fc7a870da680f0fe5806b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,86 @@@
++#![allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
++#![warn(clippy::ptr_arg)]
++
++use std::borrow::Cow;
++
++fn do_vec(x: &Vec<i64>) {
++    //Nothing here
++}
++
++fn do_vec_mut(x: &mut Vec<i64>) {
++    // no error here
++    //Nothing here
++}
++
++fn do_str(x: &String) {
++    //Nothing here either
++}
++
++fn do_str_mut(x: &mut String) {
++    // no error here
++    //Nothing here either
++}
++
++fn main() {}
++
++trait Foo {
++    type Item;
++    fn do_vec(x: &Vec<i64>);
++    fn do_item(x: &Self::Item);
++}
++
++struct Bar;
++
++// no error, in trait impl (#425)
++impl Foo for Bar {
++    type Item = Vec<u8>;
++    fn do_vec(x: &Vec<i64>) {}
++    fn do_item(x: &Vec<u8>) {}
++}
++
++fn cloned(x: &Vec<u8>) -> Vec<u8> {
++    let e = x.clone();
++    let f = e.clone(); // OK
++    let g = x;
++    let h = g.clone(); // Alas, we cannot reliably detect this without following data.
++    let i = (e).clone();
++    x.clone()
++}
++
++fn str_cloned(x: &String) -> String {
++    let a = x.clone();
++    let b = x.clone();
++    let c = b.clone();
++    let d = a.clone().clone().clone();
++    x.clone()
++}
++
++fn false_positive_capacity(x: &Vec<u8>, y: &String) {
++    let a = x.capacity();
++    let b = y.clone();
++    let c = y.as_str();
++}
++
++fn false_positive_capacity_too(x: &String) -> String {
++    if x.capacity() > 1024 {
++        panic!("Too large!");
++    }
++    x.clone()
++}
++
++#[allow(dead_code)]
++fn test_cow_with_ref(c: &Cow<[i32]>) {}
++
++#[allow(dead_code)]
++fn test_cow(c: Cow<[i32]>) {
++    let _c = c;
++}
++
++trait Foo2 {
++    fn do_string(&self);
++}
++
++// no error for &self references where self is of type String (#2293)
++impl Foo2 for String {
++    fn do_string(&self) {}
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..314f23497f9716647785e95e7fcae266156a57c6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,89 @@@
++error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices.
++  --> $DIR/ptr_arg.rs:6:14
++   |
++LL | fn do_vec(x: &Vec<i64>) {
++   |              ^^^^^^^^^ help: change this to: `&[i64]`
++   |
++   = note: `-D clippy::ptr-arg` implied by `-D warnings`
++
++error: writing `&String` instead of `&str` involves a new object where a slice will do.
++  --> $DIR/ptr_arg.rs:15:14
++   |
++LL | fn do_str(x: &String) {
++   |              ^^^^^^^ help: change this to: `&str`
++
++error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices.
++  --> $DIR/ptr_arg.rs:28:18
++   |
++LL |     fn do_vec(x: &Vec<i64>);
++   |                  ^^^^^^^^^ help: change this to: `&[i64]`
++
++error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices.
++  --> $DIR/ptr_arg.rs:41:14
++   |
++LL | fn cloned(x: &Vec<u8>) -> Vec<u8> {
++   |              ^^^^^^^^
++   |
++help: change this to
++   |
++LL | fn cloned(x: &[u8]) -> Vec<u8> {
++   |              ^^^^^
++help: change `x.clone()` to
++   |
++LL |     let e = x.to_owned();
++   |             ^^^^^^^^^^^^
++help: change `x.clone()` to
++   |
++LL |     x.to_owned()
++   |
++
++error: writing `&String` instead of `&str` involves a new object where a slice will do.
++  --> $DIR/ptr_arg.rs:50:18
++   |
++LL | fn str_cloned(x: &String) -> String {
++   |                  ^^^^^^^
++   |
++help: change this to
++   |
++LL | fn str_cloned(x: &str) -> String {
++   |                  ^^^^
++help: change `x.clone()` to
++   |
++LL |     let a = x.to_string();
++   |             ^^^^^^^^^^^^^
++help: change `x.clone()` to
++   |
++LL |     let b = x.to_string();
++   |             ^^^^^^^^^^^^^
++help: change `x.clone()` to
++   |
++LL |     x.to_string()
++   |
++
++error: writing `&String` instead of `&str` involves a new object where a slice will do.
++  --> $DIR/ptr_arg.rs:58:44
++   |
++LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) {
++   |                                            ^^^^^^^
++   |
++help: change this to
++   |
++LL | fn false_positive_capacity(x: &Vec<u8>, y: &str) {
++   |                                            ^^^^
++help: change `y.clone()` to
++   |
++LL |     let b = y.to_string();
++   |             ^^^^^^^^^^^^^
++help: change `y.as_str()` to
++   |
++LL |     let c = y;
++   |             ^
++
++error: using a reference to `Cow` is not recommended.
++  --> $DIR/ptr_arg.rs:72:25
++   |
++LL | fn test_cow_with_ref(c: &Cow<[i32]>) {}
++   |                         ^^^^^^^^^^^ help: change this to: `&[i32]`
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ebdd6c4003d191386460f72d7f5561c1f7139fd2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++// run-rustfix
++
++fn main() {
++    let vec = vec![b'a', b'b', b'c'];
++    let ptr = vec.as_ptr();
++
++    let offset_u8 = 1_u8;
++    let offset_usize = 1_usize;
++    let offset_isize = 1_isize;
++
++    unsafe {
++        ptr.add(offset_usize);
++        ptr.offset(offset_isize as isize);
++        ptr.offset(offset_u8 as isize);
++
++        ptr.wrapping_add(offset_usize);
++        ptr.wrapping_offset(offset_isize as isize);
++        ptr.wrapping_offset(offset_u8 as isize);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3416c4b727a53750dc01cb856803d2a4b3338c6b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++// run-rustfix
++
++fn main() {
++    let vec = vec![b'a', b'b', b'c'];
++    let ptr = vec.as_ptr();
++
++    let offset_u8 = 1_u8;
++    let offset_usize = 1_usize;
++    let offset_isize = 1_isize;
++
++    unsafe {
++        ptr.offset(offset_usize as isize);
++        ptr.offset(offset_isize as isize);
++        ptr.offset(offset_u8 as isize);
++
++        ptr.wrapping_offset(offset_usize as isize);
++        ptr.wrapping_offset(offset_isize as isize);
++        ptr.wrapping_offset(offset_u8 as isize);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b5c7a03e2775e8ff55d60d168eeddcb7da2cb97a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: use of `offset` with a `usize` casted to an `isize`
++  --> $DIR/ptr_offset_with_cast.rs:12:9
++   |
++LL |         ptr.offset(offset_usize as isize);
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)`
++   |
++   = note: `-D clippy::ptr-offset-with-cast` implied by `-D warnings`
++
++error: use of `wrapping_offset` with a `usize` casted to an `isize`
++  --> $DIR/ptr_offset_with_cast.rs:16:9
++   |
++LL |         ptr.wrapping_offset(offset_usize as isize);
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..11dff94a288657d0c662f383616e2e9b5175509b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,125 @@@
++// run-rustfix
++#![allow(unreachable_code)]
++
++fn some_func(a: Option<u32>) -> Option<u32> {
++    a?;
++
++    a
++}
++
++fn some_other_func(a: Option<u32>) -> Option<u32> {
++    if a.is_none() {
++        return None;
++    } else {
++        return Some(0);
++    }
++    unreachable!()
++}
++
++pub enum SeemsOption<T> {
++    Some(T),
++    None,
++}
++
++impl<T> SeemsOption<T> {
++    pub fn is_none(&self) -> bool {
++        match *self {
++            SeemsOption::None => true,
++            SeemsOption::Some(_) => false,
++        }
++    }
++}
++
++fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> {
++    if a.is_none() {
++        return SeemsOption::None;
++    }
++
++    a
++}
++
++pub struct CopyStruct {
++    pub opt: Option<u32>,
++}
++
++impl CopyStruct {
++    #[rustfmt::skip]
++    pub fn func(&self) -> Option<u32> {
++        (self.opt)?;
++
++        self.opt?;
++
++        let _ = Some(self.opt?);
++
++        let _ = self.opt?;
++
++        self.opt
++    }
++}
++
++#[derive(Clone)]
++pub struct MoveStruct {
++    pub opt: Option<Vec<u32>>,
++}
++
++impl MoveStruct {
++    pub fn ref_func(&self) -> Option<Vec<u32>> {
++        self.opt.as_ref()?;
++
++        self.opt.clone()
++    }
++
++    pub fn mov_func_reuse(self) -> Option<Vec<u32>> {
++        self.opt.as_ref()?;
++
++        self.opt
++    }
++
++    pub fn mov_func_no_use(self) -> Option<Vec<u32>> {
++        self.opt.as_ref()?;
++        Some(Vec::new())
++    }
++
++    pub fn if_let_ref_func(self) -> Option<Vec<u32>> {
++        let v: &Vec<_> = self.opt.as_ref()?;
++
++        Some(v.clone())
++    }
++
++    pub fn if_let_mov_func(self) -> Option<Vec<u32>> {
++        let v = self.opt?;
++
++        Some(v)
++    }
++}
++
++fn func() -> Option<i32> {
++    fn f() -> Option<String> {
++        Some(String::new())
++    }
++
++    f()?;
++
++    Some(0)
++}
++
++fn main() {
++    some_func(Some(42));
++    some_func(None);
++    some_other_func(Some(42));
++
++    let copy_struct = CopyStruct { opt: Some(54) };
++    copy_struct.func();
++
++    let move_struct = MoveStruct {
++        opt: Some(vec![42, 1337]),
++    };
++    move_struct.ref_func();
++    move_struct.clone().mov_func_reuse();
++    move_struct.mov_func_no_use();
++
++    let so = SeemsOption::Some(45);
++    returns_something_similar_to_option(so);
++
++    func();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1d0ee82b4f7787b5e56445c334b2115066804d67
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,155 @@@
++// run-rustfix
++#![allow(unreachable_code)]
++
++fn some_func(a: Option<u32>) -> Option<u32> {
++    if a.is_none() {
++        return None;
++    }
++
++    a
++}
++
++fn some_other_func(a: Option<u32>) -> Option<u32> {
++    if a.is_none() {
++        return None;
++    } else {
++        return Some(0);
++    }
++    unreachable!()
++}
++
++pub enum SeemsOption<T> {
++    Some(T),
++    None,
++}
++
++impl<T> SeemsOption<T> {
++    pub fn is_none(&self) -> bool {
++        match *self {
++            SeemsOption::None => true,
++            SeemsOption::Some(_) => false,
++        }
++    }
++}
++
++fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> {
++    if a.is_none() {
++        return SeemsOption::None;
++    }
++
++    a
++}
++
++pub struct CopyStruct {
++    pub opt: Option<u32>,
++}
++
++impl CopyStruct {
++    #[rustfmt::skip]
++    pub fn func(&self) -> Option<u32> {
++        if (self.opt).is_none() {
++            return None;
++        }
++
++        if self.opt.is_none() {
++            return None
++        }
++
++        let _ = if self.opt.is_none() {
++            return None;
++        } else {
++            self.opt
++        };
++
++        let _ = if let Some(x) = self.opt {
++            x
++        } else {
++            return None;
++        };
++
++        self.opt
++    }
++}
++
++#[derive(Clone)]
++pub struct MoveStruct {
++    pub opt: Option<Vec<u32>>,
++}
++
++impl MoveStruct {
++    pub fn ref_func(&self) -> Option<Vec<u32>> {
++        if self.opt.is_none() {
++            return None;
++        }
++
++        self.opt.clone()
++    }
++
++    pub fn mov_func_reuse(self) -> Option<Vec<u32>> {
++        if self.opt.is_none() {
++            return None;
++        }
++
++        self.opt
++    }
++
++    pub fn mov_func_no_use(self) -> Option<Vec<u32>> {
++        if self.opt.is_none() {
++            return None;
++        }
++        Some(Vec::new())
++    }
++
++    pub fn if_let_ref_func(self) -> Option<Vec<u32>> {
++        let v: &Vec<_> = if let Some(ref v) = self.opt {
++            v
++        } else {
++            return None;
++        };
++
++        Some(v.clone())
++    }
++
++    pub fn if_let_mov_func(self) -> Option<Vec<u32>> {
++        let v = if let Some(v) = self.opt {
++            v
++        } else {
++            return None;
++        };
++
++        Some(v)
++    }
++}
++
++fn func() -> Option<i32> {
++    fn f() -> Option<String> {
++        Some(String::new())
++    }
++
++    if f().is_none() {
++        return None;
++    }
++
++    Some(0)
++}
++
++fn main() {
++    some_func(Some(42));
++    some_func(None);
++    some_other_func(Some(42));
++
++    let copy_struct = CopyStruct { opt: Some(54) };
++    copy_struct.func();
++
++    let move_struct = MoveStruct {
++        opt: Some(vec![42, 1337]),
++    };
++    move_struct.ref_func();
++    move_struct.clone().mov_func_reuse();
++    move_struct.mov_func_no_use();
++
++    let so = SeemsOption::Some(45);
++    returns_something_similar_to_option(so);
++
++    func();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..502615fb175a1857b088db3468bccc16d3917992
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,104 @@@
++error: this block may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:5:5
++   |
++LL | /     if a.is_none() {
++LL | |         return None;
++LL | |     }
++   | |_____^ help: replace it with: `a?;`
++   |
++   = note: `-D clippy::question-mark` implied by `-D warnings`
++
++error: this block may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:50:9
++   |
++LL | /         if (self.opt).is_none() {
++LL | |             return None;
++LL | |         }
++   | |_________^ help: replace it with: `(self.opt)?;`
++
++error: this block may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:54:9
++   |
++LL | /         if self.opt.is_none() {
++LL | |             return None
++LL | |         }
++   | |_________^ help: replace it with: `self.opt?;`
++
++error: this block may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:58:17
++   |
++LL |           let _ = if self.opt.is_none() {
++   |  _________________^
++LL | |             return None;
++LL | |         } else {
++LL | |             self.opt
++LL | |         };
++   | |_________^ help: replace it with: `Some(self.opt?)`
++
++error: this if-let-else may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:64:17
++   |
++LL |           let _ = if let Some(x) = self.opt {
++   |  _________________^
++LL | |             x
++LL | |         } else {
++LL | |             return None;
++LL | |         };
++   | |_________^ help: replace it with: `self.opt?`
++
++error: this block may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:81:9
++   |
++LL | /         if self.opt.is_none() {
++LL | |             return None;
++LL | |         }
++   | |_________^ help: replace it with: `self.opt.as_ref()?;`
++
++error: this block may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:89:9
++   |
++LL | /         if self.opt.is_none() {
++LL | |             return None;
++LL | |         }
++   | |_________^ help: replace it with: `self.opt.as_ref()?;`
++
++error: this block may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:97:9
++   |
++LL | /         if self.opt.is_none() {
++LL | |             return None;
++LL | |         }
++   | |_________^ help: replace it with: `self.opt.as_ref()?;`
++
++error: this if-let-else may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:104:26
++   |
++LL |           let v: &Vec<_> = if let Some(ref v) = self.opt {
++   |  __________________________^
++LL | |             v
++LL | |         } else {
++LL | |             return None;
++LL | |         };
++   | |_________^ help: replace it with: `self.opt.as_ref()?`
++
++error: this if-let-else may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:114:17
++   |
++LL |           let v = if let Some(v) = self.opt {
++   |  _________________^
++LL | |             v
++LL | |         } else {
++LL | |             return None;
++LL | |         };
++   | |_________^ help: replace it with: `self.opt?`
++
++error: this block may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:129:5
++   |
++LL | /     if f().is_none() {
++LL | |         return None;
++LL | |     }
++   | |_____^ help: replace it with: `f()?;`
++
++error: aborting due to 11 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..628282509c1a51c22dc13735b1bb5adb523627ed
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++#[warn(clippy::range_zip_with_len)]
++fn main() {
++    let v1 = vec![1, 2, 3];
++    let v2 = vec![4, 5];
++    let _x = v1.iter().zip(0..v1.len());
++    let _y = v1.iter().zip(0..v2.len()); // No error
++}
++
++#[allow(unused)]
++fn no_panic_with_fake_range_types() {
++    struct Range {
++        foo: i32,
++    }
++
++    let _ = Range { foo: 0 };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d53c1edecac018f5d605b0653cc8f9c71603dd8e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: It is more idiomatic to use `v1.iter().enumerate()`
++  --> $DIR/range.rs:5:14
++   |
++LL |     let _x = v1.iter().zip(0..v1.len());
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::range-zip-with-len` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6b40211409974089e974b5e7b6ee6ce71c229a69
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++// run-rustfix
++
++#![allow(unused_parens)]
++
++fn f() -> usize {
++    42
++}
++
++#[warn(clippy::range_plus_one)]
++fn main() {
++    for _ in 0..2 {}
++    for _ in 0..=2 {}
++
++    for _ in 0..=3 {}
++    for _ in 0..=3 + 1 {}
++
++    for _ in 0..=5 {}
++    for _ in 0..=1 + 5 {}
++
++    for _ in 1..=1 {}
++    for _ in 1..=1 + 1 {}
++
++    for _ in 0..13 + 13 {}
++    for _ in 0..=13 - 7 {}
++
++    for _ in 0..=f() {}
++    for _ in 0..=(1 + f()) {}
++
++    let _ = ..11 - 1;
++    let _ = ..11;
++    let _ = ..11;
++    let _ = (1..=11);
++    let _ = ((f() + 1)..=f());
++
++    const ONE: usize = 1;
++    // integer consts are linted, too
++    for _ in 1..=ONE {}
++
++    let mut vec: Vec<()> = std::vec::Vec::new();
++    vec.drain(..);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3cfed4125b35c7434dda2950b72e243cbed6e2d5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++// run-rustfix
++
++#![allow(unused_parens)]
++
++fn f() -> usize {
++    42
++}
++
++#[warn(clippy::range_plus_one)]
++fn main() {
++    for _ in 0..2 {}
++    for _ in 0..=2 {}
++
++    for _ in 0..3 + 1 {}
++    for _ in 0..=3 + 1 {}
++
++    for _ in 0..1 + 5 {}
++    for _ in 0..=1 + 5 {}
++
++    for _ in 1..1 + 1 {}
++    for _ in 1..=1 + 1 {}
++
++    for _ in 0..13 + 13 {}
++    for _ in 0..=13 - 7 {}
++
++    for _ in 0..(1 + f()) {}
++    for _ in 0..=(1 + f()) {}
++
++    let _ = ..11 - 1;
++    let _ = ..=11 - 1;
++    let _ = ..=(11 - 1);
++    let _ = (1..11 + 1);
++    let _ = (f() + 1)..(f() + 1);
++
++    const ONE: usize = 1;
++    // integer consts are linted, too
++    for _ in 1..ONE + ONE {}
++
++    let mut vec: Vec<()> = std::vec::Vec::new();
++    vec.drain(..);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f72943a04f252af9b471324f225ed12a3d79fd4d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,60 @@@
++error: an inclusive range would be more readable
++  --> $DIR/range_plus_minus_one.rs:14:14
++   |
++LL |     for _ in 0..3 + 1 {}
++   |              ^^^^^^^^ help: use: `0..=3`
++   |
++   = note: `-D clippy::range-plus-one` implied by `-D warnings`
++
++error: an inclusive range would be more readable
++  --> $DIR/range_plus_minus_one.rs:17:14
++   |
++LL |     for _ in 0..1 + 5 {}
++   |              ^^^^^^^^ help: use: `0..=5`
++
++error: an inclusive range would be more readable
++  --> $DIR/range_plus_minus_one.rs:20:14
++   |
++LL |     for _ in 1..1 + 1 {}
++   |              ^^^^^^^^ help: use: `1..=1`
++
++error: an inclusive range would be more readable
++  --> $DIR/range_plus_minus_one.rs:26:14
++   |
++LL |     for _ in 0..(1 + f()) {}
++   |              ^^^^^^^^^^^^ help: use: `0..=f()`
++
++error: an exclusive range would be more readable
++  --> $DIR/range_plus_minus_one.rs:30:13
++   |
++LL |     let _ = ..=11 - 1;
++   |             ^^^^^^^^^ help: use: `..11`
++   |
++   = note: `-D clippy::range-minus-one` implied by `-D warnings`
++
++error: an exclusive range would be more readable
++  --> $DIR/range_plus_minus_one.rs:31:13
++   |
++LL |     let _ = ..=(11 - 1);
++   |             ^^^^^^^^^^^ help: use: `..11`
++
++error: an inclusive range would be more readable
++  --> $DIR/range_plus_minus_one.rs:32:13
++   |
++LL |     let _ = (1..11 + 1);
++   |             ^^^^^^^^^^^ help: use: `(1..=11)`
++
++error: an inclusive range would be more readable
++  --> $DIR/range_plus_minus_one.rs:33:13
++   |
++LL |     let _ = (f() + 1)..(f() + 1);
++   |             ^^^^^^^^^^^^^^^^^^^^ help: use: `((f() + 1)..=f())`
++
++error: an inclusive range would be more readable
++  --> $DIR/range_plus_minus_one.rs:37:14
++   |
++LL |     for _ in 1..ONE + ONE {}
++   |              ^^^^^^^^^^^^ help: use: `1..=ONE`
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..266358334587d0a6edb4a94e328e77d1a6515c93
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,48 @@@
++// run-rustfix
++#![warn(clippy::all)]
++#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
++#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
++
++use std::boxed::Box;
++use std::rc::Rc;
++
++pub struct MyStruct {}
++
++pub struct SubT<T> {
++    foo: T,
++}
++
++pub enum MyEnum {
++    One,
++    Two,
++}
++
++// Rc<&T>
++
++pub fn test1<T>(foo: &T) {}
++
++pub fn test2(foo: &MyStruct) {}
++
++pub fn test3(foo: &MyEnum) {}
++
++pub fn test4_neg(foo: Rc<SubT<&usize>>) {}
++
++// Rc<Rc<T>>
++
++pub fn test5(a: Rc<bool>) {}
++
++// Rc<Box<T>>
++
++pub fn test6(a: Box<bool>) {}
++
++// Box<&T>
++
++pub fn test7<T>(foo: &T) {}
++
++pub fn test8(foo: &MyStruct) {}
++
++pub fn test9(foo: &MyEnum) {}
++
++pub fn test10_neg(foo: Box<SubT<&usize>>) {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..677b3e56d4dcebe5ea58a9996b5cac7c5332cddc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,48 @@@
++// run-rustfix
++#![warn(clippy::all)]
++#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
++#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
++
++use std::boxed::Box;
++use std::rc::Rc;
++
++pub struct MyStruct {}
++
++pub struct SubT<T> {
++    foo: T,
++}
++
++pub enum MyEnum {
++    One,
++    Two,
++}
++
++// Rc<&T>
++
++pub fn test1<T>(foo: Rc<&T>) {}
++
++pub fn test2(foo: Rc<&MyStruct>) {}
++
++pub fn test3(foo: Rc<&MyEnum>) {}
++
++pub fn test4_neg(foo: Rc<SubT<&usize>>) {}
++
++// Rc<Rc<T>>
++
++pub fn test5(a: Rc<Rc<bool>>) {}
++
++// Rc<Box<T>>
++
++pub fn test6(a: Rc<Box<bool>>) {}
++
++// Box<&T>
++
++pub fn test7<T>(foo: Box<&T>) {}
++
++pub fn test8(foo: Box<&MyStruct>) {}
++
++pub fn test9(foo: Box<&MyEnum>) {}
++
++pub fn test10_neg(foo: Box<SubT<&usize>>) {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eaa57ce3024b604243166b7e78bcf1d58dc6f536
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++error: usage of `Rc<&T>`
++  --> $DIR/redundant_allocation.rs:22:22
++   |
++LL | pub fn test1<T>(foo: Rc<&T>) {}
++   |                      ^^^^^^ help: try: `&T`
++   |
++   = note: `-D clippy::redundant-allocation` implied by `-D warnings`
++
++error: usage of `Rc<&T>`
++  --> $DIR/redundant_allocation.rs:24:19
++   |
++LL | pub fn test2(foo: Rc<&MyStruct>) {}
++   |                   ^^^^^^^^^^^^^ help: try: `&MyStruct`
++
++error: usage of `Rc<&T>`
++  --> $DIR/redundant_allocation.rs:26:19
++   |
++LL | pub fn test3(foo: Rc<&MyEnum>) {}
++   |                   ^^^^^^^^^^^ help: try: `&MyEnum`
++
++error: usage of `Rc<Rc<T>>`
++  --> $DIR/redundant_allocation.rs:32:17
++   |
++LL | pub fn test5(a: Rc<Rc<bool>>) {}
++   |                 ^^^^^^^^^^^^ help: try: `Rc<bool>`
++
++error: usage of `Rc<Box<T>>`
++  --> $DIR/redundant_allocation.rs:36:17
++   |
++LL | pub fn test6(a: Rc<Box<bool>>) {}
++   |                 ^^^^^^^^^^^^^ help: try: `Box<bool>`
++
++error: usage of `Box<&T>`
++  --> $DIR/redundant_allocation.rs:40:22
++   |
++LL | pub fn test7<T>(foo: Box<&T>) {}
++   |                      ^^^^^^^ help: try: `&T`
++
++error: usage of `Box<&T>`
++  --> $DIR/redundant_allocation.rs:42:19
++   |
++LL | pub fn test8(foo: Box<&MyStruct>) {}
++   |                   ^^^^^^^^^^^^^^ help: try: `&MyStruct`
++
++error: usage of `Box<&T>`
++  --> $DIR/redundant_allocation.rs:44:19
++   |
++LL | pub fn test9(foo: Box<&MyEnum>) {}
++   |                   ^^^^^^^^^^^^ help: try: `&MyEnum`
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..764c10a6d398feaa87957ed4bdbe7f353f7fb657
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,172 @@@
++// run-rustfix
++// rustfix-only-machine-applicable
++
++use std::ffi::OsString;
++use std::path::Path;
++
++fn main() {
++    let _s = ["lorem", "ipsum"].join(" ");
++
++    let s = String::from("foo");
++    let _s = s;
++
++    let s = String::from("foo");
++    let _s = s;
++
++    let s = String::from("foo");
++    let _s = s;
++
++    let _s = Path::new("/a/b/").join("c");
++
++    let _s = Path::new("/a/b/").join("c");
++
++    let _s = OsString::new();
++
++    let _s = OsString::new();
++
++    // Check that lint level works
++    #[allow(clippy::redundant_clone)]
++    let _s = String::new().to_string();
++
++    let tup = (String::from("foo"),);
++    let _t = tup.0;
++
++    let tup_ref = &(String::from("foo"),);
++    let _s = tup_ref.0.clone(); // this `.clone()` cannot be removed
++
++    {
++        let x = String::new();
++        let y = &x;
++
++        let _x = x.clone(); // ok; `x` is borrowed by `y`
++
++        let _ = y.len();
++    }
++
++    let x = (String::new(),);
++    let _ = Some(String::new()).unwrap_or_else(|| x.0.clone()); // ok; closure borrows `x`
++
++    with_branch(Alpha, true);
++    cannot_double_move(Alpha);
++    cannot_move_from_type_with_drop();
++    borrower_propagation();
++    not_consumed();
++    issue_5405();
++}
++
++#[derive(Clone)]
++struct Alpha;
++fn with_branch(a: Alpha, b: bool) -> (Alpha, Alpha) {
++    if b {
++        (a.clone(), a)
++    } else {
++        (Alpha, a)
++    }
++}
++
++fn cannot_double_move(a: Alpha) -> (Alpha, Alpha) {
++    (a.clone(), a)
++}
++
++struct TypeWithDrop {
++    x: String,
++}
++
++impl Drop for TypeWithDrop {
++    fn drop(&mut self) {}
++}
++
++fn cannot_move_from_type_with_drop() -> String {
++    let s = TypeWithDrop { x: String::new() };
++    s.x.clone() // removing this `clone()` summons E0509
++}
++
++fn borrower_propagation() {
++    let s = String::new();
++    let t = String::new();
++
++    {
++        fn b() -> bool {
++            unimplemented!()
++        }
++        let _u = if b() { &s } else { &t };
++
++        // ok; `s` and `t` are possibly borrowed
++        let _s = s.clone();
++        let _t = t.clone();
++    }
++
++    {
++        let _u = || s.len();
++        let _v = [&t; 32];
++        let _s = s.clone(); // ok
++        let _t = t.clone(); // ok
++    }
++
++    {
++        let _u = {
++            let u = Some(&s);
++            let _ = s.clone(); // ok
++            u
++        };
++        let _s = s.clone(); // ok
++    }
++
++    {
++        use std::convert::identity as id;
++        let _u = id(id(&s));
++        let _s = s.clone(); // ok, `u` borrows `s`
++    }
++
++    let _s = s;
++    let _t = t;
++
++    #[derive(Clone)]
++    struct Foo {
++        x: usize,
++    }
++
++    {
++        let f = Foo { x: 123 };
++        let _x = Some(f.x);
++        let _f = f;
++    }
++
++    {
++        let f = Foo { x: 123 };
++        let _x = &f.x;
++        let _f = f.clone(); // ok
++    }
++}
++
++fn not_consumed() {
++    let x = std::path::PathBuf::from("home");
++    let y = x.join("matthias");
++    // join() creates a new owned PathBuf, does not take a &mut to x variable, thus the .clone() is
++    // redundant. (It also does not consume the PathBuf)
++
++    println!("x: {:?}, y: {:?}", x, y);
++
++    let mut s = String::new();
++    s.clone().push_str("foo"); // OK, removing this `clone()` will change the behavior.
++    s.push_str("bar");
++    assert_eq!(s, "bar");
++
++    let t = Some(s);
++    // OK
++    if let Some(x) = t.clone() {
++        println!("{}", x);
++    }
++    if let Some(x) = t {
++        println!("{}", x);
++    }
++}
++
++#[allow(clippy::clone_on_copy)]
++fn issue_5405() {
++    let a: [String; 1] = [String::from("foo")];
++    let _b: String = a[0].clone();
++
++    let c: [usize; 2] = [2, 3];
++    let _d: usize = c[1].clone();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..839747b131d77da22c7d559d0133f7006bd2d246
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,172 @@@
++// run-rustfix
++// rustfix-only-machine-applicable
++
++use std::ffi::OsString;
++use std::path::Path;
++
++fn main() {
++    let _s = ["lorem", "ipsum"].join(" ").to_string();
++
++    let s = String::from("foo");
++    let _s = s.clone();
++
++    let s = String::from("foo");
++    let _s = s.to_string();
++
++    let s = String::from("foo");
++    let _s = s.to_owned();
++
++    let _s = Path::new("/a/b/").join("c").to_owned();
++
++    let _s = Path::new("/a/b/").join("c").to_path_buf();
++
++    let _s = OsString::new().to_owned();
++
++    let _s = OsString::new().to_os_string();
++
++    // Check that lint level works
++    #[allow(clippy::redundant_clone)]
++    let _s = String::new().to_string();
++
++    let tup = (String::from("foo"),);
++    let _t = tup.0.clone();
++
++    let tup_ref = &(String::from("foo"),);
++    let _s = tup_ref.0.clone(); // this `.clone()` cannot be removed
++
++    {
++        let x = String::new();
++        let y = &x;
++
++        let _x = x.clone(); // ok; `x` is borrowed by `y`
++
++        let _ = y.len();
++    }
++
++    let x = (String::new(),);
++    let _ = Some(String::new()).unwrap_or_else(|| x.0.clone()); // ok; closure borrows `x`
++
++    with_branch(Alpha, true);
++    cannot_double_move(Alpha);
++    cannot_move_from_type_with_drop();
++    borrower_propagation();
++    not_consumed();
++    issue_5405();
++}
++
++#[derive(Clone)]
++struct Alpha;
++fn with_branch(a: Alpha, b: bool) -> (Alpha, Alpha) {
++    if b {
++        (a.clone(), a.clone())
++    } else {
++        (Alpha, a)
++    }
++}
++
++fn cannot_double_move(a: Alpha) -> (Alpha, Alpha) {
++    (a.clone(), a)
++}
++
++struct TypeWithDrop {
++    x: String,
++}
++
++impl Drop for TypeWithDrop {
++    fn drop(&mut self) {}
++}
++
++fn cannot_move_from_type_with_drop() -> String {
++    let s = TypeWithDrop { x: String::new() };
++    s.x.clone() // removing this `clone()` summons E0509
++}
++
++fn borrower_propagation() {
++    let s = String::new();
++    let t = String::new();
++
++    {
++        fn b() -> bool {
++            unimplemented!()
++        }
++        let _u = if b() { &s } else { &t };
++
++        // ok; `s` and `t` are possibly borrowed
++        let _s = s.clone();
++        let _t = t.clone();
++    }
++
++    {
++        let _u = || s.len();
++        let _v = [&t; 32];
++        let _s = s.clone(); // ok
++        let _t = t.clone(); // ok
++    }
++
++    {
++        let _u = {
++            let u = Some(&s);
++            let _ = s.clone(); // ok
++            u
++        };
++        let _s = s.clone(); // ok
++    }
++
++    {
++        use std::convert::identity as id;
++        let _u = id(id(&s));
++        let _s = s.clone(); // ok, `u` borrows `s`
++    }
++
++    let _s = s.clone();
++    let _t = t.clone();
++
++    #[derive(Clone)]
++    struct Foo {
++        x: usize,
++    }
++
++    {
++        let f = Foo { x: 123 };
++        let _x = Some(f.x);
++        let _f = f.clone();
++    }
++
++    {
++        let f = Foo { x: 123 };
++        let _x = &f.x;
++        let _f = f.clone(); // ok
++    }
++}
++
++fn not_consumed() {
++    let x = std::path::PathBuf::from("home");
++    let y = x.clone().join("matthias");
++    // join() creates a new owned PathBuf, does not take a &mut to x variable, thus the .clone() is
++    // redundant. (It also does not consume the PathBuf)
++
++    println!("x: {:?}, y: {:?}", x, y);
++
++    let mut s = String::new();
++    s.clone().push_str("foo"); // OK, removing this `clone()` will change the behavior.
++    s.push_str("bar");
++    assert_eq!(s, "bar");
++
++    let t = Some(s);
++    // OK
++    if let Some(x) = t.clone() {
++        println!("{}", x);
++    }
++    if let Some(x) = t {
++        println!("{}", x);
++    }
++}
++
++#[allow(clippy::clone_on_copy)]
++fn issue_5405() {
++    let a: [String; 1] = [String::from("foo")];
++    let _b: String = a[0].clone();
++
++    let c: [usize; 2] = [2, 3];
++    let _d: usize = c[1].clone();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eced198283ce8f5db36ef3fa7c4c2f93d38ec859
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,171 @@@
++error: redundant clone
++  --> $DIR/redundant_clone.rs:8:42
++   |
++LL |     let _s = ["lorem", "ipsum"].join(" ").to_string();
++   |                                          ^^^^^^^^^^^^ help: remove this
++   |
++   = note: `-D clippy::redundant-clone` implied by `-D warnings`
++note: this value is dropped without further use
++  --> $DIR/redundant_clone.rs:8:14
++   |
++LL |     let _s = ["lorem", "ipsum"].join(" ").to_string();
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: redundant clone
++  --> $DIR/redundant_clone.rs:11:15
++   |
++LL |     let _s = s.clone();
++   |               ^^^^^^^^ help: remove this
++   |
++note: this value is dropped without further use
++  --> $DIR/redundant_clone.rs:11:14
++   |
++LL |     let _s = s.clone();
++   |              ^
++
++error: redundant clone
++  --> $DIR/redundant_clone.rs:14:15
++   |
++LL |     let _s = s.to_string();
++   |               ^^^^^^^^^^^^ help: remove this
++   |
++note: this value is dropped without further use
++  --> $DIR/redundant_clone.rs:14:14
++   |
++LL |     let _s = s.to_string();
++   |              ^
++
++error: redundant clone
++  --> $DIR/redundant_clone.rs:17:15
++   |
++LL |     let _s = s.to_owned();
++   |               ^^^^^^^^^^^ help: remove this
++   |
++note: this value is dropped without further use
++  --> $DIR/redundant_clone.rs:17:14
++   |
++LL |     let _s = s.to_owned();
++   |              ^
++
++error: redundant clone
++  --> $DIR/redundant_clone.rs:19:42
++   |
++LL |     let _s = Path::new("/a/b/").join("c").to_owned();
++   |                                          ^^^^^^^^^^^ help: remove this
++   |
++note: this value is dropped without further use
++  --> $DIR/redundant_clone.rs:19:14
++   |
++LL |     let _s = Path::new("/a/b/").join("c").to_owned();
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: redundant clone
++  --> $DIR/redundant_clone.rs:21:42
++   |
++LL |     let _s = Path::new("/a/b/").join("c").to_path_buf();
++   |                                          ^^^^^^^^^^^^^^ help: remove this
++   |
++note: this value is dropped without further use
++  --> $DIR/redundant_clone.rs:21:14
++   |
++LL |     let _s = Path::new("/a/b/").join("c").to_path_buf();
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: redundant clone
++  --> $DIR/redundant_clone.rs:23:29
++   |
++LL |     let _s = OsString::new().to_owned();
++   |                             ^^^^^^^^^^^ help: remove this
++   |
++note: this value is dropped without further use
++  --> $DIR/redundant_clone.rs:23:14
++   |
++LL |     let _s = OsString::new().to_owned();
++   |              ^^^^^^^^^^^^^^^
++
++error: redundant clone
++  --> $DIR/redundant_clone.rs:25:29
++   |
++LL |     let _s = OsString::new().to_os_string();
++   |                             ^^^^^^^^^^^^^^^ help: remove this
++   |
++note: this value is dropped without further use
++  --> $DIR/redundant_clone.rs:25:14
++   |
++LL |     let _s = OsString::new().to_os_string();
++   |              ^^^^^^^^^^^^^^^
++
++error: redundant clone
++  --> $DIR/redundant_clone.rs:32:19
++   |
++LL |     let _t = tup.0.clone();
++   |                   ^^^^^^^^ help: remove this
++   |
++note: this value is dropped without further use
++  --> $DIR/redundant_clone.rs:32:14
++   |
++LL |     let _t = tup.0.clone();
++   |              ^^^^^
++
++error: redundant clone
++  --> $DIR/redundant_clone.rs:61:22
++   |
++LL |         (a.clone(), a.clone())
++   |                      ^^^^^^^^ help: remove this
++   |
++note: this value is dropped without further use
++  --> $DIR/redundant_clone.rs:61:21
++   |
++LL |         (a.clone(), a.clone())
++   |                     ^
++
++error: redundant clone
++  --> $DIR/redundant_clone.rs:121:15
++   |
++LL |     let _s = s.clone();
++   |               ^^^^^^^^ help: remove this
++   |
++note: this value is dropped without further use
++  --> $DIR/redundant_clone.rs:121:14
++   |
++LL |     let _s = s.clone();
++   |              ^
++
++error: redundant clone
++  --> $DIR/redundant_clone.rs:122:15
++   |
++LL |     let _t = t.clone();
++   |               ^^^^^^^^ help: remove this
++   |
++note: this value is dropped without further use
++  --> $DIR/redundant_clone.rs:122:14
++   |
++LL |     let _t = t.clone();
++   |              ^
++
++error: redundant clone
++  --> $DIR/redundant_clone.rs:132:19
++   |
++LL |         let _f = f.clone();
++   |                   ^^^^^^^^ help: remove this
++   |
++note: this value is dropped without further use
++  --> $DIR/redundant_clone.rs:132:18
++   |
++LL |         let _f = f.clone();
++   |                  ^
++
++error: redundant clone
++  --> $DIR/redundant_clone.rs:144:14
++   |
++LL |     let y = x.clone().join("matthias");
++   |              ^^^^^^^^ help: remove this
++   |
++note: cloned value is neither consumed nor mutated
++  --> $DIR/redundant_clone.rs:144:13
++   |
++LL |     let y = x.clone().join("matthias");
++   |             ^^^^^^^^^
++
++error: aborting due to 14 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bacd67db7c30571e1e12708e7ae4d911df272efe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++// non rustfixable, see redundant_closure_call_fixable.rs
++
++#![warn(clippy::redundant_closure_call)]
++
++fn main() {
++    let mut i = 1;
++    let mut k = (|m| m + 1)(i);
++
++    k = (|a, b| a * b)(1, 5);
++
++    let closure = || 32;
++    i = closure();
++
++    let closure = |i| i + 1;
++    i = closure(3);
++
++    i = closure(4);
++
++    #[allow(clippy::needless_return)]
++    (|| return 2)();
++    (|| -> Option<i32> { None? })();
++    (|| -> Result<i32, i32> { Err(2)? })();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..68c1416bb6b1a33ff731f9fa9895116248011966
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: Closure called just once immediately after it was declared
++  --> $DIR/redundant_closure_call.rs:12:5
++   |
++LL |     i = closure();
++   |     ^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::redundant-closure-call` implied by `-D warnings`
++
++error: Closure called just once immediately after it was declared
++  --> $DIR/redundant_closure_call.rs:15:5
++   |
++LL |     i = closure(3);
++   |     ^^^^^^^^^^^^^^
++
++error: Try not to call a closure in the expression where it is declared.
++  --> $DIR/redundant_closure_call.rs:7:17
++   |
++LL |     let mut k = (|m| m + 1)(i);
++   |                 ^^^^^^^^^^^^^^
++
++error: Try not to call a closure in the expression where it is declared.
++  --> $DIR/redundant_closure_call.rs:9:9
++   |
++LL |     k = (|a, b| a * b)(1, 5);
++   |         ^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0abca6fca0613b4469c74af12206f25c7a0a1b41
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++// run-rustfix
++
++#![warn(clippy::redundant_closure_call)]
++#![allow(unused)]
++
++fn main() {
++    let a = 42;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f8b9d37a5cc4e757efe2960f1a1a804a62e839cd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++// run-rustfix
++
++#![warn(clippy::redundant_closure_call)]
++#![allow(unused)]
++
++fn main() {
++    let a = (|| 42)();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e7737f9dd856fe6719da3275e182d4df6f865e8c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: Try not to call a closure in the expression where it is declared.
++  --> $DIR/redundant_closure_call_fixable.rs:7:13
++   |
++LL |     let a = (|| 42)();
++   |             ^^^^^^^^^ help: Try doing something like: : `42`
++   |
++   = note: `-D clippy::redundant-closure-call` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5b4b8eeedd4696162ea63ca250f8cd1e5b61eb0d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,71 @@@
++// run-rustfix
++#![warn(clippy::redundant_field_names)]
++#![allow(clippy::no_effect, dead_code, unused_variables)]
++
++#[macro_use]
++extern crate derive_new;
++
++use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
++
++mod foo {
++    pub const BAR: u8 = 0;
++}
++
++struct Person {
++    gender: u8,
++    age: u8,
++    name: u8,
++    buzz: u64,
++    foo: u8,
++}
++
++#[derive(new)]
++pub struct S {
++    v: String,
++}
++
++fn main() {
++    let gender: u8 = 42;
++    let age = 0;
++    let fizz: u64 = 0;
++    let name: u8 = 0;
++
++    let me = Person {
++        gender,
++        age,
++
++        name,          //should be ok
++        buzz: fizz,    //should be ok
++        foo: foo::BAR, //should be ok
++    };
++
++    // Range expressions
++    let (start, end) = (0, 0);
++
++    let _ = start..;
++    let _ = ..end;
++    let _ = start..end;
++
++    let _ = ..=end;
++    let _ = start..=end;
++
++    // Issue #2799
++    let _: Vec<_> = (start..end).collect();
++
++    // hand-written Range family structs are linted
++    let _ = RangeFrom { start };
++    let _ = RangeTo { end };
++    let _ = Range { start, end };
++    let _ = RangeInclusive::new(start, end);
++    let _ = RangeToInclusive { end };
++}
++
++fn issue_3476() {
++    fn foo<T>() {}
++
++    struct S {
++        foo: fn(),
++    }
++
++    S { foo: foo::<i32> };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3f97b80c56828c3842c451bb9b616bb3103e1eec
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,71 @@@
++// run-rustfix
++#![warn(clippy::redundant_field_names)]
++#![allow(clippy::no_effect, dead_code, unused_variables)]
++
++#[macro_use]
++extern crate derive_new;
++
++use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
++
++mod foo {
++    pub const BAR: u8 = 0;
++}
++
++struct Person {
++    gender: u8,
++    age: u8,
++    name: u8,
++    buzz: u64,
++    foo: u8,
++}
++
++#[derive(new)]
++pub struct S {
++    v: String,
++}
++
++fn main() {
++    let gender: u8 = 42;
++    let age = 0;
++    let fizz: u64 = 0;
++    let name: u8 = 0;
++
++    let me = Person {
++        gender: gender,
++        age: age,
++
++        name,          //should be ok
++        buzz: fizz,    //should be ok
++        foo: foo::BAR, //should be ok
++    };
++
++    // Range expressions
++    let (start, end) = (0, 0);
++
++    let _ = start..;
++    let _ = ..end;
++    let _ = start..end;
++
++    let _ = ..=end;
++    let _ = start..=end;
++
++    // Issue #2799
++    let _: Vec<_> = (start..end).collect();
++
++    // hand-written Range family structs are linted
++    let _ = RangeFrom { start: start };
++    let _ = RangeTo { end: end };
++    let _ = Range { start: start, end: end };
++    let _ = RangeInclusive::new(start, end);
++    let _ = RangeToInclusive { end: end };
++}
++
++fn issue_3476() {
++    fn foo<T>() {}
++
++    struct S {
++        foo: fn(),
++    }
++
++    S { foo: foo::<i32> };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7976292df224140b39a50d44c831c76072467708
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++error: redundant field names in struct initialization
++  --> $DIR/redundant_field_names.rs:34:9
++   |
++LL |         gender: gender,
++   |         ^^^^^^^^^^^^^^ help: replace it with: `gender`
++   |
++   = note: `-D clippy::redundant-field-names` implied by `-D warnings`
++
++error: redundant field names in struct initialization
++  --> $DIR/redundant_field_names.rs:35:9
++   |
++LL |         age: age,
++   |         ^^^^^^^^ help: replace it with: `age`
++
++error: redundant field names in struct initialization
++  --> $DIR/redundant_field_names.rs:56:25
++   |
++LL |     let _ = RangeFrom { start: start };
++   |                         ^^^^^^^^^^^^ help: replace it with: `start`
++
++error: redundant field names in struct initialization
++  --> $DIR/redundant_field_names.rs:57:23
++   |
++LL |     let _ = RangeTo { end: end };
++   |                       ^^^^^^^^ help: replace it with: `end`
++
++error: redundant field names in struct initialization
++  --> $DIR/redundant_field_names.rs:58:21
++   |
++LL |     let _ = Range { start: start, end: end };
++   |                     ^^^^^^^^^^^^ help: replace it with: `start`
++
++error: redundant field names in struct initialization
++  --> $DIR/redundant_field_names.rs:58:35
++   |
++LL |     let _ = Range { start: start, end: end };
++   |                                   ^^^^^^^^ help: replace it with: `end`
++
++error: redundant field names in struct initialization
++  --> $DIR/redundant_field_names.rs:60:32
++   |
++LL |     let _ = RangeToInclusive { end: end };
++   |                                ^^^^^^^^ help: replace it with: `end`
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fc8cb0e747c73e157e3c17306a2491352c208bf3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,119 @@@
++// run-rustfix
++
++#![warn(clippy::all)]
++#![warn(clippy::redundant_pattern_matching)]
++#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)]
++
++fn main() {
++    if Ok::<i32, i32>(42).is_ok() {}
++
++    if Err::<i32, i32>(42).is_err() {}
++
++    if None::<()>.is_none() {}
++
++    if Some(42).is_some() {}
++
++    if Some(42).is_some() {
++        foo();
++    } else {
++        bar();
++    }
++
++    while Some(42).is_some() {}
++
++    while Some(42).is_none() {}
++
++    while None::<()>.is_none() {}
++
++    while Ok::<i32, i32>(10).is_ok() {}
++
++    while Ok::<i32, i32>(10).is_err() {}
++
++    let mut v = vec![1, 2, 3];
++    while v.pop().is_some() {
++        foo();
++    }
++
++    if Ok::<i32, i32>(42).is_ok() {}
++
++    if Err::<i32, i32>(42).is_err() {}
++
++    if None::<i32>.is_none() {}
++
++    if Some(42).is_some() {}
++
++    if let Ok(x) = Ok::<i32, i32>(42) {
++        println!("{}", x);
++    }
++
++    Ok::<i32, i32>(42).is_ok();
++
++    Ok::<i32, i32>(42).is_err();
++
++    Err::<i32, i32>(42).is_err();
++
++    Err::<i32, i32>(42).is_ok();
++
++    Some(42).is_some();
++
++    None::<()>.is_none();
++
++    let _ = None::<()>.is_none();
++
++    let _ = if Ok::<usize, ()>(4).is_ok() { true } else { false };
++
++    let opt = Some(false);
++    let x = if opt.is_some() { true } else { false };
++    takes_bool(x);
++
++    issue5504();
++
++    let _ = if gen_opt().is_some() {
++        1
++    } else if gen_opt().is_none() {
++        2
++    } else if gen_res().is_ok() {
++        3
++    } else if gen_res().is_err() {
++        4
++    } else {
++        5
++    };
++}
++
++fn gen_opt() -> Option<()> {
++    None
++}
++
++fn gen_res() -> Result<(), ()> {
++    Ok(())
++}
++
++fn takes_bool(_: bool) {}
++
++fn foo() {}
++
++fn bar() {}
++
++macro_rules! m {
++    () => {
++        Some(42u32)
++    };
++}
++
++fn issue5504() {
++    fn result_opt() -> Result<Option<i32>, i32> {
++        Err(42)
++    }
++
++    fn try_result_opt() -> Result<i32, i32> {
++        while r#try!(result_opt()).is_some() {}
++        if r#try!(result_opt()).is_some() {}
++        Ok(42)
++    }
++
++    try_result_opt();
++
++    if m!().is_some() {}
++    while m!().is_some() {}
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..51912dade035677a5372aa97b871eb3aa2f1b62e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,140 @@@
++// run-rustfix
++
++#![warn(clippy::all)]
++#![warn(clippy::redundant_pattern_matching)]
++#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)]
++
++fn main() {
++    if let Ok(_) = Ok::<i32, i32>(42) {}
++
++    if let Err(_) = Err::<i32, i32>(42) {}
++
++    if let None = None::<()> {}
++
++    if let Some(_) = Some(42) {}
++
++    if let Some(_) = Some(42) {
++        foo();
++    } else {
++        bar();
++    }
++
++    while let Some(_) = Some(42) {}
++
++    while let None = Some(42) {}
++
++    while let None = None::<()> {}
++
++    while let Ok(_) = Ok::<i32, i32>(10) {}
++
++    while let Err(_) = Ok::<i32, i32>(10) {}
++
++    let mut v = vec![1, 2, 3];
++    while let Some(_) = v.pop() {
++        foo();
++    }
++
++    if Ok::<i32, i32>(42).is_ok() {}
++
++    if Err::<i32, i32>(42).is_err() {}
++
++    if None::<i32>.is_none() {}
++
++    if Some(42).is_some() {}
++
++    if let Ok(x) = Ok::<i32, i32>(42) {
++        println!("{}", x);
++    }
++
++    match Ok::<i32, i32>(42) {
++        Ok(_) => true,
++        Err(_) => false,
++    };
++
++    match Ok::<i32, i32>(42) {
++        Ok(_) => false,
++        Err(_) => true,
++    };
++
++    match Err::<i32, i32>(42) {
++        Ok(_) => false,
++        Err(_) => true,
++    };
++
++    match Err::<i32, i32>(42) {
++        Ok(_) => true,
++        Err(_) => false,
++    };
++
++    match Some(42) {
++        Some(_) => true,
++        None => false,
++    };
++
++    match None::<()> {
++        Some(_) => false,
++        None => true,
++    };
++
++    let _ = match None::<()> {
++        Some(_) => false,
++        None => true,
++    };
++
++    let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false };
++
++    let opt = Some(false);
++    let x = if let Some(_) = opt { true } else { false };
++    takes_bool(x);
++
++    issue5504();
++
++    let _ = if let Some(_) = gen_opt() {
++        1
++    } else if let None = gen_opt() {
++        2
++    } else if let Ok(_) = gen_res() {
++        3
++    } else if let Err(_) = gen_res() {
++        4
++    } else {
++        5
++    };
++}
++
++fn gen_opt() -> Option<()> {
++    None
++}
++
++fn gen_res() -> Result<(), ()> {
++    Ok(())
++}
++
++fn takes_bool(_: bool) {}
++
++fn foo() {}
++
++fn bar() {}
++
++macro_rules! m {
++    () => {
++        Some(42u32)
++    };
++}
++
++fn issue5504() {
++    fn result_opt() -> Result<Option<i32>, i32> {
++        Err(42)
++    }
++
++    fn try_result_opt() -> Result<i32, i32> {
++        while let Some(_) = r#try!(result_opt()) {}
++        if let Some(_) = r#try!(result_opt()) {}
++        Ok(42)
++    }
++
++    try_result_opt();
++
++    if let Some(_) = m!() {}
++    while let Some(_) = m!() {}
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b58deb7954efe06b90c8516d1b4d52c614b901db
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,194 @@@
++error: redundant pattern matching, consider using `is_ok()`
++  --> $DIR/redundant_pattern_matching.rs:8:12
++   |
++LL |     if let Ok(_) = Ok::<i32, i32>(42) {}
++   |     -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
++   |
++   = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
++
++error: redundant pattern matching, consider using `is_err()`
++  --> $DIR/redundant_pattern_matching.rs:10:12
++   |
++LL |     if let Err(_) = Err::<i32, i32>(42) {}
++   |     -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
++
++error: redundant pattern matching, consider using `is_none()`
++  --> $DIR/redundant_pattern_matching.rs:12:12
++   |
++LL |     if let None = None::<()> {}
++   |     -------^^^^------------- help: try this: `if None::<()>.is_none()`
++
++error: redundant pattern matching, consider using `is_some()`
++  --> $DIR/redundant_pattern_matching.rs:14:12
++   |
++LL |     if let Some(_) = Some(42) {}
++   |     -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
++
++error: redundant pattern matching, consider using `is_some()`
++  --> $DIR/redundant_pattern_matching.rs:16:12
++   |
++LL |     if let Some(_) = Some(42) {
++   |     -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
++
++error: redundant pattern matching, consider using `is_some()`
++  --> $DIR/redundant_pattern_matching.rs:22:15
++   |
++LL |     while let Some(_) = Some(42) {}
++   |     ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
++
++error: redundant pattern matching, consider using `is_none()`
++  --> $DIR/redundant_pattern_matching.rs:24:15
++   |
++LL |     while let None = Some(42) {}
++   |     ----------^^^^----------- help: try this: `while Some(42).is_none()`
++
++error: redundant pattern matching, consider using `is_none()`
++  --> $DIR/redundant_pattern_matching.rs:26:15
++   |
++LL |     while let None = None::<()> {}
++   |     ----------^^^^------------- help: try this: `while None::<()>.is_none()`
++
++error: redundant pattern matching, consider using `is_ok()`
++  --> $DIR/redundant_pattern_matching.rs:28:15
++   |
++LL |     while let Ok(_) = Ok::<i32, i32>(10) {}
++   |     ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
++
++error: redundant pattern matching, consider using `is_err()`
++  --> $DIR/redundant_pattern_matching.rs:30:15
++   |
++LL |     while let Err(_) = Ok::<i32, i32>(10) {}
++   |     ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
++
++error: redundant pattern matching, consider using `is_some()`
++  --> $DIR/redundant_pattern_matching.rs:33:15
++   |
++LL |     while let Some(_) = v.pop() {
++   |     ----------^^^^^^^---------- help: try this: `while v.pop().is_some()`
++
++error: redundant pattern matching, consider using `is_ok()`
++  --> $DIR/redundant_pattern_matching.rs:49:5
++   |
++LL | /     match Ok::<i32, i32>(42) {
++LL | |         Ok(_) => true,
++LL | |         Err(_) => false,
++LL | |     };
++   | |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
++
++error: redundant pattern matching, consider using `is_err()`
++  --> $DIR/redundant_pattern_matching.rs:54:5
++   |
++LL | /     match Ok::<i32, i32>(42) {
++LL | |         Ok(_) => false,
++LL | |         Err(_) => true,
++LL | |     };
++   | |_____^ help: try this: `Ok::<i32, i32>(42).is_err()`
++
++error: redundant pattern matching, consider using `is_err()`
++  --> $DIR/redundant_pattern_matching.rs:59:5
++   |
++LL | /     match Err::<i32, i32>(42) {
++LL | |         Ok(_) => false,
++LL | |         Err(_) => true,
++LL | |     };
++   | |_____^ help: try this: `Err::<i32, i32>(42).is_err()`
++
++error: redundant pattern matching, consider using `is_ok()`
++  --> $DIR/redundant_pattern_matching.rs:64:5
++   |
++LL | /     match Err::<i32, i32>(42) {
++LL | |         Ok(_) => true,
++LL | |         Err(_) => false,
++LL | |     };
++   | |_____^ help: try this: `Err::<i32, i32>(42).is_ok()`
++
++error: redundant pattern matching, consider using `is_some()`
++  --> $DIR/redundant_pattern_matching.rs:69:5
++   |
++LL | /     match Some(42) {
++LL | |         Some(_) => true,
++LL | |         None => false,
++LL | |     };
++   | |_____^ help: try this: `Some(42).is_some()`
++
++error: redundant pattern matching, consider using `is_none()`
++  --> $DIR/redundant_pattern_matching.rs:74:5
++   |
++LL | /     match None::<()> {
++LL | |         Some(_) => false,
++LL | |         None => true,
++LL | |     };
++   | |_____^ help: try this: `None::<()>.is_none()`
++
++error: redundant pattern matching, consider using `is_none()`
++  --> $DIR/redundant_pattern_matching.rs:79:13
++   |
++LL |       let _ = match None::<()> {
++   |  _____________^
++LL | |         Some(_) => false,
++LL | |         None => true,
++LL | |     };
++   | |_____^ help: try this: `None::<()>.is_none()`
++
++error: redundant pattern matching, consider using `is_ok()`
++  --> $DIR/redundant_pattern_matching.rs:84:20
++   |
++LL |     let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false };
++   |             -------^^^^^--------------------- help: try this: `if Ok::<usize, ()>(4).is_ok()`
++
++error: redundant pattern matching, consider using `is_some()`
++  --> $DIR/redundant_pattern_matching.rs:87:20
++   |
++LL |     let x = if let Some(_) = opt { true } else { false };
++   |             -------^^^^^^^------ help: try this: `if opt.is_some()`
++
++error: redundant pattern matching, consider using `is_some()`
++  --> $DIR/redundant_pattern_matching.rs:92:20
++   |
++LL |     let _ = if let Some(_) = gen_opt() {
++   |             -------^^^^^^^------------ help: try this: `if gen_opt().is_some()`
++
++error: redundant pattern matching, consider using `is_none()`
++  --> $DIR/redundant_pattern_matching.rs:94:19
++   |
++LL |     } else if let None = gen_opt() {
++   |            -------^^^^------------ help: try this: `if gen_opt().is_none()`
++
++error: redundant pattern matching, consider using `is_ok()`
++  --> $DIR/redundant_pattern_matching.rs:96:19
++   |
++LL |     } else if let Ok(_) = gen_res() {
++   |            -------^^^^^------------ help: try this: `if gen_res().is_ok()`
++
++error: redundant pattern matching, consider using `is_err()`
++  --> $DIR/redundant_pattern_matching.rs:98:19
++   |
++LL |     } else if let Err(_) = gen_res() {
++   |            -------^^^^^^------------ help: try this: `if gen_res().is_err()`
++
++error: redundant pattern matching, consider using `is_some()`
++  --> $DIR/redundant_pattern_matching.rs:131:19
++   |
++LL |         while let Some(_) = r#try!(result_opt()) {}
++   |         ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()`
++
++error: redundant pattern matching, consider using `is_some()`
++  --> $DIR/redundant_pattern_matching.rs:132:16
++   |
++LL |         if let Some(_) = r#try!(result_opt()) {}
++   |         -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()`
++
++error: redundant pattern matching, consider using `is_some()`
++  --> $DIR/redundant_pattern_matching.rs:138:12
++   |
++LL |     if let Some(_) = m!() {}
++   |     -------^^^^^^^------- help: try this: `if m!().is_some()`
++
++error: redundant pattern matching, consider using `is_some()`
++  --> $DIR/redundant_pattern_matching.rs:139:15
++   |
++LL |     while let Some(_) = m!() {}
++   |     ----------^^^^^^^------- help: try this: `while m!().is_some()`
++
++error: aborting due to 28 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..25f2fd061b88ef0a014f078c8752796354766195
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,107 @@@
++// run-rustfix
++#![allow(dead_code)]
++#![warn(clippy::redundant_pub_crate)]
++
++mod m1 {
++    fn f() {}
++    pub fn g() {} // private due to m1
++    pub fn h() {}
++
++    mod m1_1 {
++        fn f() {}
++        pub fn g() {} // private due to m1_1 and m1
++        pub fn h() {}
++    }
++
++    pub mod m1_2 {
++        // ^ private due to m1
++        fn f() {}
++        pub fn g() {} // private due to m1_2 and m1
++        pub fn h() {}
++    }
++
++    pub mod m1_3 {
++        fn f() {}
++        pub fn g() {} // private due to m1
++        pub fn h() {}
++    }
++}
++
++pub(crate) mod m2 {
++    fn f() {}
++    pub fn g() {} // already crate visible due to m2
++    pub fn h() {}
++
++    mod m2_1 {
++        fn f() {}
++        pub fn g() {} // private due to m2_1
++        pub fn h() {}
++    }
++
++    pub mod m2_2 {
++        // ^ already crate visible due to m2
++        fn f() {}
++        pub fn g() {} // already crate visible due to m2_2 and m2
++        pub fn h() {}
++    }
++
++    pub mod m2_3 {
++        fn f() {}
++        pub fn g() {} // already crate visible due to m2
++        pub fn h() {}
++    }
++}
++
++pub mod m3 {
++    fn f() {}
++    pub(crate) fn g() {} // ok: m3 is exported
++    pub fn h() {}
++
++    mod m3_1 {
++        fn f() {}
++        pub fn g() {} // private due to m3_1
++        pub fn h() {}
++    }
++
++    pub(crate) mod m3_2 {
++        // ^ ok
++        fn f() {}
++        pub fn g() {} // already crate visible due to m3_2
++        pub fn h() {}
++    }
++
++    pub mod m3_3 {
++        fn f() {}
++        pub(crate) fn g() {} // ok: m3 and m3_3 are exported
++        pub fn h() {}
++    }
++}
++
++mod m4 {
++    fn f() {}
++    pub fn g() {} // private: not re-exported by `pub use m4::*`
++    pub fn h() {}
++
++    mod m4_1 {
++        fn f() {}
++        pub fn g() {} // private due to m4_1
++        pub fn h() {}
++    }
++
++    pub mod m4_2 {
++        // ^ private: not re-exported by `pub use m4::*`
++        fn f() {}
++        pub fn g() {} // private due to m4_2
++        pub fn h() {}
++    }
++
++    pub mod m4_3 {
++        fn f() {}
++        pub(crate) fn g() {} // ok: m4_3 is re-exported by `pub use m4::*`
++        pub fn h() {}
++    }
++}
++
++pub use m4::*;
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..616286b4f39f4fb7f04910d92416e8970f0d7c4c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,107 @@@
++// run-rustfix
++#![allow(dead_code)]
++#![warn(clippy::redundant_pub_crate)]
++
++mod m1 {
++    fn f() {}
++    pub(crate) fn g() {} // private due to m1
++    pub fn h() {}
++
++    mod m1_1 {
++        fn f() {}
++        pub(crate) fn g() {} // private due to m1_1 and m1
++        pub fn h() {}
++    }
++
++    pub(crate) mod m1_2 {
++        // ^ private due to m1
++        fn f() {}
++        pub(crate) fn g() {} // private due to m1_2 and m1
++        pub fn h() {}
++    }
++
++    pub mod m1_3 {
++        fn f() {}
++        pub(crate) fn g() {} // private due to m1
++        pub fn h() {}
++    }
++}
++
++pub(crate) mod m2 {
++    fn f() {}
++    pub(crate) fn g() {} // already crate visible due to m2
++    pub fn h() {}
++
++    mod m2_1 {
++        fn f() {}
++        pub(crate) fn g() {} // private due to m2_1
++        pub fn h() {}
++    }
++
++    pub(crate) mod m2_2 {
++        // ^ already crate visible due to m2
++        fn f() {}
++        pub(crate) fn g() {} // already crate visible due to m2_2 and m2
++        pub fn h() {}
++    }
++
++    pub mod m2_3 {
++        fn f() {}
++        pub(crate) fn g() {} // already crate visible due to m2
++        pub fn h() {}
++    }
++}
++
++pub mod m3 {
++    fn f() {}
++    pub(crate) fn g() {} // ok: m3 is exported
++    pub fn h() {}
++
++    mod m3_1 {
++        fn f() {}
++        pub(crate) fn g() {} // private due to m3_1
++        pub fn h() {}
++    }
++
++    pub(crate) mod m3_2 {
++        // ^ ok
++        fn f() {}
++        pub(crate) fn g() {} // already crate visible due to m3_2
++        pub fn h() {}
++    }
++
++    pub mod m3_3 {
++        fn f() {}
++        pub(crate) fn g() {} // ok: m3 and m3_3 are exported
++        pub fn h() {}
++    }
++}
++
++mod m4 {
++    fn f() {}
++    pub(crate) fn g() {} // private: not re-exported by `pub use m4::*`
++    pub fn h() {}
++
++    mod m4_1 {
++        fn f() {}
++        pub(crate) fn g() {} // private due to m4_1
++        pub fn h() {}
++    }
++
++    pub(crate) mod m4_2 {
++        // ^ private: not re-exported by `pub use m4::*`
++        fn f() {}
++        pub(crate) fn g() {} // private due to m4_2
++        pub fn h() {}
++    }
++
++    pub mod m4_3 {
++        fn f() {}
++        pub(crate) fn g() {} // ok: m4_3 is re-exported by `pub use m4::*`
++        pub fn h() {}
++    }
++}
++
++pub use m4::*;
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6fccdaa4e20374d16915519368f6d6ec11f10641
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,132 @@@
++error: pub(crate) function inside private module
++  --> $DIR/redundant_pub_crate.rs:7:5
++   |
++LL |     pub(crate) fn g() {} // private due to m1
++   |     ----------^^^^^
++   |     |
++   |     help: consider using: `pub`
++   |
++   = note: `-D clippy::redundant-pub-crate` implied by `-D warnings`
++
++error: pub(crate) function inside private module
++  --> $DIR/redundant_pub_crate.rs:12:9
++   |
++LL |         pub(crate) fn g() {} // private due to m1_1 and m1
++   |         ----------^^^^^
++   |         |
++   |         help: consider using: `pub`
++
++error: pub(crate) module inside private module
++  --> $DIR/redundant_pub_crate.rs:16:5
++   |
++LL |     pub(crate) mod m1_2 {
++   |     ----------^^^^^^^^^
++   |     |
++   |     help: consider using: `pub`
++
++error: pub(crate) function inside private module
++  --> $DIR/redundant_pub_crate.rs:19:9
++   |
++LL |         pub(crate) fn g() {} // private due to m1_2 and m1
++   |         ----------^^^^^
++   |         |
++   |         help: consider using: `pub`
++
++error: pub(crate) function inside private module
++  --> $DIR/redundant_pub_crate.rs:25:9
++   |
++LL |         pub(crate) fn g() {} // private due to m1
++   |         ----------^^^^^
++   |         |
++   |         help: consider using: `pub`
++
++error: pub(crate) function inside private module
++  --> $DIR/redundant_pub_crate.rs:32:5
++   |
++LL |     pub(crate) fn g() {} // already crate visible due to m2
++   |     ----------^^^^^
++   |     |
++   |     help: consider using: `pub`
++
++error: pub(crate) function inside private module
++  --> $DIR/redundant_pub_crate.rs:37:9
++   |
++LL |         pub(crate) fn g() {} // private due to m2_1
++   |         ----------^^^^^
++   |         |
++   |         help: consider using: `pub`
++
++error: pub(crate) module inside private module
++  --> $DIR/redundant_pub_crate.rs:41:5
++   |
++LL |     pub(crate) mod m2_2 {
++   |     ----------^^^^^^^^^
++   |     |
++   |     help: consider using: `pub`
++
++error: pub(crate) function inside private module
++  --> $DIR/redundant_pub_crate.rs:44:9
++   |
++LL |         pub(crate) fn g() {} // already crate visible due to m2_2 and m2
++   |         ----------^^^^^
++   |         |
++   |         help: consider using: `pub`
++
++error: pub(crate) function inside private module
++  --> $DIR/redundant_pub_crate.rs:50:9
++   |
++LL |         pub(crate) fn g() {} // already crate visible due to m2
++   |         ----------^^^^^
++   |         |
++   |         help: consider using: `pub`
++
++error: pub(crate) function inside private module
++  --> $DIR/redundant_pub_crate.rs:62:9
++   |
++LL |         pub(crate) fn g() {} // private due to m3_1
++   |         ----------^^^^^
++   |         |
++   |         help: consider using: `pub`
++
++error: pub(crate) function inside private module
++  --> $DIR/redundant_pub_crate.rs:69:9
++   |
++LL |         pub(crate) fn g() {} // already crate visible due to m3_2
++   |         ----------^^^^^
++   |         |
++   |         help: consider using: `pub`
++
++error: pub(crate) function inside private module
++  --> $DIR/redundant_pub_crate.rs:82:5
++   |
++LL |     pub(crate) fn g() {} // private: not re-exported by `pub use m4::*`
++   |     ----------^^^^^
++   |     |
++   |     help: consider using: `pub`
++
++error: pub(crate) function inside private module
++  --> $DIR/redundant_pub_crate.rs:87:9
++   |
++LL |         pub(crate) fn g() {} // private due to m4_1
++   |         ----------^^^^^
++   |         |
++   |         help: consider using: `pub`
++
++error: pub(crate) module inside private module
++  --> $DIR/redundant_pub_crate.rs:91:5
++   |
++LL |     pub(crate) mod m4_2 {
++   |     ----------^^^^^^^^^
++   |     |
++   |     help: consider using: `pub`
++
++error: pub(crate) function inside private module
++  --> $DIR/redundant_pub_crate.rs:94:9
++   |
++LL |         pub(crate) fn g() {} // private due to m4_2
++   |         ----------^^^^^
++   |         |
++   |         help: consider using: `pub`
++
++error: aborting due to 16 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..921249606ad2716da28f748c27da23342d52123d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++// run-rustfix
++
++#![allow(unused)]
++
++#[derive(Debug)]
++struct Foo {}
++
++const VAR_ONE: &str = "Test constant #1"; // ERROR Consider removing 'static.
++
++const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning.
++
++const VAR_THREE: &[&str] = &["one", "two"]; // ERROR Consider removing 'static
++
++const VAR_FOUR: (&str, (&str, &str), &str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
++
++const VAR_SIX: &u8 = &5;
++
++const VAR_HEIGHT: &Foo = &Foo {};
++
++const VAR_SLICE: &[u8] = b"Test constant #1"; // ERROR Consider removing 'static.
++
++const VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static.
++
++const VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static.
++
++static STATIC_VAR_ONE: &str = "Test static #1"; // ERROR Consider removing 'static.
++
++static STATIC_VAR_TWO: &str = "Test static #2"; // This line should not raise a warning.
++
++static STATIC_VAR_THREE: &[&str] = &["one", "two"]; // ERROR Consider removing 'static
++
++static STATIC_VAR_SIX: &u8 = &5;
++
++static STATIC_VAR_HEIGHT: &Foo = &Foo {};
++
++static STATIC_VAR_SLICE: &[u8] = b"Test static #3"; // ERROR Consider removing 'static.
++
++static STATIC_VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static.
++
++static STATIC_VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static.
++
++fn main() {
++    let false_positive: &'static str = "test";
++}
++
++trait Bar {
++    const TRAIT_VAR: &'static str;
++}
++
++impl Foo {
++    const IMPL_VAR: &'static str = "var";
++}
++
++impl Bar for Foo {
++    const TRAIT_VAR: &'static str = "foo";
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4d4b249d076ffca81ecd13eca4022b2e960c5c38
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++// run-rustfix
++
++#![allow(unused)]
++
++#[derive(Debug)]
++struct Foo {}
++
++const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
++
++const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning.
++
++const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
++
++const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
++
++const VAR_SIX: &'static u8 = &5;
++
++const VAR_HEIGHT: &'static Foo = &Foo {};
++
++const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static.
++
++const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
++
++const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
++
++static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static.
++
++static STATIC_VAR_TWO: &str = "Test static #2"; // This line should not raise a warning.
++
++static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
++
++static STATIC_VAR_SIX: &'static u8 = &5;
++
++static STATIC_VAR_HEIGHT: &'static Foo = &Foo {};
++
++static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static.
++
++static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
++
++static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
++
++fn main() {
++    let false_positive: &'static str = "test";
++}
++
++trait Bar {
++    const TRAIT_VAR: &'static str;
++}
++
++impl Foo {
++    const IMPL_VAR: &'static str = "var";
++}
++
++impl Bar for Foo {
++    const TRAIT_VAR: &'static str = "foo";
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3c3d2eacd8d9c51c64a1deaf1e4decb4b18dcc0e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,100 @@@
++error: Constants have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes.rs:8:17
++   |
++LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
++   |                -^^^^^^^---- help: consider removing `'static`: `&str`
++   |
++   = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings`
++
++error: Constants have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes.rs:12:21
++   |
++LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
++   |                    -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Constants have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes.rs:14:32
++   |
++LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
++   |                               -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Constants have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes.rs:14:47
++   |
++LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
++   |                                              -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Constants have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes.rs:16:17
++   |
++LL | const VAR_SIX: &'static u8 = &5;
++   |                -^^^^^^^--- help: consider removing `'static`: `&u8`
++
++error: Constants have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes.rs:18:20
++   |
++LL | const VAR_HEIGHT: &'static Foo = &Foo {};
++   |                   -^^^^^^^---- help: consider removing `'static`: `&Foo`
++
++error: Constants have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes.rs:20:19
++   |
++LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static.
++   |                  -^^^^^^^----- help: consider removing `'static`: `&[u8]`
++
++error: Constants have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes.rs:22:19
++   |
++LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
++   |                  -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
++
++error: Constants have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes.rs:24:19
++   |
++LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
++   |                  -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
++
++error: Statics have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes.rs:26:25
++   |
++LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static.
++   |                        -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Statics have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes.rs:30:29
++   |
++LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
++   |                            -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Statics have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes.rs:32:25
++   |
++LL | static STATIC_VAR_SIX: &'static u8 = &5;
++   |                        -^^^^^^^--- help: consider removing `'static`: `&u8`
++
++error: Statics have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes.rs:34:28
++   |
++LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {};
++   |                           -^^^^^^^---- help: consider removing `'static`: `&Foo`
++
++error: Statics have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes.rs:36:27
++   |
++LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static.
++   |                          -^^^^^^^----- help: consider removing `'static`: `&[u8]`
++
++error: Statics have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes.rs:38:27
++   |
++LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
++   |                          -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
++
++error: Statics have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes.rs:40:27
++   |
++LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
++   |                          -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
++
++error: aborting due to 16 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f57dd58e230a31b05c40919f426c7362d0c513b1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++// these are rustfixable, but run-rustfix tests cannot handle them
++
++const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
++
++const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
++
++static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
++
++static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
++
++static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..afc853dcfce8357fd01e4c66f85422c5f473f5a9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,64 @@@
++error: Constants have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes_multiple.rs:3:18
++   |
++LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
++   |                 -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]`
++   |
++   = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings`
++
++error: Constants have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes_multiple.rs:3:30
++   |
++LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
++   |                             -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Constants have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes_multiple.rs:5:29
++   |
++LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
++   |                            -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]`
++
++error: Constants have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes_multiple.rs:5:39
++   |
++LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
++   |                                      -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Statics have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes_multiple.rs:7:40
++   |
++LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
++   |                                       -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Statics have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes_multiple.rs:7:55
++   |
++LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
++   |                                                      -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Statics have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes_multiple.rs:9:26
++   |
++LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
++   |                         -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]`
++
++error: Statics have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes_multiple.rs:9:38
++   |
++LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
++   |                                     -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: Statics have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes_multiple.rs:11:37
++   |
++LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
++   |                                    -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]`
++
++error: Statics have by default a `'static` lifetime
++  --> $DIR/redundant_static_lifetimes_multiple.rs:11:47
++   |
++LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
++   |                                              -^^^^^^^---- help: consider removing `'static`: `&str`
++
++error: aborting due to 10 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b523fa5b711ae68e611aa3580ea59e9ccc2a7dc9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,79 @@@
++#![allow(unused)]
++#![warn(clippy::invalid_regex, clippy::trivial_regex, clippy::regex_macro)]
++
++extern crate regex;
++
++use regex::bytes::{Regex as BRegex, RegexBuilder as BRegexBuilder, RegexSet as BRegexSet};
++use regex::{Regex, RegexBuilder, RegexSet};
++
++const OPENING_PAREN: &str = "(";
++const NOT_A_REAL_REGEX: &str = "foobar";
++
++fn syntax_error() {
++    let pipe_in_wrong_position = Regex::new("|");
++    let pipe_in_wrong_position_builder = RegexBuilder::new("|");
++    let wrong_char_ranice = Regex::new("[z-a]");
++    let some_unicode = Regex::new("[é-è]");
++
++    let some_regex = Regex::new(OPENING_PAREN);
++
++    let binary_pipe_in_wrong_position = BRegex::new("|");
++    let some_binary_regex = BRegex::new(OPENING_PAREN);
++    let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN);
++
++    let closing_paren = ")";
++    let not_linted = Regex::new(closing_paren);
++
++    let set = RegexSet::new(&[r"[a-z]+@[a-z]+\.(com|org|net)", r"[a-z]+\.(com|org|net)"]);
++    let bset = BRegexSet::new(&[
++        r"[a-z]+@[a-z]+\.(com|org|net)",
++        r"[a-z]+\.(com|org|net)",
++        r".", // regression test
++    ]);
++
++    let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]);
++    let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]);
++
++    let raw_string_error = Regex::new(r"[...\/...]");
++    let raw_string_error = Regex::new(r#"[...\/...]"#);
++}
++
++fn trivial_regex() {
++    let trivial_eq = Regex::new("^foobar$");
++
++    let trivial_eq_builder = RegexBuilder::new("^foobar$");
++
++    let trivial_starts_with = Regex::new("^foobar");
++
++    let trivial_ends_with = Regex::new("foobar$");
++
++    let trivial_contains = Regex::new("foobar");
++
++    let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
++
++    let trivial_backslash = Regex::new("a\\.b");
++
++    // unlikely corner cases
++    let trivial_empty = Regex::new("");
++
++    let trivial_empty = Regex::new("^");
++
++    let trivial_empty = Regex::new("^$");
++
++    let binary_trivial_empty = BRegex::new("^$");
++
++    // non-trivial regexes
++    let non_trivial_dot = Regex::new("a.b");
++    let non_trivial_dot_builder = RegexBuilder::new("a.b");
++    let non_trivial_eq = Regex::new("^foo|bar$");
++    let non_trivial_starts_with = Regex::new("^foo|bar");
++    let non_trivial_ends_with = Regex::new("^foo|bar");
++    let non_trivial_ends_with = Regex::new("foo|bar");
++    let non_trivial_binary = BRegex::new("foo|bar");
++    let non_trivial_binary_builder = BRegexBuilder::new("foo|bar");
++}
++
++fn main() {
++    syntax_error();
++    trivial_regex();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1394a9b63bc61f3000daa60ab72c3657f8706885
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,171 @@@
++error: trivial regex
++  --> $DIR/regex.rs:13:45
++   |
++LL |     let pipe_in_wrong_position = Regex::new("|");
++   |                                             ^^^
++   |
++   = note: `-D clippy::trivial-regex` implied by `-D warnings`
++   = help: the regex is unlikely to be useful as it is
++
++error: trivial regex
++  --> $DIR/regex.rs:14:60
++   |
++LL |     let pipe_in_wrong_position_builder = RegexBuilder::new("|");
++   |                                                            ^^^
++   |
++   = help: the regex is unlikely to be useful as it is
++
++error: regex syntax error: invalid character class range, the start must be <= the end
++  --> $DIR/regex.rs:15:42
++   |
++LL |     let wrong_char_ranice = Regex::new("[z-a]");
++   |                                          ^^^
++   |
++   = note: `-D clippy::invalid-regex` implied by `-D warnings`
++
++error: regex syntax error: invalid character class range, the start must be <= the end
++  --> $DIR/regex.rs:16:37
++   |
++LL |     let some_unicode = Regex::new("[é-è]");
++   |                                     ^^^
++
++error: regex syntax error on position 0: unclosed group
++  --> $DIR/regex.rs:18:33
++   |
++LL |     let some_regex = Regex::new(OPENING_PAREN);
++   |                                 ^^^^^^^^^^^^^
++
++error: trivial regex
++  --> $DIR/regex.rs:20:53
++   |
++LL |     let binary_pipe_in_wrong_position = BRegex::new("|");
++   |                                                     ^^^
++   |
++   = help: the regex is unlikely to be useful as it is
++
++error: regex syntax error on position 0: unclosed group
++  --> $DIR/regex.rs:21:41
++   |
++LL |     let some_binary_regex = BRegex::new(OPENING_PAREN);
++   |                                         ^^^^^^^^^^^^^
++
++error: regex syntax error on position 0: unclosed group
++  --> $DIR/regex.rs:22:56
++   |
++LL |     let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN);
++   |                                                        ^^^^^^^^^^^^^
++
++error: regex syntax error on position 0: unclosed group
++  --> $DIR/regex.rs:34:37
++   |
++LL |     let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]);
++   |                                     ^^^^^^^^^^^^^
++
++error: regex syntax error on position 0: unclosed group
++  --> $DIR/regex.rs:35:39
++   |
++LL |     let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]);
++   |                                       ^^^^^^^^^^^^^
++
++error: regex syntax error: unrecognized escape sequence
++  --> $DIR/regex.rs:37:45
++   |
++LL |     let raw_string_error = Regex::new(r"[...//...]");
++   |                                             ^^
++
++error: regex syntax error: unrecognized escape sequence
++  --> $DIR/regex.rs:38:46
++   |
++LL |     let raw_string_error = Regex::new(r#"[...//...]"#);
++   |                                              ^^
++
++error: trivial regex
++  --> $DIR/regex.rs:42:33
++   |
++LL |     let trivial_eq = Regex::new("^foobar$");
++   |                                 ^^^^^^^^^^
++   |
++   = help: consider using `==` on `str`s
++
++error: trivial regex
++  --> $DIR/regex.rs:44:48
++   |
++LL |     let trivial_eq_builder = RegexBuilder::new("^foobar$");
++   |                                                ^^^^^^^^^^
++   |
++   = help: consider using `==` on `str`s
++
++error: trivial regex
++  --> $DIR/regex.rs:46:42
++   |
++LL |     let trivial_starts_with = Regex::new("^foobar");
++   |                                          ^^^^^^^^^
++   |
++   = help: consider using `str::starts_with`
++
++error: trivial regex
++  --> $DIR/regex.rs:48:40
++   |
++LL |     let trivial_ends_with = Regex::new("foobar$");
++   |                                        ^^^^^^^^^
++   |
++   = help: consider using `str::ends_with`
++
++error: trivial regex
++  --> $DIR/regex.rs:50:39
++   |
++LL |     let trivial_contains = Regex::new("foobar");
++   |                                       ^^^^^^^^
++   |
++   = help: consider using `str::contains`
++
++error: trivial regex
++  --> $DIR/regex.rs:52:39
++   |
++LL |     let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
++   |                                       ^^^^^^^^^^^^^^^^
++   |
++   = help: consider using `str::contains`
++
++error: trivial regex
++  --> $DIR/regex.rs:54:40
++   |
++LL |     let trivial_backslash = Regex::new("a/.b");
++   |                                        ^^^^^^^
++   |
++   = help: consider using `str::contains`
++
++error: trivial regex
++  --> $DIR/regex.rs:57:36
++   |
++LL |     let trivial_empty = Regex::new("");
++   |                                    ^^
++   |
++   = help: the regex is unlikely to be useful as it is
++
++error: trivial regex
++  --> $DIR/regex.rs:59:36
++   |
++LL |     let trivial_empty = Regex::new("^");
++   |                                    ^^^
++   |
++   = help: the regex is unlikely to be useful as it is
++
++error: trivial regex
++  --> $DIR/regex.rs:61:36
++   |
++LL |     let trivial_empty = Regex::new("^$");
++   |                                    ^^^^
++   |
++   = help: consider using `str::is_empty`
++
++error: trivial regex
++  --> $DIR/regex.rs:63:44
++   |
++LL |     let binary_trivial_empty = BRegex::new("^$");
++   |                                            ^^^^
++   |
++   = help: consider using `str::is_empty`
++
++error: aborting due to 23 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..13fbb6e2a6eedb86cbb0c185e8ab307a81553853
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++//! Test for Clippy lint renames.
++// run-rustfix
++
++#![allow(dead_code)]
++// allow the new lint name here, to test if the new name works
++#![allow(clippy::module_name_repetitions)]
++#![allow(clippy::new_without_default)]
++#![allow(clippy::redundant_static_lifetimes)]
++// warn for the old lint name here, to test if the renaming worked
++#![warn(clippy::cognitive_complexity)]
++
++#[warn(clippy::module_name_repetitions)]
++fn main() {}
++
++#[warn(clippy::new_without_default)]
++struct Foo;
++
++#[warn(clippy::redundant_static_lifetimes)]
++fn foo() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cbd3b1e91666aa385bedc7c97daff6298d456343
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++//! Test for Clippy lint renames.
++// run-rustfix
++
++#![allow(dead_code)]
++// allow the new lint name here, to test if the new name works
++#![allow(clippy::module_name_repetitions)]
++#![allow(clippy::new_without_default)]
++#![allow(clippy::redundant_static_lifetimes)]
++// warn for the old lint name here, to test if the renaming worked
++#![warn(clippy::cyclomatic_complexity)]
++
++#[warn(clippy::stutter)]
++fn main() {}
++
++#[warn(clippy::new_without_default_derive)]
++struct Foo;
++
++#[warn(clippy::const_static_lifetime)]
++fn foo() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a9e803946041e9e8cd269e0fc147f16716c52263
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
++  --> $DIR/rename.rs:10:9
++   |
++LL | #![warn(clippy::cyclomatic_complexity)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
++   |
++   = note: `-D renamed-and-removed-lints` implied by `-D warnings`
++
++error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
++  --> $DIR/rename.rs:12:8
++   |
++LL | #[warn(clippy::stutter)]
++   |        ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
++
++error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
++  --> $DIR/rename.rs:15:8
++   |
++LL | #[warn(clippy::new_without_default_derive)]
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
++
++error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
++  --> $DIR/rename.rs:18:8
++   |
++LL | #[warn(clippy::const_static_lifetime)]
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
++
++error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
++  --> $DIR/rename.rs:10:9
++   |
++LL | #![warn(clippy::cyclomatic_complexity)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cb91b841d2cb2625de974adfd72f3049c2b4dd3f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++// run-rustfix
++
++#[clippy::cognitive_complexity = "1"]
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b3ce2758067cfa5a97e6516c142dbfe71cd9cb7a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++// run-rustfix
++
++#[clippy::cyclomatic_complexity = "1"]
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a399ff52fb8b607dc9500afeb05cf0c7dd1f8be7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++error: Usage of deprecated attribute
++  --> $DIR/renamed_builtin_attr.rs:3:11
++   |
++LL | #[clippy::cyclomatic_complexity = "1"]
++   |           ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `cognitive_complexity`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..346972b7bb4e0d95e1353040c58c4ed714e8e01b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++#![allow(deprecated, invalid_value)]
++#![warn(clippy::all)]
++
++use std::mem;
++
++fn might_panic<X>(x: X) -> X {
++    // in practice this would be a possibly-panicky operation
++    x
++}
++
++fn main() {
++    let mut v = vec![0i32; 4];
++    // the following is UB if `might_panic` panics
++    unsafe {
++        let taken_v = mem::replace(&mut v, mem::uninitialized());
++        let new_v = might_panic(taken_v);
++        std::mem::forget(mem::replace(&mut v, new_v));
++    }
++
++    unsafe {
++        let taken_v = mem::replace(&mut v, mem::zeroed());
++        let new_v = might_panic(taken_v);
++        std::mem::forget(mem::replace(&mut v, new_v));
++    }
++
++    // this is silly but OK, because usize is a primitive type
++    let mut u: usize = 42;
++    let uref = &mut u;
++    let taken_u = unsafe { mem::replace(uref, mem::zeroed()) };
++    *uref = taken_u + 1;
++
++    // this is still not OK, because uninit
++    let taken_u = unsafe { mem::replace(uref, mem::uninitialized()) };
++    *uref = taken_u + 1;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c1f55d7601e5ccce9592e5653330435d7f114832
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++error: replacing with `mem::uninitialized()`
++  --> $DIR/repl_uninit.rs:15:23
++   |
++LL |         let taken_v = mem::replace(&mut v, mem::uninitialized());
++   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::mem-replace-with-uninit` implied by `-D warnings`
++   = help: consider using the `take_mut` crate instead
++
++error: replacing with `mem::zeroed()`
++  --> $DIR/repl_uninit.rs:21:23
++   |
++LL |         let taken_v = mem::replace(&mut v, mem::zeroed());
++   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using a default value or the `take_mut` crate instead
++
++error: replacing with `mem::uninitialized()`
++  --> $DIR/repl_uninit.rs:33:28
++   |
++LL |     let taken_u = unsafe { mem::replace(uref, mem::uninitialized()) };
++   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using the `take_mut` crate instead
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..38fc9969804fa593e46df30c22ed61ca3072dce6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++#![warn(clippy::rest_pat_in_fully_bound_structs)]
++
++struct A {
++    a: i32,
++    b: i64,
++    c: &'static str,
++}
++
++macro_rules! foo {
++    ($param:expr) => {
++        match $param {
++            A { a: 0, b: 0, c: "", .. } => {},
++            _ => {},
++        }
++    };
++}
++
++fn main() {
++    let a_struct = A { a: 5, b: 42, c: "A" };
++
++    match a_struct {
++        A { a: 5, b: 42, c: "", .. } => {}, // Lint
++        A { a: 0, b: 0, c: "", .. } => {},  // Lint
++        _ => {},
++    }
++
++    match a_struct {
++        A { a: 5, b: 42, .. } => {},
++        A { a: 0, b: 0, c: "", .. } => {}, // Lint
++        _ => {},
++    }
++
++    // No lint
++    match a_struct {
++        A { a: 5, .. } => {},
++        A { a: 0, b: 0, .. } => {},
++        _ => {},
++    }
++
++    // No lint
++    foo!(a_struct);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..57ebd47f8c7ac4d0808d485d38886d41cc1df316
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++error: unnecessary use of `..` pattern in struct binding. All fields were already bound
++  --> $DIR/rest_pat_in_fully_bound_structs.rs:22:9
++   |
++LL |         A { a: 5, b: 42, c: "", .. } => {}, // Lint
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::rest-pat-in-fully-bound-structs` implied by `-D warnings`
++   = help: consider removing `..` from this binding
++
++error: unnecessary use of `..` pattern in struct binding. All fields were already bound
++  --> $DIR/rest_pat_in_fully_bound_structs.rs:23:9
++   |
++LL |         A { a: 0, b: 0, c: "", .. } => {},  // Lint
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider removing `..` from this binding
++
++error: unnecessary use of `..` pattern in struct binding. All fields were already bound
++  --> $DIR/rest_pat_in_fully_bound_structs.rs:29:9
++   |
++LL |         A { a: 0, b: 0, c: "", .. } => {}, // Lint
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider removing `..` from this binding
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..331531b5165f61dce075c4d800d9d806a7dba3e3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++// run-rustfix
++
++#![warn(clippy::result_map_or_into_option)]
++
++fn main() {
++    let opt: Result<u32, &str> = Ok(1);
++    let _ = opt.ok();
++
++    let rewrap = |s: u32| -> Option<u32> { Some(s) };
++
++    // A non-Some `f` arg should not emit the lint
++    let opt: Result<u32, &str> = Ok(1);
++    let _ = opt.map_or(None, rewrap);
++
++    // A non-Some `f` closure where the argument is not used as the
++    // return should not emit the lint
++    let opt: Result<u32, &str> = Ok(1);
++    opt.map_or(None, |_x| Some(1));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3058480e2ad3d935c114a144ea504e495d033f7f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++// run-rustfix
++
++#![warn(clippy::result_map_or_into_option)]
++
++fn main() {
++    let opt: Result<u32, &str> = Ok(1);
++    let _ = opt.map_or(None, Some);
++
++    let rewrap = |s: u32| -> Option<u32> { Some(s) };
++
++    // A non-Some `f` arg should not emit the lint
++    let opt: Result<u32, &str> = Ok(1);
++    let _ = opt.map_or(None, rewrap);
++
++    // A non-Some `f` closure where the argument is not used as the
++    // return should not emit the lint
++    let opt: Result<u32, &str> = Ok(1);
++    opt.map_or(None, |_x| Some(1));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..febf32147d132d3e163e13080e025ccd31d30263
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling `ok()` instead
++  --> $DIR/result_map_or_into_option.rs:7:13
++   |
++LL |     let _ = opt.map_or(None, Some);
++   |             ^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok` instead: `opt.ok()`
++   |
++   = note: `-D clippy::result-map-or-into-option` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1d0a3ecd0ff8df69cee72d525dd6e60646d910b3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,80 @@@
++// run-rustfix
++
++#![warn(clippy::result_map_unit_fn)]
++#![allow(unused)]
++
++fn do_nothing<T>(_: T) {}
++
++fn diverge<T>(_: T) -> ! {
++    panic!()
++}
++
++fn plus_one(value: usize) -> usize {
++    value + 1
++}
++
++struct HasResult {
++    field: Result<usize, usize>,
++}
++
++impl HasResult {
++    fn do_result_nothing(self: &Self, value: usize) {}
++
++    fn do_result_plus_one(self: &Self, value: usize) -> usize {
++        value + 1
++    }
++}
++
++#[rustfmt::skip]
++fn result_map_unit_fn() {
++    let x = HasResult { field: Ok(10) };
++
++    x.field.map(plus_one);
++    let _: Result<(), usize> = x.field.map(do_nothing);
++
++    if let Ok(x_field) = x.field { do_nothing(x_field) }
++
++    if let Ok(x_field) = x.field { do_nothing(x_field) }
++
++    if let Ok(x_field) = x.field { diverge(x_field) }
++
++    let captured = 10;
++    if let Ok(value) = x.field { do_nothing(value + captured) };
++    let _: Result<(), usize> = x.field.map(|value| do_nothing(value + captured));
++
++    if let Ok(value) = x.field { x.do_result_nothing(value + captured) }
++
++    if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }
++
++
++    if let Ok(value) = x.field { do_nothing(value + captured) }
++
++    if let Ok(value) = x.field { do_nothing(value + captured) }
++
++    if let Ok(value) = x.field { do_nothing(value + captured); }
++
++    if let Ok(value) = x.field { do_nothing(value + captured); }
++
++
++    if let Ok(value) = x.field { diverge(value + captured) }
++
++    if let Ok(value) = x.field { diverge(value + captured) }
++
++    if let Ok(value) = x.field { diverge(value + captured); }
++
++    if let Ok(value) = x.field { diverge(value + captured); }
++
++
++    x.field.map(|value| plus_one(value + captured));
++    x.field.map(|value| { plus_one(value + captured) });
++    if let Ok(value) = x.field { let y = plus_one(value + captured); }
++
++    if let Ok(value) = x.field { plus_one(value + captured); }
++
++    if let Ok(value) = x.field { plus_one(value + captured); }
++
++
++    if let Ok(ref value) = x.field { do_nothing(value + captured) }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2fe18f923f08fa1143195d968f01d8cc2bf5590d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,80 @@@
++// run-rustfix
++
++#![warn(clippy::result_map_unit_fn)]
++#![allow(unused)]
++
++fn do_nothing<T>(_: T) {}
++
++fn diverge<T>(_: T) -> ! {
++    panic!()
++}
++
++fn plus_one(value: usize) -> usize {
++    value + 1
++}
++
++struct HasResult {
++    field: Result<usize, usize>,
++}
++
++impl HasResult {
++    fn do_result_nothing(self: &Self, value: usize) {}
++
++    fn do_result_plus_one(self: &Self, value: usize) -> usize {
++        value + 1
++    }
++}
++
++#[rustfmt::skip]
++fn result_map_unit_fn() {
++    let x = HasResult { field: Ok(10) };
++
++    x.field.map(plus_one);
++    let _: Result<(), usize> = x.field.map(do_nothing);
++
++    x.field.map(do_nothing);
++
++    x.field.map(do_nothing);
++
++    x.field.map(diverge);
++
++    let captured = 10;
++    if let Ok(value) = x.field { do_nothing(value + captured) };
++    let _: Result<(), usize> = x.field.map(|value| do_nothing(value + captured));
++
++    x.field.map(|value| x.do_result_nothing(value + captured));
++
++    x.field.map(|value| { x.do_result_plus_one(value + captured); });
++
++
++    x.field.map(|value| do_nothing(value + captured));
++
++    x.field.map(|value| { do_nothing(value + captured) });
++
++    x.field.map(|value| { do_nothing(value + captured); });
++
++    x.field.map(|value| { { do_nothing(value + captured); } });
++
++
++    x.field.map(|value| diverge(value + captured));
++
++    x.field.map(|value| { diverge(value + captured) });
++
++    x.field.map(|value| { diverge(value + captured); });
++
++    x.field.map(|value| { { diverge(value + captured); } });
++
++
++    x.field.map(|value| plus_one(value + captured));
++    x.field.map(|value| { plus_one(value + captured) });
++    x.field.map(|value| { let y = plus_one(value + captured); });
++
++    x.field.map(|value| { plus_one(value + captured); });
++
++    x.field.map(|value| { { plus_one(value + captured); } });
++
++
++    x.field.map(|ref value| { do_nothing(value + captured) });
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..467e00263cd3ae58ae9eeed948825e6f8c054da0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,140 @@@
++error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type
++  --> $DIR/result_map_unit_fn_fixable.rs:35:5
++   |
++LL |     x.field.map(do_nothing);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }`
++   |
++   = note: `-D clippy::result-map-unit-fn` implied by `-D warnings`
++
++error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type
++  --> $DIR/result_map_unit_fn_fixable.rs:37:5
++   |
++LL |     x.field.map(do_nothing);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }`
++
++error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type
++  --> $DIR/result_map_unit_fn_fixable.rs:39:5
++   |
++LL |     x.field.map(diverge);
++   |     ^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(x_field) = x.field { diverge(x_field) }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_fixable.rs:45:5
++   |
++LL |     x.field.map(|value| x.do_result_nothing(value + captured));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(value) = x.field { x.do_result_nothing(value + captured) }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_fixable.rs:47:5
++   |
++LL |     x.field.map(|value| { x.do_result_plus_one(value + captured); });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_fixable.rs:50:5
++   |
++LL |     x.field.map(|value| do_nothing(value + captured));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_fixable.rs:52:5
++   |
++LL |     x.field.map(|value| { do_nothing(value + captured) });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_fixable.rs:54:5
++   |
++LL |     x.field.map(|value| { do_nothing(value + captured); });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_fixable.rs:56:5
++   |
++LL |     x.field.map(|value| { { do_nothing(value + captured); } });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_fixable.rs:59:5
++   |
++LL |     x.field.map(|value| diverge(value + captured));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(value) = x.field { diverge(value + captured) }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_fixable.rs:61:5
++   |
++LL |     x.field.map(|value| { diverge(value + captured) });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(value) = x.field { diverge(value + captured) }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_fixable.rs:63:5
++   |
++LL |     x.field.map(|value| { diverge(value + captured); });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(value) = x.field { diverge(value + captured); }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_fixable.rs:65:5
++   |
++LL |     x.field.map(|value| { { diverge(value + captured); } });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(value) = x.field { diverge(value + captured); }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_fixable.rs:70:5
++   |
++LL |     x.field.map(|value| { let y = plus_one(value + captured); });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(value) = x.field { let y = plus_one(value + captured); }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_fixable.rs:72:5
++   |
++LL |     x.field.map(|value| { plus_one(value + captured); });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_fixable.rs:74:5
++   |
++LL |     x.field.map(|value| { { plus_one(value + captured); } });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_fixable.rs:77:5
++   |
++LL |     x.field.map(|ref value| { do_nothing(value + captured) });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(ref value) = x.field { do_nothing(value + captured) }`
++
++error: aborting due to 17 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b197c609d7bfcc7b347c8c8213a556a7365d8e09
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++#![warn(clippy::result_map_unit_fn)]
++#![feature(never_type)]
++#![allow(unused)]
++
++struct HasResult {
++    field: Result<usize, usize>,
++}
++
++fn do_nothing<T>(_: T) {}
++
++fn diverge<T>(_: T) -> ! {
++    panic!()
++}
++
++fn plus_one(value: usize) -> usize {
++    value + 1
++}
++
++#[rustfmt::skip]
++fn result_map_unit_fn() {
++    let x = HasResult { field: Ok(10) };
++
++    x.field.map(|value| { do_nothing(value); do_nothing(value) });
++
++    x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) });
++
++    // Suggestion for the let block should be `{ ... }` as it's too difficult to build a
++    // proper suggestion for these cases
++    x.field.map(|value| {
++        do_nothing(value);
++        do_nothing(value)
++    });
++    x.field.map(|value| { do_nothing(value); do_nothing(value); });
++
++    // The following should suggest `if let Ok(_X) ...` as it's difficult to generate a proper let variable name for them
++    let res: Result<!, usize> = Ok(42).map(diverge);
++    "12".parse::<i32>().map(diverge);
++
++    let res: Result<(), usize> = Ok(plus_one(1)).map(do_nothing);
++
++    // Should suggest `if let Ok(_y) ...` to not override the existing foo variable
++    let y: Result<usize, usize> = Ok(42);
++    y.map(do_nothing);
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b23cc608621d0fa3880ac27766d86d6349c5a76d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_unfixable.rs:23:5
++   |
++LL |     x.field.map(|value| { do_nothing(value); do_nothing(value) });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(value) = x.field { ... }`
++   |
++   = note: `-D clippy::result-map-unit-fn` implied by `-D warnings`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_unfixable.rs:25:5
++   |
++LL |     x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(value) = x.field { ... }`
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_unfixable.rs:29:5
++   |
++LL |        x.field.map(|value| {
++   |   _____^
++   |  |_____|
++   | ||
++LL | ||         do_nothing(value);
++LL | ||         do_nothing(value)
++LL | ||     });
++   | ||______^- help: try this: `if let Ok(value) = x.field { ... }`
++   | |_______|
++   | 
++
++error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type
++  --> $DIR/result_map_unit_fn_unfixable.rs:33:5
++   |
++LL |     x.field.map(|value| { do_nothing(value); do_nothing(value); });
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(value) = x.field { ... }`
++
++error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type
++  --> $DIR/result_map_unit_fn_unfixable.rs:37:5
++   |
++LL |     "12".parse::<i32>().map(diverge);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(a) = "12".parse::<i32>() { diverge(a) }`
++
++error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type
++  --> $DIR/result_map_unit_fn_unfixable.rs:43:5
++   |
++LL |     y.map(do_nothing);
++   |     ^^^^^^^^^^^^^^^^^-
++   |     |
++   |     help: try this: `if let Ok(_y) = y { do_nothing(_y) }`
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..40751bfebe6c926e602eafbae9e9662aefdf32d2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++// aux-build:option_helpers.rs
++
++//! Checks implementation of `RESULT_MAP_UNWRAP_OR_ELSE`
++
++#![warn(clippy::result_map_unwrap_or_else)]
++
++#[macro_use]
++extern crate option_helpers;
++
++fn result_methods() {
++    let res: Result<i32, ()> = Ok(1);
++
++    // Check RESULT_MAP_UNWRAP_OR_ELSE
++    // single line case
++    let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line
++                                                      // multi line cases
++    let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0);
++    let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0);
++    // macro case
++    let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|e| 0); // should not lint
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ec7bc8f12414f05258c2e0feb2336cd289c3e5f6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead
++  --> $DIR/result_map_unwrap_or_else.rs:15:13
++   |
++LL |     let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::result-map-unwrap-or-else` implied by `-D warnings`
++   = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)`
++
++error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead
++  --> $DIR/result_map_unwrap_or_else.rs:17:13
++   |
++LL |     let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)`
++
++error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead
++  --> $DIR/result_map_unwrap_or_else.rs:18:13
++   |
++LL |     let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..686867cf5c6f661df745266fea2860e3ccd10b7d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,80 @@@
++#![warn(clippy::same_functions_in_if_condition)]
++#![allow(clippy::ifs_same_cond)] // This warning is different from `ifs_same_cond`.
++#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks
++
++fn function() -> bool {
++    true
++}
++
++fn fn_arg(_arg: u8) -> bool {
++    true
++}
++
++struct Struct;
++
++impl Struct {
++    fn method(&self) -> bool {
++        true
++    }
++    fn method_arg(&self, _arg: u8) -> bool {
++        true
++    }
++}
++
++fn ifs_same_cond_fn() {
++    let a = 0;
++    let obj = Struct;
++
++    if function() {
++    } else if function() {
++        //~ ERROR ifs same condition
++    }
++
++    if fn_arg(a) {
++    } else if fn_arg(a) {
++        //~ ERROR ifs same condition
++    }
++
++    if obj.method() {
++    } else if obj.method() {
++        //~ ERROR ifs same condition
++    }
++
++    if obj.method_arg(a) {
++    } else if obj.method_arg(a) {
++        //~ ERROR ifs same condition
++    }
++
++    let mut v = vec![1];
++    if v.pop() == None {
++        //~ ERROR ifs same condition
++    } else if v.pop() == None {
++    }
++
++    if v.len() == 42 {
++        //~ ERROR ifs same condition
++    } else if v.len() == 42 {
++    }
++
++    if v.len() == 1 {
++        // ok, different conditions
++    } else if v.len() == 2 {
++    }
++
++    if fn_arg(0) {
++        // ok, different arguments.
++    } else if fn_arg(1) {
++    }
++
++    if obj.method_arg(0) {
++        // ok, different arguments.
++    } else if obj.method_arg(1) {
++    }
++
++    if a == 1 {
++        // ok, warning is on `ifs_same_cond` behalf.
++    } else if a == 1 {
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..363a03846d236e72ce5b2cd3296e16bb4d7aaf02
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,75 @@@
++error: this `if` has the same function call as a previous `if`
++  --> $DIR/same_functions_in_if_condition.rs:29:15
++   |
++LL |     } else if function() {
++   |               ^^^^^^^^^^
++   |
++   = note: `-D clippy::same-functions-in-if-condition` implied by `-D warnings`
++note: same as this
++  --> $DIR/same_functions_in_if_condition.rs:28:8
++   |
++LL |     if function() {
++   |        ^^^^^^^^^^
++
++error: this `if` has the same function call as a previous `if`
++  --> $DIR/same_functions_in_if_condition.rs:34:15
++   |
++LL |     } else if fn_arg(a) {
++   |               ^^^^^^^^^
++   |
++note: same as this
++  --> $DIR/same_functions_in_if_condition.rs:33:8
++   |
++LL |     if fn_arg(a) {
++   |        ^^^^^^^^^
++
++error: this `if` has the same function call as a previous `if`
++  --> $DIR/same_functions_in_if_condition.rs:39:15
++   |
++LL |     } else if obj.method() {
++   |               ^^^^^^^^^^^^
++   |
++note: same as this
++  --> $DIR/same_functions_in_if_condition.rs:38:8
++   |
++LL |     if obj.method() {
++   |        ^^^^^^^^^^^^
++
++error: this `if` has the same function call as a previous `if`
++  --> $DIR/same_functions_in_if_condition.rs:44:15
++   |
++LL |     } else if obj.method_arg(a) {
++   |               ^^^^^^^^^^^^^^^^^
++   |
++note: same as this
++  --> $DIR/same_functions_in_if_condition.rs:43:8
++   |
++LL |     if obj.method_arg(a) {
++   |        ^^^^^^^^^^^^^^^^^
++
++error: this `if` has the same function call as a previous `if`
++  --> $DIR/same_functions_in_if_condition.rs:51:15
++   |
++LL |     } else if v.pop() == None {
++   |               ^^^^^^^^^^^^^^^
++   |
++note: same as this
++  --> $DIR/same_functions_in_if_condition.rs:49:8
++   |
++LL |     if v.pop() == None {
++   |        ^^^^^^^^^^^^^^^
++
++error: this `if` has the same function call as a previous `if`
++  --> $DIR/same_functions_in_if_condition.rs:56:15
++   |
++LL |     } else if v.len() == 42 {
++   |               ^^^^^^^^^^^^^
++   |
++note: same as this
++  --> $DIR/same_functions_in_if_condition.rs:54:8
++   |
++LL |     if v.len() == 42 {
++   |        ^^^^^^^^^^^^^
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5843344eba89a32f83bce805f88d99e4027653d5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++#![warn(clippy::serde_api_misuse)]
++#![allow(dead_code)]
++
++extern crate serde;
++
++struct A;
++
++impl<'de> serde::de::Visitor<'de> for A {
++    type Value = ();
++
++    fn expecting(&self, _: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
++        unimplemented!()
++    }
++
++    fn visit_str<E>(self, _v: &str) -> Result<Self::Value, E>
++    where
++        E: serde::de::Error,
++    {
++        unimplemented!()
++    }
++
++    fn visit_string<E>(self, _v: String) -> Result<Self::Value, E>
++    where
++        E: serde::de::Error,
++    {
++        unimplemented!()
++    }
++}
++
++struct B;
++
++impl<'de> serde::de::Visitor<'de> for B {
++    type Value = ();
++
++    fn expecting(&self, _: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
++        unimplemented!()
++    }
++
++    fn visit_string<E>(self, _v: String) -> Result<Self::Value, E>
++    where
++        E: serde::de::Error,
++    {
++        unimplemented!()
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..760c9c9908a6f06733c6e0db7a3421b94462cfcb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++error: you should not implement `visit_string` without also implementing `visit_str`
++  --> $DIR/serde.rs:39:5
++   |
++LL | /     fn visit_string<E>(self, _v: String) -> Result<Self::Value, E>
++LL | |     where
++LL | |         E: serde::de::Error,
++LL | |     {
++LL | |         unimplemented!()
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::serde-api-misuse` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bd91ae4e9340cf9a15d67f4c57d075fa933807e1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++#![warn(
++    clippy::all,
++    clippy::pedantic,
++    clippy::shadow_same,
++    clippy::shadow_reuse,
++    clippy::shadow_unrelated
++)]
++#![allow(
++    unused_parens,
++    unused_variables,
++    clippy::missing_docs_in_private_items,
++    clippy::single_match
++)]
++
++fn id<T>(x: T) -> T {
++    x
++}
++
++#[must_use]
++fn first(x: (isize, isize)) -> isize {
++    x.0
++}
++
++fn main() {
++    let mut x = 1;
++    let x = &mut x;
++    let x = { x };
++    let x = (&*x);
++    let x = { *x + 1 };
++    let x = id(x);
++    let x = (1, x);
++    let x = first(x);
++    let y = 1;
++    let x = y;
++
++    let x;
++    x = 42;
++
++    let o = Some(1_u8);
++
++    if let Some(p) = o {
++        assert_eq!(1, p);
++    }
++    match o {
++        Some(p) => p, // no error, because the p above is in its own scope
++        None => 0,
++    };
++
++    match (x, o) {
++        (1, Some(a)) | (a, Some(1)) => (), // no error though `a` appears twice
++        _ => (),
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7fa58cf76499b8016b9c062a647ef041cc5d2444
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,138 @@@
++error: `x` is shadowed by itself in `&mut x`
++  --> $DIR/shadow.rs:26:5
++   |
++LL |     let x = &mut x;
++   |     ^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::shadow-same` implied by `-D warnings`
++note: previous binding is here
++  --> $DIR/shadow.rs:25:13
++   |
++LL |     let mut x = 1;
++   |             ^
++
++error: `x` is shadowed by itself in `{ x }`
++  --> $DIR/shadow.rs:27:5
++   |
++LL |     let x = { x };
++   |     ^^^^^^^^^^^^^^
++   |
++note: previous binding is here
++  --> $DIR/shadow.rs:26:9
++   |
++LL |     let x = &mut x;
++   |         ^
++
++error: `x` is shadowed by itself in `(&*x)`
++  --> $DIR/shadow.rs:28:5
++   |
++LL |     let x = (&*x);
++   |     ^^^^^^^^^^^^^^
++   |
++note: previous binding is here
++  --> $DIR/shadow.rs:27:9
++   |
++LL |     let x = { x };
++   |         ^
++
++error: `x` is shadowed by `{ *x + 1 }` which reuses the original value
++  --> $DIR/shadow.rs:29:9
++   |
++LL |     let x = { *x + 1 };
++   |         ^
++   |
++   = note: `-D clippy::shadow-reuse` implied by `-D warnings`
++note: initialization happens here
++  --> $DIR/shadow.rs:29:13
++   |
++LL |     let x = { *x + 1 };
++   |             ^^^^^^^^^^
++note: previous binding is here
++  --> $DIR/shadow.rs:28:9
++   |
++LL |     let x = (&*x);
++   |         ^
++
++error: `x` is shadowed by `id(x)` which reuses the original value
++  --> $DIR/shadow.rs:30:9
++   |
++LL |     let x = id(x);
++   |         ^
++   |
++note: initialization happens here
++  --> $DIR/shadow.rs:30:13
++   |
++LL |     let x = id(x);
++   |             ^^^^^
++note: previous binding is here
++  --> $DIR/shadow.rs:29:9
++   |
++LL |     let x = { *x + 1 };
++   |         ^
++
++error: `x` is shadowed by `(1, x)` which reuses the original value
++  --> $DIR/shadow.rs:31:9
++   |
++LL |     let x = (1, x);
++   |         ^
++   |
++note: initialization happens here
++  --> $DIR/shadow.rs:31:13
++   |
++LL |     let x = (1, x);
++   |             ^^^^^^
++note: previous binding is here
++  --> $DIR/shadow.rs:30:9
++   |
++LL |     let x = id(x);
++   |         ^
++
++error: `x` is shadowed by `first(x)` which reuses the original value
++  --> $DIR/shadow.rs:32:9
++   |
++LL |     let x = first(x);
++   |         ^
++   |
++note: initialization happens here
++  --> $DIR/shadow.rs:32:13
++   |
++LL |     let x = first(x);
++   |             ^^^^^^^^
++note: previous binding is here
++  --> $DIR/shadow.rs:31:9
++   |
++LL |     let x = (1, x);
++   |         ^
++
++error: `x` is shadowed by `y`
++  --> $DIR/shadow.rs:34:9
++   |
++LL |     let x = y;
++   |         ^
++   |
++   = note: `-D clippy::shadow-unrelated` implied by `-D warnings`
++note: initialization happens here
++  --> $DIR/shadow.rs:34:13
++   |
++LL |     let x = y;
++   |             ^
++note: previous binding is here
++  --> $DIR/shadow.rs:32:9
++   |
++LL |     let x = first(x);
++   |         ^
++
++error: `x` shadows a previous declaration
++  --> $DIR/shadow.rs:36:5
++   |
++LL |     let x;
++   |     ^^^^^^
++   |
++note: previous binding is here
++  --> $DIR/shadow.rs:34:9
++   |
++LL |     let x = y;
++   |         ^
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..af0a397bd1aff0339695868b93d5d9aa38dd01b8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++// run-rustfix
++
++#![warn(clippy::short_circuit_statement)]
++#![allow(clippy::nonminimal_bool)]
++
++fn main() {
++    if f() { g(); }
++    if !f() { g(); }
++    if !(1 == 2) { g(); }
++}
++
++fn f() -> bool {
++    true
++}
++
++fn g() -> bool {
++    false
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..73a55bf1f5e27c077c081206069879a47d0cf4b9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++// run-rustfix
++
++#![warn(clippy::short_circuit_statement)]
++#![allow(clippy::nonminimal_bool)]
++
++fn main() {
++    f() && g();
++    f() || g();
++    1 == 2 || g();
++}
++
++fn f() -> bool {
++    true
++}
++
++fn g() -> bool {
++    false
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0a3f60c3d132db57852c9cc2190db75f2eb357de
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: boolean short circuit operator in statement may be clearer using an explicit test
++  --> $DIR/short_circuit_statement.rs:7:5
++   |
++LL |     f() && g();
++   |     ^^^^^^^^^^^ help: replace it with: `if f() { g(); }`
++   |
++   = note: `-D clippy::short-circuit-statement` implied by `-D warnings`
++
++error: boolean short circuit operator in statement may be clearer using an explicit test
++  --> $DIR/short_circuit_statement.rs:8:5
++   |
++LL |     f() || g();
++   |     ^^^^^^^^^^^ help: replace it with: `if !f() { g(); }`
++
++error: boolean short circuit operator in statement may be clearer using an explicit test
++  --> $DIR/short_circuit_statement.rs:9:5
++   |
++LL |     1 == 2 || g();
++   |     ^^^^^^^^^^^^^^ help: replace it with: `if !(1 == 2) { g(); }`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6796b15289ecd08909049f5677c4712c2a387a8b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,103 @@@
++#![warn(clippy::similar_names)]
++#![allow(unused, clippy::println_empty_string)]
++
++struct Foo {
++    apple: i32,
++    bpple: i32,
++}
++
++fn main() {
++    let specter: i32;
++    let spectre: i32;
++
++    let apple: i32;
++
++    let bpple: i32;
++
++    let cpple: i32;
++
++    let a_bar: i32;
++    let b_bar: i32;
++    let c_bar: i32;
++
++    let items = [5];
++    for item in &items {
++        loop {}
++    }
++
++    let foo_x: i32;
++    let foo_y: i32;
++
++    let rhs: i32;
++    let lhs: i32;
++
++    let bla_rhs: i32;
++    let bla_lhs: i32;
++
++    let blubrhs: i32;
++    let blublhs: i32;
++
++    let blubx: i32;
++    let bluby: i32;
++
++    let cake: i32;
++    let cakes: i32;
++    let coke: i32;
++
++    match 5 {
++        cheese @ 1 => {},
++        rabbit => panic!(),
++    }
++    let cheese: i32;
++    match (42, 43) {
++        (cheese1, 1) => {},
++        (cheese2, 2) => panic!(),
++        _ => println!(""),
++    }
++    let ipv4: i32;
++    let ipv6: i32;
++    let abcd1: i32;
++    let abdc2: i32;
++    let xyz1abc: i32;
++    let xyz2abc: i32;
++    let xyzeabc: i32;
++
++    let parser: i32;
++    let parsed: i32;
++    let parsee: i32;
++
++    let setter: i32;
++    let getter: i32;
++    let tx1: i32;
++    let rx1: i32;
++    let tx_cake: i32;
++    let rx_cake: i32;
++}
++
++fn foo() {
++    let Foo { apple, bpple } = unimplemented!();
++    let Foo {
++        apple: spring,
++        bpple: sprang,
++    } = unimplemented!();
++}
++
++// false positive similar_names (#3057, #2651)
++// clippy claimed total_reg_src_size and total_size and
++// numb_reg_src_checkouts and total_bin_size were similar
++#[derive(Debug, Clone)]
++pub(crate) struct DirSizes {
++    pub(crate) total_size: u64,
++    pub(crate) numb_bins: u64,
++    pub(crate) total_bin_size: u64,
++    pub(crate) total_reg_size: u64,
++    pub(crate) total_git_db_size: u64,
++    pub(crate) total_git_repos_bare_size: u64,
++    pub(crate) numb_git_repos_bare_repos: u64,
++    pub(crate) numb_git_checkouts: u64,
++    pub(crate) total_git_chk_size: u64,
++    pub(crate) total_reg_cache_size: u64,
++    pub(crate) total_reg_src_size: u64,
++    pub(crate) numb_reg_cache_entries: u64,
++    pub(crate) numb_reg_src_checkouts: u64,
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0256f126a94fc5d4a0e84ea06aa9ebb60d695e1d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,107 @@@
++error: binding's name is too similar to existing binding
++  --> $DIR/similar_names.rs:15:9
++   |
++LL |     let bpple: i32;
++   |         ^^^^^
++   |
++   = note: `-D clippy::similar-names` implied by `-D warnings`
++note: existing binding defined here
++  --> $DIR/similar_names.rs:13:9
++   |
++LL |     let apple: i32;
++   |         ^^^^^
++help: separate the discriminating character by an underscore like: `b_pple`
++  --> $DIR/similar_names.rs:15:9
++   |
++LL |     let bpple: i32;
++   |         ^^^^^
++
++error: binding's name is too similar to existing binding
++  --> $DIR/similar_names.rs:17:9
++   |
++LL |     let cpple: i32;
++   |         ^^^^^
++   |
++note: existing binding defined here
++  --> $DIR/similar_names.rs:13:9
++   |
++LL |     let apple: i32;
++   |         ^^^^^
++help: separate the discriminating character by an underscore like: `c_pple`
++  --> $DIR/similar_names.rs:17:9
++   |
++LL |     let cpple: i32;
++   |         ^^^^^
++
++error: binding's name is too similar to existing binding
++  --> $DIR/similar_names.rs:41:9
++   |
++LL |     let bluby: i32;
++   |         ^^^^^
++   |
++note: existing binding defined here
++  --> $DIR/similar_names.rs:40:9
++   |
++LL |     let blubx: i32;
++   |         ^^^^^
++help: separate the discriminating character by an underscore like: `blub_y`
++  --> $DIR/similar_names.rs:41:9
++   |
++LL |     let bluby: i32;
++   |         ^^^^^
++
++error: binding's name is too similar to existing binding
++  --> $DIR/similar_names.rs:45:9
++   |
++LL |     let coke: i32;
++   |         ^^^^
++   |
++note: existing binding defined here
++  --> $DIR/similar_names.rs:43:9
++   |
++LL |     let cake: i32;
++   |         ^^^^
++
++error: binding's name is too similar to existing binding
++  --> $DIR/similar_names.rs:63:9
++   |
++LL |     let xyzeabc: i32;
++   |         ^^^^^^^
++   |
++note: existing binding defined here
++  --> $DIR/similar_names.rs:61:9
++   |
++LL |     let xyz1abc: i32;
++   |         ^^^^^^^
++
++error: binding's name is too similar to existing binding
++  --> $DIR/similar_names.rs:67:9
++   |
++LL |     let parsee: i32;
++   |         ^^^^^^
++   |
++note: existing binding defined here
++  --> $DIR/similar_names.rs:65:9
++   |
++LL |     let parser: i32;
++   |         ^^^^^^
++help: separate the discriminating character by an underscore like: `parse_e`
++  --> $DIR/similar_names.rs:67:9
++   |
++LL |     let parsee: i32;
++   |         ^^^^^^
++
++error: binding's name is too similar to existing binding
++  --> $DIR/similar_names.rs:81:16
++   |
++LL |         bpple: sprang,
++   |                ^^^^^^
++   |
++note: existing binding defined here
++  --> $DIR/similar_names.rs:80:16
++   |
++LL |         apple: spring,
++   |                ^^^^^^
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3871c4f2268cc36e02c1c73916b2cbcaff9a5ffb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++// run-rustfix
++
++#![allow(unused_must_use)]
++
++use std::collections::HashSet;
++
++fn main() {
++    let x = "foo";
++    x.split('x');
++    x.split("xx");
++    x.split('x');
++
++    let y = "x";
++    x.split(y);
++    // Not yet testing for multi-byte characters
++    // Changing `r.len() == 1` to `r.chars().count() == 1` in `lint_clippy::single_char_pattern`
++    // should have done this but produced an ICE
++    //
++    // We may not want to suggest changing these anyway
++    // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984
++    x.split("ß");
++    x.split("ℝ");
++    x.split("💣");
++    // Can't use this lint for unicode code points which don't fit in a char
++    x.split("❤️");
++    x.contains('x');
++    x.starts_with('x');
++    x.ends_with('x');
++    x.find('x');
++    x.rfind('x');
++    x.rsplit('x');
++    x.split_terminator('x');
++    x.rsplit_terminator('x');
++    x.splitn(0, 'x');
++    x.rsplitn(0, 'x');
++    x.matches('x');
++    x.rmatches('x');
++    x.match_indices('x');
++    x.rmatch_indices('x');
++    x.trim_start_matches('x');
++    x.trim_end_matches('x');
++    // Make sure we escape characters correctly.
++    x.split('\n');
++    x.split('\'');
++    x.split('\'');
++
++    let h = HashSet::<String>::new();
++    h.contains("X"); // should not warn
++
++    x.replace(";", ",").split(','); // issue #2978
++    x.starts_with('\x03'); // issue #2996
++
++    // Issue #3204
++    const S: &str = "#";
++    x.find(S);
++
++    // Raw string
++    x.split('a');
++    x.split('a');
++    x.split('a');
++    x.split('\'');
++    x.split('#');
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..32afe339cd81cc0cf07d87dc231f4368be748392
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++// run-rustfix
++
++#![allow(unused_must_use)]
++
++use std::collections::HashSet;
++
++fn main() {
++    let x = "foo";
++    x.split("x");
++    x.split("xx");
++    x.split('x');
++
++    let y = "x";
++    x.split(y);
++    // Not yet testing for multi-byte characters
++    // Changing `r.len() == 1` to `r.chars().count() == 1` in `lint_clippy::single_char_pattern`
++    // should have done this but produced an ICE
++    //
++    // We may not want to suggest changing these anyway
++    // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984
++    x.split("ß");
++    x.split("ℝ");
++    x.split("💣");
++    // Can't use this lint for unicode code points which don't fit in a char
++    x.split("❤️");
++    x.contains("x");
++    x.starts_with("x");
++    x.ends_with("x");
++    x.find("x");
++    x.rfind("x");
++    x.rsplit("x");
++    x.split_terminator("x");
++    x.rsplit_terminator("x");
++    x.splitn(0, "x");
++    x.rsplitn(0, "x");
++    x.matches("x");
++    x.rmatches("x");
++    x.match_indices("x");
++    x.rmatch_indices("x");
++    x.trim_start_matches("x");
++    x.trim_end_matches("x");
++    // Make sure we escape characters correctly.
++    x.split("\n");
++    x.split("'");
++    x.split("\'");
++
++    let h = HashSet::<String>::new();
++    h.contains("X"); // should not warn
++
++    x.replace(";", ",").split(","); // issue #2978
++    x.starts_with("\x03"); // issue #2996
++
++    // Issue #3204
++    const S: &str = "#";
++    x.find(S);
++
++    // Raw string
++    x.split(r"a");
++    x.split(r#"a"#);
++    x.split(r###"a"###);
++    x.split(r###"'"###);
++    x.split(r###"#"###);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fe7211c53f8521bb6f155121b14bc0c69eb341f3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,166 @@@
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:9:13
++   |
++LL |     x.split("x");
++   |             ^^^ help: try using a `char` instead: `'x'`
++   |
++   = note: `-D clippy::single-char-pattern` implied by `-D warnings`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:26:16
++   |
++LL |     x.contains("x");
++   |                ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:27:19
++   |
++LL |     x.starts_with("x");
++   |                   ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:28:17
++   |
++LL |     x.ends_with("x");
++   |                 ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:29:12
++   |
++LL |     x.find("x");
++   |            ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:30:13
++   |
++LL |     x.rfind("x");
++   |             ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:31:14
++   |
++LL |     x.rsplit("x");
++   |              ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:32:24
++   |
++LL |     x.split_terminator("x");
++   |                        ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:33:25
++   |
++LL |     x.rsplit_terminator("x");
++   |                         ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:34:17
++   |
++LL |     x.splitn(0, "x");
++   |                 ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:35:18
++   |
++LL |     x.rsplitn(0, "x");
++   |                  ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:36:15
++   |
++LL |     x.matches("x");
++   |               ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:37:16
++   |
++LL |     x.rmatches("x");
++   |                ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:38:21
++   |
++LL |     x.match_indices("x");
++   |                     ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:39:22
++   |
++LL |     x.rmatch_indices("x");
++   |                      ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:40:26
++   |
++LL |     x.trim_start_matches("x");
++   |                          ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:41:24
++   |
++LL |     x.trim_end_matches("x");
++   |                        ^^^ help: try using a `char` instead: `'x'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:43:13
++   |
++LL |     x.split("/n");
++   |             ^^^^ help: try using a `char` instead: `'/n'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:44:13
++   |
++LL |     x.split("'");
++   |             ^^^ help: try using a `char` instead: `'/''`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:45:13
++   |
++LL |     x.split("/'");
++   |             ^^^^ help: try using a `char` instead: `'/''`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:50:31
++   |
++LL |     x.replace(";", ",").split(","); // issue #2978
++   |                               ^^^ help: try using a `char` instead: `','`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:51:19
++   |
++LL |     x.starts_with("/x03"); // issue #2996
++   |                   ^^^^^^ help: try using a `char` instead: `'/x03'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:58:13
++   |
++LL |     x.split(r"a");
++   |             ^^^^ help: try using a `char` instead: `'a'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:59:13
++   |
++LL |     x.split(r#"a"#);
++   |             ^^^^^^ help: try using a `char` instead: `'a'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:60:13
++   |
++LL |     x.split(r###"a"###);
++   |             ^^^^^^^^^^ help: try using a `char` instead: `'a'`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:61:13
++   |
++LL |     x.split(r###"'"###);
++   |             ^^^^^^^^^^ help: try using a `char` instead: `'/''`
++
++error: single-character string constant used as pattern
++  --> $DIR/single_char_pattern.rs:62:13
++   |
++LL |     x.split(r###"#"###);
++   |             ^^^^^^^^^^ help: try using a `char` instead: `'#'`
++
++error: aborting due to 27 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a7a8499b58f000a58e163bed66534e50a2d38ea0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++// run-rustfix
++// edition:2018
++#![warn(clippy::single_component_path_imports)]
++#![allow(unused_imports)]
++
++
++use serde as edres;
++pub use serde;
++
++macro_rules! m {
++    () => {
++        use regex;
++    };
++}
++
++fn main() {
++    regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
++
++    // False positive #5154, shouldn't trigger lint.
++    m!();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9a427e90ad3df619a43491aaae91be5f83a418ba
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++// run-rustfix
++// edition:2018
++#![warn(clippy::single_component_path_imports)]
++#![allow(unused_imports)]
++
++use regex;
++use serde as edres;
++pub use serde;
++
++macro_rules! m {
++    () => {
++        use regex;
++    };
++}
++
++fn main() {
++    regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
++
++    // False positive #5154, shouldn't trigger lint.
++    m!();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..519ada0169a6d89ee87e2f37db80790d19512c30
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: this import is redundant
++  --> $DIR/single_component_path_imports.rs:6:1
++   |
++LL | use regex;
++   | ^^^^^^^^^^ help: remove it entirely
++   |
++   = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1c55af5dfb673536605d3443bc135b4d49209e65
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,95 @@@
++#![warn(clippy::single_match)]
++
++fn dummy() {}
++
++fn single_match() {
++    let x = Some(1u8);
++
++    match x {
++        Some(y) => {
++            println!("{:?}", y);
++        },
++        _ => (),
++    };
++
++    let x = Some(1u8);
++    match x {
++        // Note the missing block braces.
++        // We suggest `if let Some(y) = x { .. }` because the macro
++        // is expanded before we can do anything.
++        Some(y) => println!("{:?}", y),
++        _ => (),
++    }
++
++    let z = (1u8, 1u8);
++    match z {
++        (2..=3, 7..=9) => dummy(),
++        _ => {},
++    };
++
++    // Not linted (pattern guards used)
++    match x {
++        Some(y) if y == 0 => println!("{:?}", y),
++        _ => (),
++    }
++
++    // Not linted (no block with statements in the single arm)
++    match z {
++        (2..=3, 7..=9) => println!("{:?}", z),
++        _ => println!("nope"),
++    }
++}
++
++enum Foo {
++    Bar,
++    Baz(u8),
++}
++use std::borrow::Cow;
++use Foo::*;
++
++fn single_match_know_enum() {
++    let x = Some(1u8);
++    let y: Result<_, i8> = Ok(1i8);
++
++    match x {
++        Some(y) => dummy(),
++        None => (),
++    };
++
++    match y {
++        Ok(y) => dummy(),
++        Err(..) => (),
++    };
++
++    let c = Cow::Borrowed("");
++
++    match c {
++        Cow::Borrowed(..) => dummy(),
++        Cow::Owned(..) => (),
++    };
++
++    let z = Foo::Bar;
++    // no warning
++    match z {
++        Bar => println!("42"),
++        Baz(_) => (),
++    }
++
++    match z {
++        Baz(_) => println!("42"),
++        Bar => (),
++    }
++}
++
++macro_rules! single_match {
++    ($num:literal) => {
++        match $num {
++            15 => println!("15"),
++            _ => (),
++        }
++    };
++}
++
++fn main() {
++    single_match!(5);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f69554d75f9bf7aa1f08f4e0bfb3fc3d5d4bb89a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,69 @@@
++error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let`
++  --> $DIR/single_match.rs:8:5
++   |
++LL | /     match x {
++LL | |         Some(y) => {
++LL | |             println!("{:?}", y);
++LL | |         },
++LL | |         _ => (),
++LL | |     };
++   | |_____^
++   |
++   = note: `-D clippy::single-match` implied by `-D warnings`
++help: try this
++   |
++LL |     if let Some(y) = x {
++LL |         println!("{:?}", y);
++LL |     };
++   |
++
++error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let`
++  --> $DIR/single_match.rs:16:5
++   |
++LL | /     match x {
++LL | |         // Note the missing block braces.
++LL | |         // We suggest `if let Some(y) = x { .. }` because the macro
++LL | |         // is expanded before we can do anything.
++LL | |         Some(y) => println!("{:?}", y),
++LL | |         _ => (),
++LL | |     }
++   | |_____^ help: try this: `if let Some(y) = x { println!("{:?}", y) }`
++
++error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let`
++  --> $DIR/single_match.rs:25:5
++   |
++LL | /     match z {
++LL | |         (2..=3, 7..=9) => dummy(),
++LL | |         _ => {},
++LL | |     };
++   | |_____^ help: try this: `if let (2..=3, 7..=9) = z { dummy() }`
++
++error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let`
++  --> $DIR/single_match.rs:54:5
++   |
++LL | /     match x {
++LL | |         Some(y) => dummy(),
++LL | |         None => (),
++LL | |     };
++   | |_____^ help: try this: `if let Some(y) = x { dummy() }`
++
++error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let`
++  --> $DIR/single_match.rs:59:5
++   |
++LL | /     match y {
++LL | |         Ok(y) => dummy(),
++LL | |         Err(..) => (),
++LL | |     };
++   | |_____^ help: try this: `if let Ok(y) = y { dummy() }`
++
++error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let`
++  --> $DIR/single_match.rs:66:5
++   |
++LL | /     match c {
++LL | |         Cow::Borrowed(..) => dummy(),
++LL | |         Cow::Owned(..) => (),
++LL | |     };
++   | |_____^ help: try this: `if let Cow::Borrowed(..) = c { dummy() }`
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..34193be0b75e40b978ae7661c3ab944fe1fd5b90
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++#![warn(clippy::single_match_else)]
++
++enum ExprNode {
++    ExprAddrOf,
++    Butterflies,
++    Unicorns,
++}
++
++static NODE: ExprNode = ExprNode::Unicorns;
++
++fn unwrap_addr() -> Option<&'static ExprNode> {
++    match ExprNode::Butterflies {
++        ExprNode::ExprAddrOf => Some(&NODE),
++        _ => {
++            let x = 5;
++            None
++        },
++    }
++}
++
++macro_rules! unwrap_addr {
++    ($expression:expr) => {
++        match $expression {
++            ExprNode::ExprAddrOf => Some(&NODE),
++            _ => {
++                let x = 5;
++                None
++            },
++        }
++    };
++}
++
++fn main() {
++    unwrap_addr!(ExprNode::Unicorns);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..59861d46eb34cf5383bec85151672e023308e719
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let`
++  --> $DIR/single_match_else.rs:12:5
++   |
++LL | /     match ExprNode::Butterflies {
++LL | |         ExprNode::ExprAddrOf => Some(&NODE),
++LL | |         _ => {
++LL | |             let x = 5;
++LL | |             None
++LL | |         },
++LL | |     }
++   | |_____^
++   |
++   = note: `-D clippy::single-match-else` implied by `-D warnings`
++help: try this
++   |
++LL |     if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else {
++LL |         let x = 5;
++LL |         None
++LL |     }
++   |
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a522c0f08b207a5abd7cf037c304b39357c4a4b3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++// aux-build:option_helpers.rs
++
++#![warn(clippy::skip_while_next)]
++#![allow(clippy::blacklisted_name)]
++
++extern crate option_helpers;
++use option_helpers::IteratorFalsePositives;
++
++#[rustfmt::skip]
++fn skip_while_next() {
++    let v = vec![3, 2, 1, 0, -1, -2, -3];
++
++    // Single-line case.
++    let _ = v.iter().skip_while(|&x| *x < 0).next();
++
++    // Multi-line case.
++    let _ = v.iter().skip_while(|&x| {
++                                *x < 0
++                            }
++                   ).next();
++
++    // Check that hat we don't lint if the caller is not an `Iterator`.
++    let foo = IteratorFalsePositives { foo: 0 };
++    let _ = foo.skip_while().next();
++}
++
++fn main() {
++    skip_while_next();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a6b7bcd63ff39922285bcf8973e06d9e09ee7e71
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++error: called `skip_while(p).next()` on an `Iterator`
++  --> $DIR/skip_while_next.rs:14:13
++   |
++LL |     let _ = v.iter().skip_while(|&x| *x < 0).next();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::skip-while-next` implied by `-D warnings`
++   = help: this is more succinctly expressed by calling `.find(!p)` instead
++
++error: called `skip_while(p).next()` on an `Iterator`
++  --> $DIR/skip_while_next.rs:17:13
++   |
++LL |       let _ = v.iter().skip_while(|&x| {
++   |  _____________^
++LL | |                                 *x < 0
++LL | |                             }
++LL | |                    ).next();
++   | |___________________________^
++   |
++   = help: this is more succinctly expressed by calling `.find(!p)` instead
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c5ae3ff769b11bb8dd6513a95728fb16fb18d7d1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++use std::iter::repeat;
++
++fn main() {
++    resize_vector();
++    extend_vector();
++    mixed_extend_resize_vector();
++}
++
++fn extend_vector() {
++    // Extend with constant expression
++    let len = 300;
++    let mut vec1 = Vec::with_capacity(len);
++    vec1.extend(repeat(0).take(len));
++
++    // Extend with len expression
++    let mut vec2 = Vec::with_capacity(len - 10);
++    vec2.extend(repeat(0).take(len - 10));
++
++    // Extend with mismatching expression should not be warned
++    let mut vec3 = Vec::with_capacity(24322);
++    vec3.extend(repeat(0).take(2));
++}
++
++fn mixed_extend_resize_vector() {
++    // Mismatching len
++    let mut mismatching_len = Vec::with_capacity(30);
++    mismatching_len.extend(repeat(0).take(40));
++
++    // Slow initialization
++    let mut resized_vec = Vec::with_capacity(30);
++    resized_vec.resize(30, 0);
++
++    let mut extend_vec = Vec::with_capacity(30);
++    extend_vec.extend(repeat(0).take(30));
++}
++
++fn resize_vector() {
++    // Resize with constant expression
++    let len = 300;
++    let mut vec1 = Vec::with_capacity(len);
++    vec1.resize(len, 0);
++
++    // Resize mismatch len
++    let mut vec2 = Vec::with_capacity(200);
++    vec2.resize(10, 0);
++
++    // Resize with len expression
++    let mut vec3 = Vec::with_capacity(len - 10);
++    vec3.resize(len - 10, 0);
++
++    // Reinitialization should be warned
++    vec1 = Vec::with_capacity(10);
++    vec1.resize(10, 0);
++}
++
++fn do_stuff(vec: &mut Vec<u8>) {}
++
++fn extend_vector_with_manipulations_between() {
++    let len = 300;
++    let mut vec1: Vec<u8> = Vec::with_capacity(len);
++    do_stuff(&mut vec1);
++    vec1.extend(repeat(0).take(len));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d2788ec2608632267ef559e10960515924136d6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,60 @@@
++error: slow zero-filling initialization
++  --> $DIR/slow_vector_initialization.rs:13:5
++   |
++LL |     let mut vec1 = Vec::with_capacity(len);
++   |                    ----------------------- help: consider replace allocation with: `vec![0; len]`
++LL |     vec1.extend(repeat(0).take(len));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::slow-vector-initialization` implied by `-D warnings`
++
++error: slow zero-filling initialization
++  --> $DIR/slow_vector_initialization.rs:17:5
++   |
++LL |     let mut vec2 = Vec::with_capacity(len - 10);
++   |                    ---------------------------- help: consider replace allocation with: `vec![0; len - 10]`
++LL |     vec2.extend(repeat(0).take(len - 10));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: slow zero-filling initialization
++  --> $DIR/slow_vector_initialization.rs:31:5
++   |
++LL |     let mut resized_vec = Vec::with_capacity(30);
++   |                           ---------------------- help: consider replace allocation with: `vec![0; 30]`
++LL |     resized_vec.resize(30, 0);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: slow zero-filling initialization
++  --> $DIR/slow_vector_initialization.rs:34:5
++   |
++LL |     let mut extend_vec = Vec::with_capacity(30);
++   |                          ---------------------- help: consider replace allocation with: `vec![0; 30]`
++LL |     extend_vec.extend(repeat(0).take(30));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: slow zero-filling initialization
++  --> $DIR/slow_vector_initialization.rs:41:5
++   |
++LL |     let mut vec1 = Vec::with_capacity(len);
++   |                    ----------------------- help: consider replace allocation with: `vec![0; len]`
++LL |     vec1.resize(len, 0);
++   |     ^^^^^^^^^^^^^^^^^^^
++
++error: slow zero-filling initialization
++  --> $DIR/slow_vector_initialization.rs:49:5
++   |
++LL |     let mut vec3 = Vec::with_capacity(len - 10);
++   |                    ---------------------------- help: consider replace allocation with: `vec![0; len - 10]`
++LL |     vec3.resize(len - 10, 0);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: slow zero-filling initialization
++  --> $DIR/slow_vector_initialization.rs:53:5
++   |
++LL |     vec1 = Vec::with_capacity(10);
++   |            ---------------------- help: consider replace allocation with: `vec![0; 10]`
++LL |     vec1.resize(10, 0);
++   |     ^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7dfcf9c91e486a8b4bd7273660f4b358b99611c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++// run-rustfix
++#![allow(dead_code, unused_must_use)]
++
++fn main() {}
++
++#[allow(clippy::unnecessary_operation)]
++fn starts_with() {
++    "".starts_with(' ');
++    !"".starts_with(' ');
++}
++
++fn chars_cmp_with_unwrap() {
++    let s = String::from("foo");
++    if s.starts_with('f') {
++        // s.starts_with('f')
++        // Nothing here
++    }
++    if s.ends_with('o') {
++        // s.ends_with('o')
++        // Nothing here
++    }
++    if s.ends_with('o') {
++        // s.ends_with('o')
++        // Nothing here
++    }
++    if !s.starts_with('f') {
++        // !s.starts_with('f')
++        // Nothing here
++    }
++    if !s.ends_with('o') {
++        // !s.ends_with('o')
++        // Nothing here
++    }
++    if !s.ends_with('o') {
++        // !s.ends_with('o')
++        // Nothing here
++    }
++}
++
++#[allow(clippy::unnecessary_operation)]
++fn ends_with() {
++    "".ends_with(' ');
++    !"".ends_with(' ');
++    "".ends_with(' ');
++    !"".ends_with(' ');
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e48a4246354391000da93003a6347b02bafbd0cc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++// run-rustfix
++#![allow(dead_code, unused_must_use)]
++
++fn main() {}
++
++#[allow(clippy::unnecessary_operation)]
++fn starts_with() {
++    "".chars().next() == Some(' ');
++    Some(' ') != "".chars().next();
++}
++
++fn chars_cmp_with_unwrap() {
++    let s = String::from("foo");
++    if s.chars().next().unwrap() == 'f' {
++        // s.starts_with('f')
++        // Nothing here
++    }
++    if s.chars().next_back().unwrap() == 'o' {
++        // s.ends_with('o')
++        // Nothing here
++    }
++    if s.chars().last().unwrap() == 'o' {
++        // s.ends_with('o')
++        // Nothing here
++    }
++    if s.chars().next().unwrap() != 'f' {
++        // !s.starts_with('f')
++        // Nothing here
++    }
++    if s.chars().next_back().unwrap() != 'o' {
++        // !s.ends_with('o')
++        // Nothing here
++    }
++    if s.chars().last().unwrap() != 'o' {
++        // !s.ends_with('o')
++        // Nothing here
++    }
++}
++
++#[allow(clippy::unnecessary_operation)]
++fn ends_with() {
++    "".chars().last() == Some(' ');
++    Some(' ') != "".chars().last();
++    "".chars().next_back() == Some(' ');
++    Some(' ') != "".chars().next_back();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7c726d0e010263ffca9352d1c41325cb042e9e86
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,78 @@@
++error: you should use the `starts_with` method
++  --> $DIR/starts_ends_with.rs:8:5
++   |
++LL |     "".chars().next() == Some(' ');
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".starts_with(' ')`
++   |
++   = note: `-D clippy::chars-next-cmp` implied by `-D warnings`
++
++error: you should use the `starts_with` method
++  --> $DIR/starts_ends_with.rs:9:5
++   |
++LL |     Some(' ') != "".chars().next();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".starts_with(' ')`
++
++error: you should use the `starts_with` method
++  --> $DIR/starts_ends_with.rs:14:8
++   |
++LL |     if s.chars().next().unwrap() == 'f' {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.starts_with('f')`
++
++error: you should use the `ends_with` method
++  --> $DIR/starts_ends_with.rs:18:8
++   |
++LL |     if s.chars().next_back().unwrap() == 'o' {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.ends_with('o')`
++   |
++   = note: `-D clippy::chars-last-cmp` implied by `-D warnings`
++
++error: you should use the `ends_with` method
++  --> $DIR/starts_ends_with.rs:22:8
++   |
++LL |     if s.chars().last().unwrap() == 'o' {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.ends_with('o')`
++
++error: you should use the `starts_with` method
++  --> $DIR/starts_ends_with.rs:26:8
++   |
++LL |     if s.chars().next().unwrap() != 'f' {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.starts_with('f')`
++
++error: you should use the `ends_with` method
++  --> $DIR/starts_ends_with.rs:30:8
++   |
++LL |     if s.chars().next_back().unwrap() != 'o' {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.ends_with('o')`
++
++error: you should use the `ends_with` method
++  --> $DIR/starts_ends_with.rs:34:8
++   |
++LL |     if s.chars().last().unwrap() != 'o' {
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.ends_with('o')`
++
++error: you should use the `ends_with` method
++  --> $DIR/starts_ends_with.rs:42:5
++   |
++LL |     "".chars().last() == Some(' ');
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".ends_with(' ')`
++
++error: you should use the `ends_with` method
++  --> $DIR/starts_ends_with.rs:43:5
++   |
++LL |     Some(' ') != "".chars().last();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".ends_with(' ')`
++
++error: you should use the `ends_with` method
++  --> $DIR/starts_ends_with.rs:44:5
++   |
++LL |     "".chars().next_back() == Some(' ');
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".ends_with(' ')`
++
++error: you should use the `ends_with` method
++  --> $DIR/starts_ends_with.rs:45:5
++   |
++LL |     Some(' ') != "".chars().next_back();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".ends_with(' ')`
++
++error: aborting due to 12 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..30fd17c59e518ed931dd82d8ed01c5e64865cf62
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++// aux-build:macro_rules.rs
++
++#[macro_use]
++extern crate macro_rules;
++
++#[warn(clippy::string_add)]
++#[allow(clippy::string_add_assign, unused)]
++fn main() {
++    // ignores assignment distinction
++    let mut x = "".to_owned();
++
++    for _ in 1..3 {
++        x = x + ".";
++    }
++
++    let y = "".to_owned();
++    let z = y + "...";
++
++    assert_eq!(&x, &z);
++
++    let mut x = 1;
++    x = x + 1;
++    assert_eq!(2, x);
++
++    string_add!();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3987641c75a30c04ce8dd03626eae79188722ee4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++error: manual implementation of an assign operation
++  --> $DIR/string_add.rs:13:9
++   |
++LL |         x = x + ".";
++   |         ^^^^^^^^^^^ help: replace it with: `x += "."`
++   |
++   = note: `-D clippy::assign-op-pattern` implied by `-D warnings`
++
++error: you added something to a string. Consider using `String::push_str()` instead
++  --> $DIR/string_add.rs:13:13
++   |
++LL |         x = x + ".";
++   |             ^^^^^^^
++   |
++   = note: `-D clippy::string-add` implied by `-D warnings`
++
++error: you added something to a string. Consider using `String::push_str()` instead
++  --> $DIR/string_add.rs:17:13
++   |
++LL |     let z = y + "...";
++   |             ^^^^^^^^^
++
++error: manual implementation of an assign operation
++  --> $DIR/string_add.rs:22:5
++   |
++LL |     x = x + 1;
++   |     ^^^^^^^^^ help: replace it with: `x += 1`
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..db71bab1e5214a96baceeb1a62ee426e3d6173a3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++// run-rustfix
++
++#[allow(clippy::string_add, unused)]
++#[warn(clippy::string_add_assign)]
++fn main() {
++    // ignores assignment distinction
++    let mut x = "".to_owned();
++
++    for _ in 1..3 {
++        x += ".";
++    }
++
++    let y = "".to_owned();
++    let z = y + "...";
++
++    assert_eq!(&x, &z);
++
++    let mut x = 1;
++    x += 1;
++    assert_eq!(2, x);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..644991945cbe2b77da49ca71a1e5e8a8830301a3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++// run-rustfix
++
++#[allow(clippy::string_add, unused)]
++#[warn(clippy::string_add_assign)]
++fn main() {
++    // ignores assignment distinction
++    let mut x = "".to_owned();
++
++    for _ in 1..3 {
++        x = x + ".";
++    }
++
++    let y = "".to_owned();
++    let z = y + "...";
++
++    assert_eq!(&x, &z);
++
++    let mut x = 1;
++    x = x + 1;
++    assert_eq!(2, x);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7676175c1b82f39a2943db9a5b48c7fd484c5359
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++error: you assigned the result of adding something to this string. Consider using `String::push_str()` instead
++  --> $DIR/string_add_assign.rs:10:9
++   |
++LL |         x = x + ".";
++   |         ^^^^^^^^^^^
++   |
++   = note: `-D clippy::string-add-assign` implied by `-D warnings`
++
++error: manual implementation of an assign operation
++  --> $DIR/string_add_assign.rs:10:9
++   |
++LL |         x = x + ".";
++   |         ^^^^^^^^^^^ help: replace it with: `x += "."`
++   |
++   = note: `-D clippy::assign-op-pattern` implied by `-D warnings`
++
++error: manual implementation of an assign operation
++  --> $DIR/string_add_assign.rs:19:5
++   |
++LL |     x = x + 1;
++   |     ^^^^^^^^^ help: replace it with: `x += 1`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1883a9f8325783ecb7949b9d21bf791842c701ff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++// run-rustfix
++
++#[derive(Copy, Clone)]
++struct HasChars;
++
++impl HasChars {
++    fn chars(self) -> std::str::Chars<'static> {
++        "HasChars".chars()
++    }
++}
++
++fn main() {
++    let abc = "abc";
++    let def = String::from("def");
++    let mut s = String::new();
++
++    s.push_str(abc);
++    s.push_str(abc);
++
++    s.push_str("abc");
++    s.push_str("abc");
++
++    s.push_str(&def);
++    s.push_str(&def);
++
++    s.extend(abc.chars().skip(1));
++    s.extend("abc".chars().skip(1));
++    s.extend(['a', 'b', 'c'].iter());
++
++    let f = HasChars;
++    s.extend(f.chars());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..07d0baa1be6c7ee5ecbf653ec408b491d959de48
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++// run-rustfix
++
++#[derive(Copy, Clone)]
++struct HasChars;
++
++impl HasChars {
++    fn chars(self) -> std::str::Chars<'static> {
++        "HasChars".chars()
++    }
++}
++
++fn main() {
++    let abc = "abc";
++    let def = String::from("def");
++    let mut s = String::new();
++
++    s.push_str(abc);
++    s.extend(abc.chars());
++
++    s.push_str("abc");
++    s.extend("abc".chars());
++
++    s.push_str(&def);
++    s.extend(def.chars());
++
++    s.extend(abc.chars().skip(1));
++    s.extend("abc".chars().skip(1));
++    s.extend(['a', 'b', 'c'].iter());
++
++    let f = HasChars;
++    s.extend(f.chars());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6af8c9e1662b5b970237dfe206e83ce6b13cc8be
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: calling `.extend(_.chars())`
++  --> $DIR/string_extend.rs:18:5
++   |
++LL |     s.extend(abc.chars());
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(abc)`
++   |
++   = note: `-D clippy::string-extend-chars` implied by `-D warnings`
++
++error: calling `.extend(_.chars())`
++  --> $DIR/string_extend.rs:21:5
++   |
++LL |     s.extend("abc".chars());
++   |     ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str("abc")`
++
++error: calling `.extend(_.chars())`
++  --> $DIR/string_extend.rs:24:5
++   |
++LL |     s.extend(def.chars());
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(&def)`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7ad272ade5f9c6bb6fbde13e3182a28b47cff0a3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++// run-rustfix
++
++#![allow(dead_code, unused_variables)]
++#![warn(clippy::string_lit_as_bytes)]
++
++fn str_lit_as_bytes() {
++    let bs = b"hello there";
++
++    let bs = br###"raw string with 3# plus " ""###;
++
++    // 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 strify = stringify!(foobar).as_bytes();
++
++    let includestr = include_bytes!("entry_unfixable.rs");
++
++    let _ = b"string with newline\t\n";
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1bf4538b7c94f25069289340e53ae4d8819b9142
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++// run-rustfix
++
++#![allow(dead_code, unused_variables)]
++#![warn(clippy::string_lit_as_bytes)]
++
++fn str_lit_as_bytes() {
++    let bs = "hello there".as_bytes();
++
++    let bs = r###"raw string with 3# plus " ""###.as_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 strify = stringify!(foobar).as_bytes();
++
++    let includestr = include_str!("entry_unfixable.rs").as_bytes();
++
++    let _ = "string with newline\t\n".as_bytes();
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ff6e3346dfc756e6112e9a8d1f6128236b0261e1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: calling `as_bytes()` on a string literal
++  --> $DIR/string_lit_as_bytes.rs:7:14
++   |
++LL |     let bs = "hello there".as_bytes();
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"hello there"`
++   |
++   = note: `-D clippy::string-lit-as-bytes` implied by `-D warnings`
++
++error: calling `as_bytes()` on a string literal
++  --> $DIR/string_lit_as_bytes.rs:9:14
++   |
++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 `as_bytes()` on `include_str!(..)`
++  --> $DIR/string_lit_as_bytes.rs:17: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:19: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
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ce4fe830a0a210e17e692b00fa01baf6f4c444fe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++#![warn(clippy::struct_excessive_bools)]
++
++macro_rules! foo {
++    () => {
++        struct MacroFoo {
++            a: bool,
++            b: bool,
++            c: bool,
++            d: bool,
++        }
++    };
++}
++
++foo!();
++
++struct Foo {
++    a: bool,
++    b: bool,
++    c: bool,
++}
++
++struct BadFoo {
++    a: bool,
++    b: bool,
++    c: bool,
++    d: bool,
++}
++
++#[repr(C)]
++struct Bar {
++    a: bool,
++    b: bool,
++    c: bool,
++    d: bool,
++}
++
++fn main() {
++    struct FooFoo {
++        a: bool,
++        b: bool,
++        c: bool,
++        d: bool,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2941bf2983aa8ba739591a15e34cc599a6af6e39
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++error: more than 3 bools in a struct
++  --> $DIR/struct_excessive_bools.rs:22:1
++   |
++LL | / struct BadFoo {
++LL | |     a: bool,
++LL | |     b: bool,
++LL | |     c: bool,
++LL | |     d: bool,
++LL | | }
++   | |_^
++   |
++   = note: `-D clippy::struct-excessive-bools` implied by `-D warnings`
++   = help: consider using a state machine or refactoring bools into two-variant enums
++
++error: more than 3 bools in a struct
++  --> $DIR/struct_excessive_bools.rs:38:5
++   |
++LL | /     struct FooFoo {
++LL | |         a: bool,
++LL | |         b: bool,
++LL | |         c: bool,
++LL | |         d: bool,
++LL | |     }
++   | |_____^
++   |
++   = help: consider using a state machine or refactoring bools into two-variant enums
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1f5b981188706fd579b77f761c5563a79bdaa7f6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,90 @@@
++#![warn(clippy::suspicious_arithmetic_impl)]
++use std::ops::{Add, AddAssign, BitOrAssign, Div, DivAssign, Mul, MulAssign, Sub};
++
++#[derive(Copy, Clone)]
++struct Foo(u32);
++
++impl Add for Foo {
++    type Output = Foo;
++
++    fn add(self, other: Self) -> Self {
++        Foo(self.0 - other.0)
++    }
++}
++
++impl AddAssign for Foo {
++    fn add_assign(&mut self, other: Foo) {
++        *self = *self - other;
++    }
++}
++
++impl BitOrAssign for Foo {
++    fn bitor_assign(&mut self, other: Foo) {
++        let idx = other.0;
++        self.0 |= 1 << idx; // OK: BinOpKind::Shl part of AssignOp as child node
++    }
++}
++
++impl MulAssign for Foo {
++    fn mul_assign(&mut self, other: Foo) {
++        self.0 /= other.0;
++    }
++}
++
++impl DivAssign for Foo {
++    fn div_assign(&mut self, other: Foo) {
++        self.0 /= other.0; // OK: BinOpKind::Div == DivAssign
++    }
++}
++
++impl Mul for Foo {
++    type Output = Foo;
++
++    fn mul(self, other: Foo) -> Foo {
++        Foo(self.0 * other.0 % 42) // OK: BinOpKind::Rem part of BiExpr as parent node
++    }
++}
++
++impl Sub for Foo {
++    type Output = Foo;
++
++    fn sub(self, other: Self) -> Self {
++        Foo(self.0 * other.0 - 42) // OK: BinOpKind::Mul part of BiExpr as child node
++    }
++}
++
++impl Div for Foo {
++    type Output = Foo;
++
++    fn div(self, other: Self) -> Self {
++        Foo(do_nothing(self.0 + other.0) / 42) // OK: BinOpKind::Add part of BiExpr as child node
++    }
++}
++
++struct Bar(i32);
++
++impl Add for Bar {
++    type Output = Bar;
++
++    fn add(self, other: Self) -> Self {
++        Bar(self.0 & !other.0) // OK: UnNot part of BiExpr as child node
++    }
++}
++
++impl Sub for Bar {
++    type Output = Bar;
++
++    fn sub(self, other: Self) -> Self {
++        if self.0 <= other.0 {
++            Bar(-(self.0 & other.0)) // OK: UnNeg part of BiExpr as parent node
++        } else {
++            Bar(0)
++        }
++    }
++}
++
++fn main() {}
++
++fn do_nothing(x: u32) -> u32 {
++    x
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7e42d72c30b2cefd6503f6315ac316e0e372053f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++error: Suspicious use of binary operator in `Add` impl
++  --> $DIR/suspicious_arithmetic_impl.rs:11:20
++   |
++LL |         Foo(self.0 - other.0)
++   |                    ^
++   |
++   = note: `-D clippy::suspicious-arithmetic-impl` implied by `-D warnings`
++
++error: Suspicious use of binary operator in `AddAssign` impl
++  --> $DIR/suspicious_arithmetic_impl.rs:17:23
++   |
++LL |         *self = *self - other;
++   |                       ^
++   |
++   = note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default
++
++error: Suspicious use of binary operator in `MulAssign` impl
++  --> $DIR/suspicious_arithmetic_impl.rs:30:16
++   |
++LL |         self.0 /= other.0;
++   |                ^^
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d838d8fde2105244a90d0067fb5386172686dbaa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,5 @@@
++#![warn(clippy::suspicious_map)]
++
++fn main() {
++    let _ = (0..3).map(|x| x + 2).count();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e1b4ba40376f88d0904b96cf6c459e57bbbd56ee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++error: this call to `map()` won't have an effect on the call to `count()`
++  --> $DIR/suspicious_map.rs:4: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
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9564e373c246d7094c89821734c285de7c867d18
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++#![warn(clippy::suspicious_unary_op_formatting)]
++
++#[rustfmt::skip]
++fn main() {
++    // weird binary operator formatting:
++    let a = 42;
++
++    if a >- 30 {}
++    if a >=- 30 {}
++
++    let b = true;
++    let c = false;
++
++    if b &&! c {}
++
++    if a >-   30 {}
++
++    // those are ok:
++    if a >-30 {}
++    if a < -30 {}
++    if b && !c {}
++    if a > -   30 {}
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..581527dcff8e46da31c6e6d425e7e7144e9d4fde
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++error: by not having a space between `>` and `-` it looks like `>-` is a single operator
++  --> $DIR/suspicious_unary_op_formatting.rs:8:9
++   |
++LL |     if a >- 30 {}
++   |         ^^^^
++   |
++   = note: `-D clippy::suspicious-unary-op-formatting` implied by `-D warnings`
++   = help: put a space between `>` and `-` and remove the space after `-`
++
++error: by not having a space between `>=` and `-` it looks like `>=-` is a single operator
++  --> $DIR/suspicious_unary_op_formatting.rs:9:9
++   |
++LL |     if a >=- 30 {}
++   |         ^^^^^
++   |
++   = help: put a space between `>=` and `-` and remove the space after `-`
++
++error: by not having a space between `&&` and `!` it looks like `&&!` is a single operator
++  --> $DIR/suspicious_unary_op_formatting.rs:14:9
++   |
++LL |     if b &&! c {}
++   |         ^^^^^
++   |
++   = help: put a space between `&&` and `!` and remove the space after `!`
++
++error: by not having a space between `>` and `-` it looks like `>-` is a single operator
++  --> $DIR/suspicious_unary_op_formatting.rs:16:9
++   |
++LL |     if a >-   30 {}
++   |         ^^^^^^
++   |
++   = help: put a space between `>` and `-` and remove the space after `-`
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0f8f839a0d5426fa808fbe2384875ee427ff18ed
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,83 @@@
++// run-rustfix
++
++#![warn(clippy::all)]
++#![allow(
++    clippy::blacklisted_name,
++    clippy::no_effect,
++    clippy::redundant_clone,
++    redundant_semicolons,
++    unused_assignments
++)]
++
++struct Foo(u32);
++
++#[derive(Clone)]
++struct Bar {
++    a: u32,
++    b: u32,
++}
++
++fn field() {
++    let mut bar = Bar { a: 1, b: 2 };
++
++    let temp = bar.a;
++    bar.a = bar.b;
++    bar.b = temp;
++
++    let mut baz = vec![bar.clone(), bar.clone()];
++    let temp = baz[0].a;
++    baz[0].a = baz[1].a;
++    baz[1].a = temp;
++}
++
++fn array() {
++    let mut foo = [1, 2];
++    foo.swap(0, 1);
++
++    foo.swap(0, 1);
++}
++
++fn slice() {
++    let foo = &mut [1, 2];
++    foo.swap(0, 1);
++
++    foo.swap(0, 1);
++}
++
++fn unswappable_slice() {
++    let foo = &mut [vec![1, 2], vec![3, 4]];
++    let temp = foo[0][1];
++    foo[0][1] = foo[1][0];
++    foo[1][0] = temp;
++
++    // swap(foo[0][1], foo[1][0]) would fail
++}
++
++fn vec() {
++    let mut foo = vec![1, 2];
++    foo.swap(0, 1);
++
++    foo.swap(0, 1);
++}
++
++#[rustfmt::skip]
++fn main() {
++    field();
++    array();
++    slice();
++    unswappable_slice();
++    vec();
++
++    let mut a = 42;
++    let mut b = 1337;
++
++    std::mem::swap(&mut a, &mut b);
++
++    ; std::mem::swap(&mut a, &mut b);
++
++    let mut c = Foo(42);
++
++    std::mem::swap(&mut c.0, &mut a);
++
++    ; std::mem::swap(&mut c.0, &mut a);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5763d9e82d486779f12c1d4b9ba11f684229b33c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,95 @@@
++// run-rustfix
++
++#![warn(clippy::all)]
++#![allow(
++    clippy::blacklisted_name,
++    clippy::no_effect,
++    clippy::redundant_clone,
++    redundant_semicolons,
++    unused_assignments
++)]
++
++struct Foo(u32);
++
++#[derive(Clone)]
++struct Bar {
++    a: u32,
++    b: u32,
++}
++
++fn field() {
++    let mut bar = Bar { a: 1, b: 2 };
++
++    let temp = bar.a;
++    bar.a = bar.b;
++    bar.b = temp;
++
++    let mut baz = vec![bar.clone(), bar.clone()];
++    let temp = baz[0].a;
++    baz[0].a = baz[1].a;
++    baz[1].a = temp;
++}
++
++fn array() {
++    let mut foo = [1, 2];
++    let temp = foo[0];
++    foo[0] = foo[1];
++    foo[1] = temp;
++
++    foo.swap(0, 1);
++}
++
++fn slice() {
++    let foo = &mut [1, 2];
++    let temp = foo[0];
++    foo[0] = foo[1];
++    foo[1] = temp;
++
++    foo.swap(0, 1);
++}
++
++fn unswappable_slice() {
++    let foo = &mut [vec![1, 2], vec![3, 4]];
++    let temp = foo[0][1];
++    foo[0][1] = foo[1][0];
++    foo[1][0] = temp;
++
++    // swap(foo[0][1], foo[1][0]) would fail
++}
++
++fn vec() {
++    let mut foo = vec![1, 2];
++    let temp = foo[0];
++    foo[0] = foo[1];
++    foo[1] = temp;
++
++    foo.swap(0, 1);
++}
++
++#[rustfmt::skip]
++fn main() {
++    field();
++    array();
++    slice();
++    unswappable_slice();
++    vec();
++
++    let mut a = 42;
++    let mut b = 1337;
++
++    a = b;
++    b = a;
++
++    ; let t = a;
++    a = b;
++    b = t;
++
++    let mut c = Foo(42);
++
++    c.0 = a;
++    a = c.0;
++
++    ; let t = c.0;
++    c.0 = a;
++    a = t;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f49bcfedf3a1970b59751afe03ef8bf400d5cc23
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,69 @@@
++error: this looks like you are swapping elements of `foo` manually
++  --> $DIR/swap.rs:35:5
++   |
++LL | /     let temp = foo[0];
++LL | |     foo[0] = foo[1];
++LL | |     foo[1] = temp;
++   | |_________________^ help: try: `foo.swap(0, 1)`
++   |
++   = note: `-D clippy::manual-swap` implied by `-D warnings`
++
++error: this looks like you are swapping elements of `foo` manually
++  --> $DIR/swap.rs:44:5
++   |
++LL | /     let temp = foo[0];
++LL | |     foo[0] = foo[1];
++LL | |     foo[1] = temp;
++   | |_________________^ help: try: `foo.swap(0, 1)`
++
++error: this looks like you are swapping elements of `foo` manually
++  --> $DIR/swap.rs:62:5
++   |
++LL | /     let temp = foo[0];
++LL | |     foo[0] = foo[1];
++LL | |     foo[1] = temp;
++   | |_________________^ help: try: `foo.swap(0, 1)`
++
++error: this looks like you are swapping `a` and `b` manually
++  --> $DIR/swap.rs:83:7
++   |
++LL |       ; let t = a;
++   |  _______^
++LL | |     a = b;
++LL | |     b = t;
++   | |_________^ help: try: `std::mem::swap(&mut a, &mut b)`
++   |
++   = note: or maybe you should use `std::mem::replace`?
++
++error: this looks like you are swapping `c.0` and `a` manually
++  --> $DIR/swap.rs:92:7
++   |
++LL |       ; let t = c.0;
++   |  _______^
++LL | |     c.0 = a;
++LL | |     a = t;
++   | |_________^ help: try: `std::mem::swap(&mut c.0, &mut a)`
++   |
++   = note: or maybe you should use `std::mem::replace`?
++
++error: this looks like you are trying to swap `a` and `b`
++  --> $DIR/swap.rs:80:5
++   |
++LL | /     a = b;
++LL | |     b = a;
++   | |_________^ help: try: `std::mem::swap(&mut a, &mut b)`
++   |
++   = note: `-D clippy::almost-swapped` implied by `-D warnings`
++   = note: or maybe you should use `std::mem::replace`?
++
++error: this looks like you are trying to swap `c.0` and `a`
++  --> $DIR/swap.rs:89:5
++   |
++LL | /     c.0 = a;
++LL | |     a = c.0;
++   | |___________^ help: try: `std::mem::swap(&mut c.0, &mut a)`
++   |
++   = note: or maybe you should use `std::mem::replace`?
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4bc4bc86c76c291dd2fd9417c00fc7ec16a0e195
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++// run-rustfix
++
++#![warn(clippy::tabs_in_doc_comments)]
++#[allow(dead_code)]
++
++///
++/// Struct to hold two strings:
++///     - first        one
++///     - second    one
++pub struct DoubleString {
++    ///
++    ///     - First String:
++    ///         - needs to be inside here
++    first_string: String,
++    ///
++    ///     - Second String:
++    ///         - needs to be inside here
++    second_string: String,
++}
++
++/// This is main
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9db3416e6596409c385bf20c1a40e746a24b1b01
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++// run-rustfix
++
++#![warn(clippy::tabs_in_doc_comments)]
++#[allow(dead_code)]
++
++///
++/// Struct to hold two strings:
++///   - first         one
++///   - second        one
++pub struct DoubleString {
++    ///
++    ///       - First String:
++    ///               - needs to be inside here
++    first_string: String,
++    ///
++    ///       - Second String:
++    ///               - needs to be inside here
++    second_string: String,
++}
++
++/// This is main
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..355f2e805796436a09d4900cb596ed3848926bc7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++error: using tabs in doc comments is not recommended
++  --> $DIR/tabs_in_doc_comments.rs:12:9
++   |
++LL |     ///     - First String:
++   |         ^^^^ help: consider using four spaces per tab
++   |
++   = note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings`
++
++error: using tabs in doc comments is not recommended
++  --> $DIR/tabs_in_doc_comments.rs:13:9
++   |
++LL |     ///         - needs to be inside here
++   |         ^^^^^^^^ help: consider using four spaces per tab
++
++error: using tabs in doc comments is not recommended
++  --> $DIR/tabs_in_doc_comments.rs:16:9
++   |
++LL |     ///     - Second String:
++   |         ^^^^ help: consider using four spaces per tab
++
++error: using tabs in doc comments is not recommended
++  --> $DIR/tabs_in_doc_comments.rs:17:9
++   |
++LL |     ///         - needs to be inside here
++   |         ^^^^^^^^ help: consider using four spaces per tab
++
++error: using tabs in doc comments is not recommended
++  --> $DIR/tabs_in_doc_comments.rs:8:5
++   |
++LL | ///     - first        one
++   |     ^^^^ help: consider using four spaces per tab
++
++error: using tabs in doc comments is not recommended
++  --> $DIR/tabs_in_doc_comments.rs:8:13
++   |
++LL | ///     - first        one
++   |                ^^^^^^^^ help: consider using four spaces per tab
++
++error: using tabs in doc comments is not recommended
++  --> $DIR/tabs_in_doc_comments.rs:9:5
++   |
++LL | ///     - second    one
++   |     ^^^^ help: consider using four spaces per tab
++
++error: using tabs in doc comments is not recommended
++  --> $DIR/tabs_in_doc_comments.rs:9:14
++   |
++LL | ///     - second    one
++   |                 ^^^^ help: consider using four spaces per tab
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c6c315d5fab5d359aa7378719722fdb9a8ead38c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,75 @@@
++#![warn(clippy::temporary_assignment)]
++
++use std::ops::{Deref, DerefMut};
++
++struct TupleStruct(i32);
++
++struct Struct {
++    field: i32,
++}
++
++struct MultiStruct {
++    structure: Struct,
++}
++
++struct Wrapper<'a> {
++    inner: &'a mut Struct,
++}
++
++impl<'a> Deref for Wrapper<'a> {
++    type Target = Struct;
++    fn deref(&self) -> &Struct {
++        self.inner
++    }
++}
++
++impl<'a> DerefMut for Wrapper<'a> {
++    fn deref_mut(&mut self) -> &mut Struct {
++        self.inner
++    }
++}
++
++struct ArrayStruct {
++    array: [i32; 1],
++}
++
++const A: TupleStruct = TupleStruct(1);
++const B: Struct = Struct { field: 1 };
++const C: MultiStruct = MultiStruct {
++    structure: Struct { field: 1 },
++};
++const D: ArrayStruct = ArrayStruct { array: [1] };
++
++fn main() {
++    let mut s = Struct { field: 0 };
++    let mut t = (0, 0);
++
++    Struct { field: 0 }.field = 1;
++    MultiStruct {
++        structure: Struct { field: 0 },
++    }
++    .structure
++    .field = 1;
++    ArrayStruct { array: [0] }.array[0] = 1;
++    (0, 0).0 = 1;
++
++    A.0 = 2;
++    B.field = 2;
++    C.structure.field = 2;
++    D.array[0] = 2;
++
++    // no error
++    s.field = 1;
++    t.0 = 1;
++    Wrapper { inner: &mut s }.field = 1;
++    let mut a_mut = TupleStruct(1);
++    a_mut.0 = 2;
++    let mut b_mut = Struct { field: 1 };
++    b_mut.field = 2;
++    let mut c_mut = MultiStruct {
++        structure: Struct { field: 1 },
++    };
++    c_mut.structure.field = 2;
++    let mut d_mut = ArrayStruct { array: [1] };
++    d_mut.array[0] = 2;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4efe2d4bb6713625bd6b02fb299670e32e135155
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++error: assignment to temporary
++  --> $DIR/temporary_assignment.rs:47:5
++   |
++LL |     Struct { field: 0 }.field = 1;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::temporary-assignment` implied by `-D warnings`
++
++error: assignment to temporary
++  --> $DIR/temporary_assignment.rs:48:5
++   |
++LL | /     MultiStruct {
++LL | |         structure: Struct { field: 0 },
++LL | |     }
++LL | |     .structure
++LL | |     .field = 1;
++   | |______________^
++
++error: assignment to temporary
++  --> $DIR/temporary_assignment.rs:53:5
++   |
++LL |     ArrayStruct { array: [0] }.array[0] = 1;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: assignment to temporary
++  --> $DIR/temporary_assignment.rs:54:5
++   |
++LL |     (0, 0).0 = 1;
++   |     ^^^^^^^^^^^^
++
++error: assignment to temporary
++  --> $DIR/temporary_assignment.rs:56:5
++   |
++LL |     A.0 = 2;
++   |     ^^^^^^^
++
++error: assignment to temporary
++  --> $DIR/temporary_assignment.rs:57:5
++   |
++LL |     B.field = 2;
++   |     ^^^^^^^^^^^
++
++error: assignment to temporary
++  --> $DIR/temporary_assignment.rs:58:5
++   |
++LL |     C.structure.field = 2;
++   |     ^^^^^^^^^^^^^^^^^^^^^
++
++error: assignment to temporary
++  --> $DIR/temporary_assignment.rs:59:5
++   |
++LL |     D.array[0] = 2;
++   |     ^^^^^^^^^^^^^^
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..19184df0becb579fc6a505bf5924fc089aeb52e9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++//run-rustfix
++
++#![warn(clippy::to_digit_is_some)]
++
++fn main() {
++    let c = 'x';
++    let d = &c;
++
++    let _ = d.is_digit(10);
++    let _ = char::is_digit(c, 10);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..45a6728ebf57812f6facc06f55a104780bc4972b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++//run-rustfix
++
++#![warn(clippy::to_digit_is_some)]
++
++fn main() {
++    let c = 'x';
++    let d = &c;
++
++    let _ = d.to_digit(10).is_some();
++    let _ = char::to_digit(c, 10).is_some();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..177d3ccd3e23d7a4f507df4dc3dd053161ee1d8d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: use of `.to_digit(..).is_some()`
++  --> $DIR/to_digit_is_some.rs:9:13
++   |
++LL |     let _ = d.to_digit(10).is_some();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(10)`
++   |
++   = note: `-D clippy::to-digit-is-some` implied by `-D warnings`
++
++error: use of `.to_digit(..).is_some()`
++  --> $DIR/to_digit_is_some.rs:10:13
++   |
++LL |     let _ = char::to_digit(c, 10).is_some();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 10)`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..33605aca0199c4e3099aca9e50179fa41df7f58d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++// run-rustfix
++
++#![warn(clippy::toplevel_ref_arg)]
++
++fn main() {
++    // Closures should not warn
++    let y = |ref x| println!("{:?}", x);
++    y(1u8);
++
++    let _x = &1;
++
++    let _y: &(&_, u8) = &(&1, 2);
++
++    let _z = &(1 + 2);
++
++    let _z = &mut (1 + 2);
++
++    let (ref x, _) = (1, 2); // ok, not top level
++    println!("The answer is {}.", x);
++
++    let _x = &vec![1, 2, 3];
++
++    // Make sure that allowing the lint works
++    #[allow(clippy::toplevel_ref_arg)]
++    let ref mut _x = 1_234_543;
++
++    // ok
++    for ref _x in 0..10 {}
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..59759f1189333539a3b3c718e252f1fdb11fbfb1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++// run-rustfix
++
++#![warn(clippy::toplevel_ref_arg)]
++
++fn main() {
++    // Closures should not warn
++    let y = |ref x| println!("{:?}", x);
++    y(1u8);
++
++    let ref _x = 1;
++
++    let ref _y: (&_, u8) = (&1, 2);
++
++    let ref _z = 1 + 2;
++
++    let ref mut _z = 1 + 2;
++
++    let (ref x, _) = (1, 2); // ok, not top level
++    println!("The answer is {}.", x);
++
++    let ref _x = vec![1, 2, 3];
++
++    // Make sure that allowing the lint works
++    #[allow(clippy::toplevel_ref_arg)]
++    let ref mut _x = 1_234_543;
++
++    // ok
++    for ref _x in 0..10 {}
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..19d69496709be158fc260cceb39ca02b4f21bcb4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
++  --> $DIR/toplevel_ref_arg.rs:10:9
++   |
++LL |     let ref _x = 1;
++   |     ----^^^^^^----- help: try: `let _x = &1;`
++   |
++   = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings`
++
++error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
++  --> $DIR/toplevel_ref_arg.rs:12:9
++   |
++LL |     let ref _y: (&_, u8) = (&1, 2);
++   |     ----^^^^^^--------------------- help: try: `let _y: &(&_, u8) = &(&1, 2);`
++
++error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
++  --> $DIR/toplevel_ref_arg.rs:14:9
++   |
++LL |     let ref _z = 1 + 2;
++   |     ----^^^^^^--------- help: try: `let _z = &(1 + 2);`
++
++error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
++  --> $DIR/toplevel_ref_arg.rs:16:9
++   |
++LL |     let ref mut _z = 1 + 2;
++   |     ----^^^^^^^^^^--------- help: try: `let _z = &mut (1 + 2);`
++
++error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
++  --> $DIR/toplevel_ref_arg.rs:21:9
++   |
++LL |     let ref _x = vec![1, 2, 3];
++   |     ----^^^^^^----------------- help: try: `let _x = &vec![1, 2, 3];`
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..42cac2ba4de255ef3a2a96602458577f02ee9342
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++#![warn(clippy::toplevel_ref_arg)]
++#![allow(unused)]
++
++fn the_answer(ref mut x: u8) {
++    *x = 42;
++}
++
++fn main() {
++    let mut x = 0;
++    the_answer(x);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..295e2f35608bfc6d533e4dc4238e25faf7a48d18
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: `ref` directly on a function argument is ignored. Consider using a reference type instead.
++  --> $DIR/toplevel_ref_arg_non_rustfix.rs:4:15
++   |
++LL | fn the_answer(ref mut x: u8) {
++   |               ^^^^^^^^^
++   |
++   = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1cef8c2cfc997dc0a7937dbae6fcdcc7b872a8f7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++#![allow(unused_parens)]
++
++fn main() {
++    let x: i32 = 42;
++    let _ = (x & 0b1111 == 0); // suggest trailing_zeros
++    let _ = x & 0b1_1111 == 0; // suggest trailing_zeros
++    let _ = x & 0b1_1010 == 0; // do not lint
++    let _ = x & 1 == 0; // do not lint
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..320d9cc3f643412bad951fad6547a7ea07a8d2a3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: bit mask could be simplified with a call to `trailing_zeros`
++  --> $DIR/trailing_zeros.rs:5:13
++   |
++LL |     let _ = (x & 0b1111 == 0); // suggest trailing_zeros
++   |             ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 4`
++   |
++   = note: `-D clippy::verbose-bit-mask` implied by `-D warnings`
++
++error: bit mask could be simplified with a call to `trailing_zeros`
++  --> $DIR/trailing_zeros.rs:6:13
++   |
++LL |     let _ = x & 0b1_1111 == 0; // suggest trailing_zeros
++   |             ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 5`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bb853d237047fbc33f6bd85b50c04ad8f2e5edf2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,94 @@@
++#![allow(dead_code)]
++
++extern crate core;
++
++use std::mem::transmute as my_transmute;
++use std::vec::Vec as MyVec;
++
++fn my_int() -> Usize {
++    Usize(42)
++}
++
++fn my_vec() -> MyVec<i32> {
++    vec![]
++}
++
++#[allow(clippy::needless_lifetimes, clippy::transmute_ptr_to_ptr)]
++#[warn(clippy::useless_transmute)]
++unsafe fn _generic<'a, T, U: 'a>(t: &'a T) {
++    let _: &'a T = core::intrinsics::transmute(t);
++
++    let _: &'a U = core::intrinsics::transmute(t);
++
++    let _: *const T = core::intrinsics::transmute(t);
++
++    let _: *mut T = core::intrinsics::transmute(t);
++
++    let _: *const U = core::intrinsics::transmute(t);
++}
++
++#[warn(clippy::useless_transmute)]
++fn useless() {
++    unsafe {
++        let _: Vec<i32> = core::intrinsics::transmute(my_vec());
++
++        let _: Vec<i32> = core::mem::transmute(my_vec());
++
++        let _: Vec<i32> = std::intrinsics::transmute(my_vec());
++
++        let _: Vec<i32> = std::mem::transmute(my_vec());
++
++        let _: Vec<i32> = my_transmute(my_vec());
++
++        let _: *const usize = std::mem::transmute(5_isize);
++
++        let _ = 5_isize as *const usize;
++
++        let _: *const usize = std::mem::transmute(1 + 1usize);
++
++        let _ = (1 + 1_usize) as *const usize;
++    }
++}
++
++struct Usize(usize);
++
++#[warn(clippy::crosspointer_transmute)]
++fn crosspointer() {
++    let mut int: Usize = Usize(0);
++    let int_const_ptr: *const Usize = &int as *const Usize;
++    let int_mut_ptr: *mut Usize = &mut int as *mut Usize;
++
++    unsafe {
++        let _: Usize = core::intrinsics::transmute(int_const_ptr);
++
++        let _: Usize = core::intrinsics::transmute(int_mut_ptr);
++
++        let _: *const Usize = core::intrinsics::transmute(my_int());
++
++        let _: *mut Usize = core::intrinsics::transmute(my_int());
++    }
++}
++
++#[warn(clippy::transmute_int_to_char)]
++fn int_to_char() {
++    let _: char = unsafe { std::mem::transmute(0_u32) };
++    let _: char = unsafe { std::mem::transmute(0_i32) };
++}
++
++#[warn(clippy::transmute_int_to_bool)]
++fn int_to_bool() {
++    let _: bool = unsafe { std::mem::transmute(0_u8) };
++}
++
++#[warn(clippy::transmute_int_to_float)]
++fn int_to_float() {
++    let _: f32 = unsafe { std::mem::transmute(0_u32) };
++    let _: f32 = unsafe { std::mem::transmute(0_i32) };
++}
++
++fn bytes_to_str(b: &[u8], mb: &mut [u8]) {
++    let _: &str = unsafe { std::mem::transmute(b) };
++    let _: &mut str = unsafe { std::mem::transmute(mb) };
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8582080498f3e4febde7b3ef68d942c6a8eaf3bd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,146 @@@
++error: transmute from a type (`&T`) to itself
++  --> $DIR/transmute.rs:19:20
++   |
++LL |     let _: &'a T = core::intrinsics::transmute(t);
++   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::useless-transmute` implied by `-D warnings`
++
++error: transmute from a reference to a pointer
++  --> $DIR/transmute.rs:23:23
++   |
++LL |     let _: *const T = core::intrinsics::transmute(t);
++   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T`
++
++error: transmute from a reference to a pointer
++  --> $DIR/transmute.rs:25:21
++   |
++LL |     let _: *mut T = core::intrinsics::transmute(t);
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T`
++
++error: transmute from a reference to a pointer
++  --> $DIR/transmute.rs:27:23
++   |
++LL |     let _: *const U = core::intrinsics::transmute(t);
++   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U`
++
++error: transmute from a type (`std::vec::Vec<i32>`) to itself
++  --> $DIR/transmute.rs:33:27
++   |
++LL |         let _: Vec<i32> = core::intrinsics::transmute(my_vec());
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a type (`std::vec::Vec<i32>`) to itself
++  --> $DIR/transmute.rs:35:27
++   |
++LL |         let _: Vec<i32> = core::mem::transmute(my_vec());
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a type (`std::vec::Vec<i32>`) to itself
++  --> $DIR/transmute.rs:37:27
++   |
++LL |         let _: Vec<i32> = std::intrinsics::transmute(my_vec());
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a type (`std::vec::Vec<i32>`) to itself
++  --> $DIR/transmute.rs:39:27
++   |
++LL |         let _: Vec<i32> = std::mem::transmute(my_vec());
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a type (`std::vec::Vec<i32>`) to itself
++  --> $DIR/transmute.rs:41:27
++   |
++LL |         let _: Vec<i32> = my_transmute(my_vec());
++   |                           ^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from an integer to a pointer
++  --> $DIR/transmute.rs:43:31
++   |
++LL |         let _: *const usize = std::mem::transmute(5_isize);
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize`
++
++error: transmute from an integer to a pointer
++  --> $DIR/transmute.rs:47:31
++   |
++LL |         let _: *const usize = std::mem::transmute(1 + 1usize);
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize`
++
++error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`)
++  --> $DIR/transmute.rs:62:24
++   |
++LL |         let _: Usize = core::intrinsics::transmute(int_const_ptr);
++   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::crosspointer-transmute` implied by `-D warnings`
++
++error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`)
++  --> $DIR/transmute.rs:64:24
++   |
++LL |         let _: Usize = core::intrinsics::transmute(int_mut_ptr);
++   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`)
++  --> $DIR/transmute.rs:66:31
++   |
++LL |         let _: *const Usize = core::intrinsics::transmute(my_int());
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`)
++  --> $DIR/transmute.rs:68:29
++   |
++LL |         let _: *mut Usize = core::intrinsics::transmute(my_int());
++   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a `u32` to a `char`
++  --> $DIR/transmute.rs:74:28
++   |
++LL |     let _: char = unsafe { std::mem::transmute(0_u32) };
++   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()`
++   |
++   = note: `-D clippy::transmute-int-to-char` implied by `-D warnings`
++
++error: transmute from a `i32` to a `char`
++  --> $DIR/transmute.rs:75:28
++   |
++LL |     let _: char = unsafe { std::mem::transmute(0_i32) };
++   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()`
++
++error: transmute from a `u8` to a `bool`
++  --> $DIR/transmute.rs:80:28
++   |
++LL |     let _: bool = unsafe { std::mem::transmute(0_u8) };
++   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0`
++   |
++   = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings`
++
++error: transmute from a `u32` to a `f32`
++  --> $DIR/transmute.rs:85:27
++   |
++LL |     let _: f32 = unsafe { std::mem::transmute(0_u32) };
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)`
++   |
++   = note: `-D clippy::transmute-int-to-float` implied by `-D warnings`
++
++error: transmute from a `i32` to a `f32`
++  --> $DIR/transmute.rs:86:27
++   |
++LL |     let _: f32 = unsafe { std::mem::transmute(0_i32) };
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)`
++
++error: transmute from a `&[u8]` to a `&str`
++  --> $DIR/transmute.rs:90:28
++   |
++LL |     let _: &str = unsafe { std::mem::transmute(b) };
++   |                            ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()`
++   |
++   = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings`
++
++error: transmute from a `&mut [u8]` to a `&mut str`
++  --> $DIR/transmute.rs:91:32
++   |
++LL |     let _: &mut str = unsafe { std::mem::transmute(mb) };
++   |                                ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()`
++
++error: aborting due to 22 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ffe22b12f5510646450f9219061828cb2c5e69de
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++// ignore-64bit
++
++#[warn(clippy::wrong_transmute)]
++fn main() {
++    unsafe {
++        let _: *const usize = std::mem::transmute(6.0f32);
++
++        let _: *mut usize = std::mem::transmute(6.0f32);
++
++        let _: *const usize = std::mem::transmute('x');
++
++        let _: *mut usize = std::mem::transmute('x');
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..040519564b94cf64230269e3c50344a19db2851b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: transmute from a `f32` to a pointer
++  --> $DIR/transmute_32bit.rs:6:31
++   |
++LL |         let _: *const usize = std::mem::transmute(6.0f32);
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::wrong-transmute` implied by `-D warnings`
++
++error: transmute from a `f32` to a pointer
++  --> $DIR/transmute_32bit.rs:8:29
++   |
++LL |         let _: *mut usize = std::mem::transmute(6.0f32);
++   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a `char` to a pointer
++  --> $DIR/transmute_32bit.rs:10:31
++   |
++LL |         let _: *const usize = std::mem::transmute('x');
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from a `char` to a pointer
++  --> $DIR/transmute_32bit.rs:12:29
++   |
++LL |         let _: *mut usize = std::mem::transmute('x');
++   |                             ^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..00dc0b2c36081b0433744a86849c6349f699731a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++// ignore-32bit
++
++#[warn(clippy::wrong_transmute)]
++fn main() {
++    unsafe {
++        let _: *const usize = std::mem::transmute(6.0f64);
++
++        let _: *mut usize = std::mem::transmute(6.0f64);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d1854c009ef56f9b183f551dfdcc76e5943819e8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: transmute from a `f64` to a pointer
++  --> $DIR/transmute_64bit.rs:6:31
++   |
++LL |         let _: *const usize = std::mem::transmute(6.0f64);
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::wrong-transmute` implied by `-D warnings`
++
++error: transmute from a `f64` to a pointer
++  --> $DIR/transmute_64bit.rs:8:29
++   |
++LL |         let _: *mut usize = std::mem::transmute(6.0f64);
++   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cd5a7127791a86784fe8332d91c2589e9ba10d49
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++#![warn(clippy::unsound_collection_transmute)]
++
++use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
++use std::mem::transmute;
++
++fn main() {
++    unsafe {
++        // wrong size
++        let _ = transmute::<_, Vec<u32>>(vec![0u8]);
++        // wrong layout
++        let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]);
++
++        // wrong size
++        let _ = transmute::<_, VecDeque<u32>>(VecDeque::<u8>::new());
++        // wrong layout
++        let _ = transmute::<_, VecDeque<u32>>(VecDeque::<[u8; 4]>::new());
++
++        // wrong size
++        let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<u8>::new());
++        // wrong layout
++        let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<[u8; 4]>::new());
++
++        // wrong size
++        let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<u8>::new());
++        // wrong layout
++        let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<[u8; 4]>::new());
++
++        // wrong size
++        let _ = transmute::<_, HashSet<u32>>(HashSet::<u8>::new());
++        // wrong layout
++        let _ = transmute::<_, HashSet<u32>>(HashSet::<[u8; 4]>::new());
++
++        // wrong size
++        let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, u8>::new());
++        let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u32, u32>::new());
++        // wrong layout
++        let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, [u8; 4]>::new());
++        let _ = transmute::<_, BTreeMap<u32, u32>>(BTreeMap::<[u8; 4], u32>::new());
++
++        // wrong size
++        let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, u8>::new());
++        let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u32, u32>::new());
++        // wrong layout
++        let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, [u8; 4]>::new());
++        let _ = transmute::<_, HashMap<u32, u32>>(HashMap::<[u8; 4], u32>::new());
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ebc05c402abf69b9e0856083c46286677db22e20
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,112 @@@
++error: transmute from `std::vec::Vec<u8>` to `std::vec::Vec<u32>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:9:17
++   |
++LL |         let _ = transmute::<_, Vec<u32>>(vec![0u8]);
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::unsound-collection-transmute` implied by `-D warnings`
++
++error: transmute from `std::vec::Vec<u32>` to `std::vec::Vec<[u8; 4]>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:11:17
++   |
++LL |         let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]);
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::VecDeque<u8>` to `std::collections::VecDeque<u32>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:14:17
++   |
++LL |         let _ = transmute::<_, VecDeque<u32>>(VecDeque::<u8>::new());
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::VecDeque<[u8; 4]>` to `std::collections::VecDeque<u32>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:16:17
++   |
++LL |         let _ = transmute::<_, VecDeque<u32>>(VecDeque::<[u8; 4]>::new());
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::BinaryHeap<u8>` to `std::collections::BinaryHeap<u32>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:19:17
++   |
++LL |         let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<u8>::new());
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::BinaryHeap<[u8; 4]>` to `std::collections::BinaryHeap<u32>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:21:17
++   |
++LL |         let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<[u8; 4]>::new());
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::BTreeSet<u8>` to `std::collections::BTreeSet<u32>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:24:17
++   |
++LL |         let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<u8>::new());
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::BTreeSet<[u8; 4]>` to `std::collections::BTreeSet<u32>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:26:17
++   |
++LL |         let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<[u8; 4]>::new());
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::HashSet<u8>` to `std::collections::HashSet<u32>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:29:17
++   |
++LL |         let _ = transmute::<_, HashSet<u32>>(HashSet::<u8>::new());
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::HashSet<[u8; 4]>` to `std::collections::HashSet<u32>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:31:17
++   |
++LL |         let _ = transmute::<_, HashSet<u32>>(HashSet::<[u8; 4]>::new());
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::BTreeMap<u8, u8>` to `std::collections::BTreeMap<u8, u32>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:34:17
++   |
++LL |         let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, u8>::new());
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::BTreeMap<u32, u32>` to `std::collections::BTreeMap<u8, u32>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:35:17
++   |
++LL |         let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u32, u32>::new());
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::BTreeMap<u8, [u8; 4]>` to `std::collections::BTreeMap<u8, u32>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:37:17
++   |
++LL |         let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, [u8; 4]>::new());
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::BTreeMap<[u8; 4], u32>` to `std::collections::BTreeMap<u32, u32>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:38:17
++   |
++LL |         let _ = transmute::<_, BTreeMap<u32, u32>>(BTreeMap::<[u8; 4], u32>::new());
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::HashMap<u8, u8>` to `std::collections::HashMap<u8, u32>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:41:17
++   |
++LL |         let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, u8>::new());
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::HashMap<u32, u32>` to `std::collections::HashMap<u8, u32>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:42:17
++   |
++LL |         let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u32, u32>::new());
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::HashMap<u8, [u8; 4]>` to `std::collections::HashMap<u8, u32>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:44:17
++   |
++LL |         let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, [u8; 4]>::new());
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmute from `std::collections::HashMap<[u8; 4], u32>` to `std::collections::HashMap<u32, u32>` with mismatched layout is unsound
++  --> $DIR/transmute_collection.rs:45:17
++   |
++LL |         let _ = transmute::<_, HashMap<u32, u32>>(HashMap::<[u8; 4], u32>::new());
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 18 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ce942751ada82cee06778cf4599528b3073e5397
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++#[warn(clippy::transmute_float_to_int)]
++
++fn float_to_int() {
++    let _: u32 = unsafe { std::mem::transmute(1f32) };
++    let _: i32 = unsafe { std::mem::transmute(1f32) };
++    let _: u64 = unsafe { std::mem::transmute(1f64) };
++    let _: i64 = unsafe { std::mem::transmute(1f64) };
++    let _: u64 = unsafe { std::mem::transmute(1.0) };
++    let _: u64 = unsafe { std::mem::transmute(-1.0) };
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eb786bb39f95aa5c1f7110d053acf78cd96b340b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: transmute from a `f32` to a `u32`
++  --> $DIR/transmute_float_to_int.rs:4:27
++   |
++LL |     let _: u32 = unsafe { std::mem::transmute(1f32) };
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits()`
++   |
++   = note: `-D clippy::transmute-float-to-int` implied by `-D warnings`
++
++error: transmute from a `f32` to a `i32`
++  --> $DIR/transmute_float_to_int.rs:5:27
++   |
++LL |     let _: i32 = unsafe { std::mem::transmute(1f32) };
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32`
++
++error: transmute from a `f64` to a `u64`
++  --> $DIR/transmute_float_to_int.rs:6:27
++   |
++LL |     let _: u64 = unsafe { std::mem::transmute(1f64) };
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()`
++
++error: transmute from a `f64` to a `i64`
++  --> $DIR/transmute_float_to_int.rs:7:27
++   |
++LL |     let _: i64 = unsafe { std::mem::transmute(1f64) };
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits() as i64`
++
++error: transmute from a `f64` to a `u64`
++  --> $DIR/transmute_float_to_int.rs:8:27
++   |
++LL |     let _: u64 = unsafe { std::mem::transmute(1.0) };
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.0f64.to_bits()`
++
++error: transmute from a `f64` to a `u64`
++  --> $DIR/transmute_float_to_int.rs:9:27
++   |
++LL |     let _: u64 = unsafe { std::mem::transmute(-1.0) };
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()`
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0d8a322f2b2b0b7fa31f410c4af644409b1e0310
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,54 @@@
++#![warn(clippy::transmute_ptr_to_ptr)]
++
++// Make sure we can modify lifetimes, which is one of the recommended uses
++// of transmute
++
++// Make sure we can do static lifetime transmutes
++unsafe fn transmute_lifetime_to_static<'a, T>(t: &'a T) -> &'static T {
++    std::mem::transmute::<&'a T, &'static T>(t)
++}
++
++// Make sure we can do non-static lifetime transmutes
++unsafe fn transmute_lifetime<'a, 'b, T>(t: &'a T, u: &'b T) -> &'b T {
++    std::mem::transmute::<&'a T, &'b T>(t)
++}
++
++struct LifetimeParam<'a> {
++    s: &'a str,
++}
++
++struct GenericParam<T> {
++    t: T,
++}
++
++fn transmute_ptr_to_ptr() {
++    let ptr = &1u32 as *const u32;
++    let mut_ptr = &mut 1u32 as *mut u32;
++    unsafe {
++        // pointer-to-pointer transmutes; bad
++        let _: *const f32 = std::mem::transmute(ptr);
++        let _: *mut f32 = std::mem::transmute(mut_ptr);
++        // ref-ref transmutes; bad
++        let _: &f32 = std::mem::transmute(&1u32);
++        let _: &f64 = std::mem::transmute(&1f32);
++        // ^ this test is here because both f32 and f64 are the same TypeVariant, but they are not
++        // the same type
++        let _: &mut f32 = std::mem::transmute(&mut 1u32);
++        let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 });
++    }
++
++    // these are recommendations for solving the above; if these lint we need to update
++    // those suggestions
++    let _ = ptr as *const f32;
++    let _ = mut_ptr as *mut f32;
++    let _ = unsafe { &*(&1u32 as *const u32 as *const f32) };
++    let _ = unsafe { &mut *(&mut 1u32 as *mut u32 as *mut f32) };
++
++    // transmute internal lifetimes, should not lint
++    let s = "hello world".to_owned();
++    let lp = LifetimeParam { s: &s };
++    let _: &LifetimeParam<'static> = unsafe { std::mem::transmute(&lp) };
++    let _: &GenericParam<&LifetimeParam<'static>> = unsafe { std::mem::transmute(&GenericParam { t: &lp }) };
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4d1b8fcc199e80b0860876026fa2febf28105a26
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: transmute from a pointer to a pointer
++  --> $DIR/transmute_ptr_to_ptr.rs:29:29
++   |
++LL |         let _: *const f32 = std::mem::transmute(ptr);
++   |                             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr as *const f32`
++   |
++   = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings`
++
++error: transmute from a pointer to a pointer
++  --> $DIR/transmute_ptr_to_ptr.rs:30:27
++   |
++LL |         let _: *mut f32 = std::mem::transmute(mut_ptr);
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `mut_ptr as *mut f32`
++
++error: transmute from a reference to a reference
++  --> $DIR/transmute_ptr_to_ptr.rs:32:23
++   |
++LL |         let _: &f32 = std::mem::transmute(&1u32);
++   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)`
++
++error: transmute from a reference to a reference
++  --> $DIR/transmute_ptr_to_ptr.rs:33:23
++   |
++LL |         let _: &f64 = std::mem::transmute(&1f32);
++   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1f32 as *const f32 as *const f64)`
++
++error: transmute from a reference to a reference
++  --> $DIR/transmute_ptr_to_ptr.rs:36:27
++   |
++LL |         let _: &mut f32 = std::mem::transmute(&mut 1u32);
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)`
++
++error: transmute from a reference to a reference
++  --> $DIR/transmute_ptr_to_ptr.rs:37:37
++   |
++LL |         let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 });
++   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>)`
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ba35c6adc4deebb66edd529fb8c432ad0cb5dfbb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++#![warn(clippy::transmute_ptr_to_ref)]
++
++unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
++    let _: &T = std::mem::transmute(p);
++    let _: &T = &*p;
++
++    let _: &mut T = std::mem::transmute(m);
++    let _: &mut T = &mut *m;
++
++    let _: &T = std::mem::transmute(m);
++    let _: &T = &*m;
++
++    let _: &mut T = std::mem::transmute(p as *mut T);
++    let _ = &mut *(p as *mut T);
++
++    let _: &T = std::mem::transmute(o);
++    let _: &T = &*(o as *const T);
++
++    let _: &mut T = std::mem::transmute(om);
++    let _: &mut T = &mut *(om as *mut T);
++
++    let _: &T = std::mem::transmute(om);
++    let _: &T = &*(om as *const T);
++}
++
++fn issue1231() {
++    struct Foo<'a, T> {
++        bar: &'a T,
++    }
++
++    let raw = 42 as *const i32;
++    let _: &Foo<u8> = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) };
++
++    let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) };
++
++    type Bar<'a> = &'a u8;
++    let raw = 42 as *const i32;
++    unsafe { std::mem::transmute::<_, Bar>(raw) };
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..df0598a58cd3617cdb539261d29416537215802f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,64 @@@
++error: transmute from a pointer type (`*const T`) to a reference type (`&T`)
++  --> $DIR/transmute_ptr_to_ref.rs:4:17
++   |
++LL |     let _: &T = std::mem::transmute(p);
++   |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p`
++   |
++   = note: `-D clippy::transmute-ptr-to-ref` implied by `-D warnings`
++
++error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`)
++  --> $DIR/transmute_ptr_to_ref.rs:7:21
++   |
++LL |     let _: &mut T = std::mem::transmute(m);
++   |                     ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m`
++
++error: transmute from a pointer type (`*mut T`) to a reference type (`&T`)
++  --> $DIR/transmute_ptr_to_ref.rs:10:17
++   |
++LL |     let _: &T = std::mem::transmute(m);
++   |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m`
++
++error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`)
++  --> $DIR/transmute_ptr_to_ref.rs:13:21
++   |
++LL |     let _: &mut T = std::mem::transmute(p as *mut T);
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)`
++
++error: transmute from a pointer type (`*const U`) to a reference type (`&T`)
++  --> $DIR/transmute_ptr_to_ref.rs:16:17
++   |
++LL |     let _: &T = std::mem::transmute(o);
++   |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)`
++
++error: transmute from a pointer type (`*mut U`) to a reference type (`&mut T`)
++  --> $DIR/transmute_ptr_to_ref.rs:19:21
++   |
++LL |     let _: &mut T = std::mem::transmute(om);
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)`
++
++error: transmute from a pointer type (`*mut U`) to a reference type (`&T`)
++  --> $DIR/transmute_ptr_to_ref.rs:22:17
++   |
++LL |     let _: &T = std::mem::transmute(om);
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)`
++
++error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<u8>`)
++  --> $DIR/transmute_ptr_to_ref.rs:32:32
++   |
++LL |     let _: &Foo<u8> = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) };
++   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const Foo<_>)`
++
++error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<&u8>`)
++  --> $DIR/transmute_ptr_to_ref.rs:34:33
++   |
++LL |     let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) };
++   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const Foo<&_>)`
++
++error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`)
++  --> $DIR/transmute_ptr_to_ref.rs:38:14
++   |
++LL |     unsafe { std::mem::transmute::<_, Bar>(raw) };
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)`
++
++error: aborting due to 10 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ea3ee8edc81b1b65cd16aea50dab9da6d311d44b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++#![allow(dead_code)]
++#![warn(clippy::transmuting_null)]
++#![allow(clippy::zero_ptr)]
++#![allow(clippy::transmute_ptr_to_ref)]
++#![allow(clippy::eq_op)]
++
++// Easy to lint because these only span one line.
++fn one_liners() {
++    unsafe {
++        let _: &u64 = std::mem::transmute(0 as *const u64);
++        let _: &u64 = std::mem::transmute(std::ptr::null::<u64>());
++    }
++}
++
++pub const ZPTR: *const usize = 0 as *const _;
++pub const NOT_ZPTR: *const usize = 1 as *const _;
++
++fn transmute_const() {
++    unsafe {
++        // Should raise a lint.
++        let _: &u64 = std::mem::transmute(ZPTR);
++        // Should NOT raise a lint.
++        let _: &u64 = std::mem::transmute(NOT_ZPTR);
++    }
++}
++
++fn main() {
++    one_liners();
++    transmute_const();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..05f91ee2adaa8b37e2ffd769445166e13aa7e112
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: transmuting a known null pointer into a reference.
++  --> $DIR/transmuting_null.rs:10:23
++   |
++LL |         let _: &u64 = std::mem::transmute(0 as *const u64);
++   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::transmuting-null` implied by `-D warnings`
++
++error: transmuting a known null pointer into a reference.
++  --> $DIR/transmuting_null.rs:11:23
++   |
++LL |         let _: &u64 = std::mem::transmute(std::ptr::null::<u64>());
++   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: transmuting a known null pointer into a reference.
++  --> $DIR/transmuting_null.rs:21:23
++   |
++LL |         let _: &u64 = std::mem::transmute(ZPTR);
++   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..316426f1cf181788dbc48dba7e69f244f4908531
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,114 @@@
++// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)"
++// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)"
++
++#![deny(clippy::trivially_copy_pass_by_ref)]
++#![allow(
++    clippy::many_single_char_names,
++    clippy::blacklisted_name,
++    clippy::redundant_field_names
++)]
++
++#[derive(Copy, Clone)]
++struct Foo(u32);
++
++#[derive(Copy, Clone)]
++struct Bar([u8; 24]);
++
++#[derive(Copy, Clone)]
++pub struct Color {
++    pub r: u8,
++    pub g: u8,
++    pub b: u8,
++    pub a: u8,
++}
++
++struct FooRef<'a> {
++    foo: &'a Foo,
++}
++
++type Baz = u32;
++
++fn good(a: &mut u32, b: u32, c: &Bar) {}
++
++fn good_return_implicit_lt_ref(foo: &Foo) -> &u32 {
++    &foo.0
++}
++
++#[allow(clippy::needless_lifetimes)]
++fn good_return_explicit_lt_ref<'a>(foo: &'a Foo) -> &'a u32 {
++    &foo.0
++}
++
++fn good_return_implicit_lt_struct(foo: &Foo) -> FooRef {
++    FooRef { foo }
++}
++
++#[allow(clippy::needless_lifetimes)]
++fn good_return_explicit_lt_struct<'a>(foo: &'a Foo) -> FooRef<'a> {
++    FooRef { foo }
++}
++
++fn bad(x: &u32, y: &Foo, z: &Baz) {}
++
++impl Foo {
++    fn good(self, a: &mut u32, b: u32, c: &Bar) {}
++
++    fn good2(&mut self) {}
++
++    fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
++
++    fn bad2(x: &u32, y: &Foo, z: &Baz) {}
++}
++
++impl AsRef<u32> for Foo {
++    fn as_ref(&self) -> &u32 {
++        &self.0
++    }
++}
++
++impl Bar {
++    fn good(&self, a: &mut u32, b: u32, c: &Bar) {}
++
++    fn bad2(x: &u32, y: &Foo, z: &Baz) {}
++}
++
++trait MyTrait {
++    fn trait_method(&self, _foo: &Foo);
++}
++
++pub trait MyTrait2 {
++    fn trait_method2(&self, _color: &Color);
++}
++
++impl MyTrait for Foo {
++    fn trait_method(&self, _foo: &Foo) {
++        unimplemented!()
++    }
++}
++
++#[allow(unused_variables)]
++mod issue3992 {
++    pub trait A {
++        #[allow(clippy::trivially_copy_pass_by_ref)]
++        fn a(b: &u16) {}
++    }
++
++    #[allow(clippy::trivially_copy_pass_by_ref)]
++    pub fn c(d: &u16) {}
++}
++
++fn main() {
++    let (mut foo, bar) = (Foo(0), Bar([0; 24]));
++    let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0);
++    good(&mut a, b, &c);
++    good_return_implicit_lt_ref(&y);
++    good_return_explicit_lt_ref(&y);
++    bad(&x, &y, &z);
++    foo.good(&mut a, b, &c);
++    foo.good2();
++    foo.bad(&x, &y, &z);
++    Foo::bad2(&x, &y, &z);
++    bar.good(&mut a, b, &c);
++    Bar::bad2(&x, &y, &z);
++    foo.as_ref();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..be0914e4a7947f7197002ee591009b0c79fa8abd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,98 @@@
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/trivially_copy_pass_by_ref.rs:51:11
++   |
++LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
++   |           ^^^^ help: consider passing by value instead: `u32`
++   |
++note: the lint level is defined here
++  --> $DIR/trivially_copy_pass_by_ref.rs:4:9
++   |
++LL | #![deny(clippy::trivially_copy_pass_by_ref)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/trivially_copy_pass_by_ref.rs:51:20
++   |
++LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
++   |                    ^^^^ help: consider passing by value instead: `Foo`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/trivially_copy_pass_by_ref.rs:51:29
++   |
++LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
++   |                             ^^^^ help: consider passing by value instead: `Baz`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/trivially_copy_pass_by_ref.rs:58:12
++   |
++LL |     fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
++   |            ^^^^^ help: consider passing by value instead: `self`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/trivially_copy_pass_by_ref.rs:58:22
++   |
++LL |     fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
++   |                      ^^^^ help: consider passing by value instead: `u32`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/trivially_copy_pass_by_ref.rs:58:31
++   |
++LL |     fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
++   |                               ^^^^ help: consider passing by value instead: `Foo`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/trivially_copy_pass_by_ref.rs:58:40
++   |
++LL |     fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
++   |                                        ^^^^ help: consider passing by value instead: `Baz`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/trivially_copy_pass_by_ref.rs:60:16
++   |
++LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
++   |                ^^^^ help: consider passing by value instead: `u32`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/trivially_copy_pass_by_ref.rs:60:25
++   |
++LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
++   |                         ^^^^ help: consider passing by value instead: `Foo`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/trivially_copy_pass_by_ref.rs:60:34
++   |
++LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
++   |                                  ^^^^ help: consider passing by value instead: `Baz`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/trivially_copy_pass_by_ref.rs:72:16
++   |
++LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
++   |                ^^^^ help: consider passing by value instead: `u32`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/trivially_copy_pass_by_ref.rs:72:25
++   |
++LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
++   |                         ^^^^ help: consider passing by value instead: `Foo`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/trivially_copy_pass_by_ref.rs:72:34
++   |
++LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
++   |                                  ^^^^ help: consider passing by value instead: `Baz`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/trivially_copy_pass_by_ref.rs:76:34
++   |
++LL |     fn trait_method(&self, _foo: &Foo);
++   |                                  ^^^^ help: consider passing by value instead: `Foo`
++
++error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/trivially_copy_pass_by_ref.rs:80:37
++   |
++LL |     fn trait_method2(&self, _color: &Color);
++   |                                     ^^^^^^ help: consider passing by value instead: `Color`
++
++error: aborting due to 15 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..29d9139d3e346e87e13cb62dbe96cfb9a08c9ebd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,106 @@@
++// run-rustfix
++// aux-build:macro_rules.rs
++
++#![deny(clippy::try_err)]
++
++#[macro_use]
++extern crate macro_rules;
++
++// Tests that a simple case works
++// Should flag `Err(err)?`
++pub fn basic_test() -> Result<i32, i32> {
++    let err: i32 = 1;
++    // To avoid warnings during rustfix
++    if true {
++        return Err(err);
++    }
++    Ok(0)
++}
++
++// Tests that `.into()` is added when appropriate
++pub fn into_test() -> Result<i32, i32> {
++    let err: u8 = 1;
++    // To avoid warnings during rustfix
++    if true {
++        return Err(err.into());
++    }
++    Ok(0)
++}
++
++// Tests that tries in general don't trigger the error
++pub fn negative_test() -> Result<i32, i32> {
++    Ok(nested_error()? + 1)
++}
++
++// Tests that `.into()` isn't added when the error type
++// matches the surrounding closure's return type, even
++// when it doesn't match the surrounding function's.
++pub fn closure_matches_test() -> Result<i32, i32> {
++    let res: Result<i32, i8> = Some(1)
++        .into_iter()
++        .map(|i| {
++            let err: i8 = 1;
++            // To avoid warnings during rustfix
++            if true {
++                return Err(err);
++            }
++            Ok(i)
++        })
++        .next()
++        .unwrap();
++
++    Ok(res?)
++}
++
++// Tests that `.into()` isn't added when the error type
++// doesn't match the surrounding closure's return type.
++pub fn closure_into_test() -> Result<i32, i32> {
++    let res: Result<i32, i16> = Some(1)
++        .into_iter()
++        .map(|i| {
++            let err: i8 = 1;
++            // To avoid warnings during rustfix
++            if true {
++                return Err(err.into());
++            }
++            Ok(i)
++        })
++        .next()
++        .unwrap();
++
++    Ok(res?)
++}
++
++fn nested_error() -> Result<i32, i32> {
++    Ok(1)
++}
++
++fn main() {
++    basic_test().unwrap();
++    into_test().unwrap();
++    negative_test().unwrap();
++    closure_matches_test().unwrap();
++    closure_into_test().unwrap();
++
++    // We don't want to lint in external macros
++    try_err!();
++}
++
++macro_rules! bar {
++    () => {
++        String::from("aasdfasdfasdfa")
++    };
++}
++
++macro_rules! foo {
++    () => {
++        bar!()
++    };
++}
++
++pub fn macro_inside(fail: bool) -> Result<i32, String> {
++    if fail {
++        return Err(foo!());
++    }
++    Ok(0)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5e85d091a2ae73359046f06a82d7e3ce744d9672
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,106 @@@
++// run-rustfix
++// aux-build:macro_rules.rs
++
++#![deny(clippy::try_err)]
++
++#[macro_use]
++extern crate macro_rules;
++
++// Tests that a simple case works
++// Should flag `Err(err)?`
++pub fn basic_test() -> Result<i32, i32> {
++    let err: i32 = 1;
++    // To avoid warnings during rustfix
++    if true {
++        Err(err)?;
++    }
++    Ok(0)
++}
++
++// Tests that `.into()` is added when appropriate
++pub fn into_test() -> Result<i32, i32> {
++    let err: u8 = 1;
++    // To avoid warnings during rustfix
++    if true {
++        Err(err)?;
++    }
++    Ok(0)
++}
++
++// Tests that tries in general don't trigger the error
++pub fn negative_test() -> Result<i32, i32> {
++    Ok(nested_error()? + 1)
++}
++
++// Tests that `.into()` isn't added when the error type
++// matches the surrounding closure's return type, even
++// when it doesn't match the surrounding function's.
++pub fn closure_matches_test() -> Result<i32, i32> {
++    let res: Result<i32, i8> = Some(1)
++        .into_iter()
++        .map(|i| {
++            let err: i8 = 1;
++            // To avoid warnings during rustfix
++            if true {
++                Err(err)?;
++            }
++            Ok(i)
++        })
++        .next()
++        .unwrap();
++
++    Ok(res?)
++}
++
++// Tests that `.into()` isn't added when the error type
++// doesn't match the surrounding closure's return type.
++pub fn closure_into_test() -> Result<i32, i32> {
++    let res: Result<i32, i16> = Some(1)
++        .into_iter()
++        .map(|i| {
++            let err: i8 = 1;
++            // To avoid warnings during rustfix
++            if true {
++                Err(err)?;
++            }
++            Ok(i)
++        })
++        .next()
++        .unwrap();
++
++    Ok(res?)
++}
++
++fn nested_error() -> Result<i32, i32> {
++    Ok(1)
++}
++
++fn main() {
++    basic_test().unwrap();
++    into_test().unwrap();
++    negative_test().unwrap();
++    closure_matches_test().unwrap();
++    closure_into_test().unwrap();
++
++    // We don't want to lint in external macros
++    try_err!();
++}
++
++macro_rules! bar {
++    () => {
++        String::from("aasdfasdfasdfa")
++    };
++}
++
++macro_rules! foo {
++    () => {
++        bar!()
++    };
++}
++
++pub fn macro_inside(fail: bool) -> Result<i32, String> {
++    if fail {
++        Err(foo!())?;
++    }
++    Ok(0)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..21e9d4048a5887bdc49d3a0e10f1a43f4f48df2f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++error: returning an `Err(_)` with the `?` operator
++  --> $DIR/try_err.rs:15:9
++   |
++LL |         Err(err)?;
++   |         ^^^^^^^^^ help: try this: `return Err(err)`
++   |
++note: the lint level is defined here
++  --> $DIR/try_err.rs:4:9
++   |
++LL | #![deny(clippy::try_err)]
++   |         ^^^^^^^^^^^^^^^
++
++error: returning an `Err(_)` with the `?` operator
++  --> $DIR/try_err.rs:25:9
++   |
++LL |         Err(err)?;
++   |         ^^^^^^^^^ help: try this: `return Err(err.into())`
++
++error: returning an `Err(_)` with the `?` operator
++  --> $DIR/try_err.rs:45:17
++   |
++LL |                 Err(err)?;
++   |                 ^^^^^^^^^ help: try this: `return Err(err)`
++
++error: returning an `Err(_)` with the `?` operator
++  --> $DIR/try_err.rs:64:17
++   |
++LL |                 Err(err)?;
++   |                 ^^^^^^^^^ help: try this: `return Err(err.into())`
++
++error: returning an `Err(_)` with the `?` operator
++  --> $DIR/try_err.rs:103:9
++   |
++LL |         Err(foo!())?;
++   |         ^^^^^^^^^^^^ help: try this: `return Err(foo!())`
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9e2753dcb18d6d5037debe0aa40eaa518571511c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++// Regression test
++
++pub fn retry<F: Fn()>(f: F) {
++    for _i in 0.. {
++        f();
++    }
++}
++
++fn main() {
++    for y in 0..4 {
++        let func = || ();
++        func();
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8b538be762b0c3bb9821171388e33c85c99f1fea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++#[deny(clippy::type_repetition_in_bounds)]
++
++pub fn foo<T>(_t: T)
++where
++    T: Copy,
++    T: Clone,
++{
++    unimplemented!();
++}
++
++pub fn bar<T, U>(_t: T, _u: U)
++where
++    T: Copy,
++    U: Clone,
++{
++    unimplemented!();
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4264e2e10bf17ebe8978e02132323f502ab0d2e9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++error: this type has already been used as a bound predicate
++  --> $DIR/type_repetition_in_bounds.rs:6:5
++   |
++LL |     T: Clone,
++   |     ^^^^^^^^
++   |
++note: the lint level is defined here
++  --> $DIR/type_repetition_in_bounds.rs:1:8
++   |
++LL | #[deny(clippy::type_repetition_in_bounds)]
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   = help: consider combining the bounds: `T: Copy + Clone`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..417da42edf17b3dff4b70692a998bf306a474908
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++// run-rustfix
++
++#![allow(dead_code, unused_variables)]
++#![warn(clippy::cast_lossless)]
++
++// should not warn on lossy casting in constant types
++// because not supported yet
++const C: i32 = 42;
++const C_I64: i64 = C as i64;
++
++fn main() {
++    // should suggest i64::from(c)
++    let c: i32 = 42;
++    let c_i64: i64 = i64::from(c);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b16e9e538b106a4c4fcb0a802ada71239501a6cb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++// run-rustfix
++
++#![allow(dead_code, unused_variables)]
++#![warn(clippy::cast_lossless)]
++
++// should not warn on lossy casting in constant types
++// because not supported yet
++const C: i32 = 42;
++const C_I64: i64 = C as i64;
++
++fn main() {
++    // should suggest i64::from(c)
++    let c: i32 = 42;
++    let c_i64: i64 = c as i64;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..59c3e05a1aa3ce9bf94ffc9cc87f4088275868b9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: casting `i32` to `i64` may become silently lossy if you later change the type
++  --> $DIR/types.rs:14:22
++   |
++LL |     let c_i64: i64 = c as i64;
++   |                      ^^^^^^^^ help: try: `i64::from(c)`
++   |
++   = note: `-D clippy::cast-lossless` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..27db9594f3b3305c7385ccea3506a22f534740f1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++#[warn(clippy::zero_width_space)]
++fn zero() {
++    print!("Here >​< is a ZWS, and ​another");
++    print!("This\u{200B}is\u{200B}fine");
++}
++
++#[warn(clippy::unicode_not_nfc)]
++fn canon() {
++    print!("̀àh?");
++    print!("a\u{0300}h?"); // also ok
++}
++
++#[warn(clippy::non_ascii_literal)]
++fn uni() {
++    print!("Üben!");
++    print!("\u{DC}ben!"); // this is ok
++}
++
++fn main() {
++    zero();
++    uni();
++    canon();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4575a132e5b2c8de599253e5e944993133443138
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++error: zero-width space detected
++  --> $DIR/unicode.rs:3:12
++   |
++LL |     print!("Here >​< is a ZWS, and ​another");
++   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"`
++   |
++   = note: `-D clippy::zero-width-space` implied by `-D warnings`
++
++error: non-NFC Unicode sequence detected
++  --> $DIR/unicode.rs:9:12
++   |
++LL |     print!("̀àh?");
++   |            ^^^^^ help: consider replacing the string with: `"̀àh?"`
++   |
++   = note: `-D clippy::unicode-not-nfc` implied by `-D warnings`
++
++error: literal non-ASCII character detected
++  --> $DIR/unicode.rs:15:12
++   |
++LL |     print!("Üben!");
++   |            ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"`
++   |
++   = note: `-D clippy::non-ascii-literal` implied by `-D warnings`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f42b884e0f0e546ce0bb95dd5793fbb85dda4e34
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++#![feature(stmt_expr_attributes)]
++
++use std::mem::MaybeUninit;
++
++fn main() {
++    let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
++
++    // edge case: For now we lint on empty arrays
++    let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() };
++
++    // edge case: For now we accept unit tuples
++    let _: () = unsafe { MaybeUninit::uninit().assume_init() };
++
++    // This is OK, because `MaybeUninit` allows uninitialized data.
++    let _: MaybeUninit<usize> = unsafe { MaybeUninit::uninit().assume_init() };
++
++    // This is OK, because all constitutent types are uninit-compatible.
++    let _: (MaybeUninit<usize>, MaybeUninit<bool>) = unsafe { MaybeUninit::uninit().assume_init() };
++
++    // This is OK, because all constitutent types are uninit-compatible.
++    let _: (MaybeUninit<usize>, [MaybeUninit<bool>; 2]) = unsafe { MaybeUninit::uninit().assume_init() };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a37233ecddaee0dd4181bb95e80ecc347d9210e2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: this call for this type may be undefined behavior
++  --> $DIR/uninit.rs:6:29
++   |
++LL |     let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
++   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `#[deny(clippy::uninit_assumed_init)]` on by default
++
++error: this call for this type may be undefined behavior
++  --> $DIR/uninit.rs:9:31
++   |
++LL |     let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() };
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a739cf7ad814eb8f399734e0acdc8db418244be7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,64 @@@
++// run-rustfix
++#![warn(clippy::unit_arg)]
++#![allow(unused_braces, clippy::no_effect, unused_must_use)]
++
++use std::fmt::Debug;
++
++fn foo<T: Debug>(t: T) {
++    println!("{:?}", t);
++}
++
++fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
++    println!("{:?}, {:?}, {:?}", t1, t2, t3);
++}
++
++struct Bar;
++
++impl Bar {
++    fn bar<T: Debug>(&self, t: T) {
++        println!("{:?}", t);
++    }
++}
++
++fn bad() {
++    foo(());
++    foo(());
++    foo(());
++    foo(());
++    foo3((), 2, 2);
++    let b = Bar;
++    b.bar(());
++}
++
++fn ok() {
++    foo(());
++    foo(1);
++    foo({ 1 });
++    foo3("a", 3, vec![3]);
++    let b = Bar;
++    b.bar({ 1 });
++    b.bar(());
++    question_mark();
++}
++
++fn question_mark() -> Result<(), ()> {
++    Ok(Ok(())?)?;
++    Ok(Ok(()))??;
++    Ok(())
++}
++
++#[allow(dead_code)]
++mod issue_2945 {
++    fn unit_fn() -> Result<(), i32> {
++        Ok(())
++    }
++
++    fn fallible() -> Result<(), i32> {
++        Ok(unit_fn()?)
++    }
++}
++
++fn main() {
++    bad();
++    ok();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d90c49f79de623de06b623664d540e3f2498d4d0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,71 @@@
++// run-rustfix
++#![warn(clippy::unit_arg)]
++#![allow(unused_braces, clippy::no_effect, unused_must_use)]
++
++use std::fmt::Debug;
++
++fn foo<T: Debug>(t: T) {
++    println!("{:?}", t);
++}
++
++fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
++    println!("{:?}, {:?}, {:?}", t1, t2, t3);
++}
++
++struct Bar;
++
++impl Bar {
++    fn bar<T: Debug>(&self, t: T) {
++        println!("{:?}", t);
++    }
++}
++
++fn bad() {
++    foo({});
++    foo({
++        1;
++    });
++    foo(foo(1));
++    foo({
++        foo(1);
++        foo(2);
++    });
++    foo3({}, 2, 2);
++    let b = Bar;
++    b.bar({
++        1;
++    });
++}
++
++fn ok() {
++    foo(());
++    foo(1);
++    foo({ 1 });
++    foo3("a", 3, vec![3]);
++    let b = Bar;
++    b.bar({ 1 });
++    b.bar(());
++    question_mark();
++}
++
++fn question_mark() -> Result<(), ()> {
++    Ok(Ok(())?)?;
++    Ok(Ok(()))??;
++    Ok(())
++}
++
++#[allow(dead_code)]
++mod issue_2945 {
++    fn unit_fn() -> Result<(), i32> {
++        Ok(())
++    }
++
++    fn fallible() -> Result<(), i32> {
++        Ok(unit_fn()?)
++    }
++}
++
++fn main() {
++    bad();
++    ok();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..21ccc684ea9de9e30944abba009a9ce6958f4e5a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,79 @@@
++error: passing a unit value to a function
++  --> $DIR/unit_arg.rs:24:9
++   |
++LL |     foo({});
++   |         ^^
++   |
++   = note: `-D clippy::unit-arg` implied by `-D warnings`
++help: if you intended to pass a unit value, use a unit literal instead
++   |
++LL |     foo(());
++   |         ^^
++
++error: passing a unit value to a function
++  --> $DIR/unit_arg.rs:25:9
++   |
++LL |       foo({
++   |  _________^
++LL | |         1;
++LL | |     });
++   | |_____^
++   |
++help: if you intended to pass a unit value, use a unit literal instead
++   |
++LL |     foo(());
++   |         ^^
++
++error: passing a unit value to a function
++  --> $DIR/unit_arg.rs:28:9
++   |
++LL |     foo(foo(1));
++   |         ^^^^^^
++   |
++help: if you intended to pass a unit value, use a unit literal instead
++   |
++LL |     foo(());
++   |         ^^
++
++error: passing a unit value to a function
++  --> $DIR/unit_arg.rs:29:9
++   |
++LL |       foo({
++   |  _________^
++LL | |         foo(1);
++LL | |         foo(2);
++LL | |     });
++   | |_____^
++   |
++help: if you intended to pass a unit value, use a unit literal instead
++   |
++LL |     foo(());
++   |         ^^
++
++error: passing a unit value to a function
++  --> $DIR/unit_arg.rs:33:10
++   |
++LL |     foo3({}, 2, 2);
++   |          ^^
++   |
++help: if you intended to pass a unit value, use a unit literal instead
++   |
++LL |     foo3((), 2, 2);
++   |          ^^
++
++error: passing a unit value to a function
++  --> $DIR/unit_arg.rs:35:11
++   |
++LL |       b.bar({
++   |  ___________^
++LL | |         1;
++LL | |     });
++   | |_____^
++   |
++help: if you intended to pass a unit value, use a unit literal instead
++   |
++LL |     b.bar(());
++   |           ^^
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8d3a4eed82e3e6a41bf220dad7e46f3d59a4f7f1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,57 @@@
++#![warn(clippy::unit_cmp)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation)]
++
++#[derive(PartialEq)]
++pub struct ContainsUnit(()); // should be fine
++
++fn main() {
++    // this is fine
++    if true == false {}
++
++    // this warns
++    if {
++        true;
++    } == {
++        false;
++    } {}
++
++    if {
++        true;
++    } > {
++        false;
++    } {}
++
++    assert_eq!(
++        {
++            true;
++        },
++        {
++            false;
++        }
++    );
++    debug_assert_eq!(
++        {
++            true;
++        },
++        {
++            false;
++        }
++    );
++
++    assert_ne!(
++        {
++            true;
++        },
++        {
++            false;
++        }
++    );
++    debug_assert_ne!(
++        {
++            true;
++        },
++        {
++            false;
++        }
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c8c0a85dfc102d9b2cb4c25e03803a7c8ec260c6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,82 @@@
++error: ==-comparison of unit values detected. This will always be true
++  --> $DIR/unit_cmp.rs:12:8
++   |
++LL |       if {
++   |  ________^
++LL | |         true;
++LL | |     } == {
++LL | |         false;
++LL | |     } {}
++   | |_____^
++   |
++   = note: `-D clippy::unit-cmp` implied by `-D warnings`
++
++error: >-comparison of unit values detected. This will always be false
++  --> $DIR/unit_cmp.rs:18:8
++   |
++LL |       if {
++   |  ________^
++LL | |         true;
++LL | |     } > {
++LL | |         false;
++LL | |     } {}
++   | |_____^
++
++error: `assert_eq` of unit values detected. This will always succeed
++  --> $DIR/unit_cmp.rs:24:5
++   |
++LL | /     assert_eq!(
++LL | |         {
++LL | |             true;
++LL | |         },
++...  |
++LL | |         }
++LL | |     );
++   | |______^
++   |
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `debug_assert_eq` of unit values detected. This will always succeed
++  --> $DIR/unit_cmp.rs:32:5
++   |
++LL | /     debug_assert_eq!(
++LL | |         {
++LL | |             true;
++LL | |         },
++...  |
++LL | |         }
++LL | |     );
++   | |______^
++   |
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `assert_ne` of unit values detected. This will always fail
++  --> $DIR/unit_cmp.rs:41:5
++   |
++LL | /     assert_ne!(
++LL | |         {
++LL | |             true;
++LL | |         },
++...  |
++LL | |         }
++LL | |     );
++   | |______^
++   |
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `debug_assert_ne` of unit values detected. This will always fail
++  --> $DIR/unit_cmp.rs:49:5
++   |
++LL | /     debug_assert_ne!(
++LL | |         {
++LL | |             true;
++LL | |         },
++...  |
++LL | |         }
++LL | |     );
++   | |______^
++   |
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e993e63f8ed86534d2b2af848c51e51c0be10481
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++#[clippy::unknown]
++#[clippy::cognitive_complexity = "1"]
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..47e37aed2464e4a5567ddb5ec62d16b2f67d8f50
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++error: Usage of unknown attribute
++  --> $DIR/unknown_attribute.rs:1:11
++   |
++LL | #[clippy::unknown]
++   |           ^^^^^^^
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4249ff8a958d1fb70b966a7ac3fe466408655ee5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++// run-rustfix
++
++#![warn(clippy::pedantic)]
++// Should suggest lowercase
++#![allow(clippy::all)]
++#![warn(clippy::cmp_nan)]
++
++// Should suggest similar clippy lint name
++#[warn(clippy::if_not_else)]
++#[warn(clippy::unnecessary_cast)]
++#[warn(clippy::useless_transmute)]
++// Shouldn't suggest rustc lint name(`dead_code`)
++#[warn(clippy::drop_copy)]
++// Shouldn't suggest removed/deprecated clippy lint name(`unused_collect`)
++#[warn(clippy::unused_self)]
++// Shouldn't suggest renamed clippy lint name(`const_static_lifetime`)
++#[warn(clippy::redundant_static_lifetimes)]
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5db345f5444137fecca77f3486524310bcc33cb7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++// run-rustfix
++
++#![warn(clippy::pedantic)]
++// Should suggest lowercase
++#![allow(clippy::All)]
++#![warn(clippy::CMP_NAN)]
++
++// Should suggest similar clippy lint name
++#[warn(clippy::if_not_els)]
++#[warn(clippy::UNNecsaRy_cAst)]
++#[warn(clippy::useles_transute)]
++// Shouldn't suggest rustc lint name(`dead_code`)
++#[warn(clippy::dead_cod)]
++// Shouldn't suggest removed/deprecated clippy lint name(`unused_collect`)
++#[warn(clippy::unused_colle)]
++// Shouldn't suggest renamed clippy lint name(`const_static_lifetime`)
++#[warn(clippy::const_static_lifetim)]
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1b859043bb53b8a493a1f98a6b3a87a6b0c069df
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++error: unknown clippy lint: clippy::if_not_els
++  --> $DIR/unknown_clippy_lints.rs:9:8
++   |
++LL | #[warn(clippy::if_not_els)]
++   |        ^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::if_not_else`
++   |
++   = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings`
++
++error: unknown clippy lint: clippy::UNNecsaRy_cAst
++  --> $DIR/unknown_clippy_lints.rs:10:8
++   |
++LL | #[warn(clippy::UNNecsaRy_cAst)]
++   |        ^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::unnecessary_cast`
++
++error: unknown clippy lint: clippy::useles_transute
++  --> $DIR/unknown_clippy_lints.rs:11:8
++   |
++LL | #[warn(clippy::useles_transute)]
++   |        ^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::useless_transmute`
++
++error: unknown clippy lint: clippy::dead_cod
++  --> $DIR/unknown_clippy_lints.rs:13:8
++   |
++LL | #[warn(clippy::dead_cod)]
++   |        ^^^^^^^^^^^^^^^^ help: did you mean: `clippy::drop_copy`
++
++error: unknown clippy lint: clippy::unused_colle
++  --> $DIR/unknown_clippy_lints.rs:15:8
++   |
++LL | #[warn(clippy::unused_colle)]
++   |        ^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::unused_self`
++
++error: unknown clippy lint: clippy::const_static_lifetim
++  --> $DIR/unknown_clippy_lints.rs:17:8
++   |
++LL | #[warn(clippy::const_static_lifetim)]
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::redundant_static_lifetimes`
++
++error: unknown clippy lint: clippy::All
++  --> $DIR/unknown_clippy_lints.rs:5:10
++   |
++LL | #![allow(clippy::All)]
++   |          ^^^^^^^^^^^ help: lowercase the lint name: `clippy::all`
++
++error: unknown clippy lint: clippy::CMP_NAN
++  --> $DIR/unknown_clippy_lints.rs:6:9
++   |
++LL | #![warn(clippy::CMP_NAN)]
++   |         ^^^^^^^^^^^^^^^ help: lowercase the lint name: `clippy::cmp_nan`
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..df9b227eeb3f55ad2cc6acb1f69c3ad040d9b11b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++#![warn(clippy::unnecessary_cast)]
++#![allow(clippy::no_effect)]
++
++fn main() {
++    // Test cast_unnecessary
++    1i32 as i32;
++    1f32 as f32;
++    false as bool;
++    &1i32 as &i32;
++
++    // macro version
++    macro_rules! foo {
++        ($a:ident, $b:ident) => {
++            #[allow(unused)]
++            pub fn $a() -> $b {
++                1 as $b
++            }
++        };
++    }
++    foo!(a, i32);
++    foo!(b, f32);
++    foo!(c, f64);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8981d13e8eabb61c1e2888f237af5016c8e9ccbe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: casting to the same type is unnecessary (`i32` -> `i32`)
++  --> $DIR/unnecessary_cast.rs:6:5
++   |
++LL |     1i32 as i32;
++   |     ^^^^^^^^^^^
++   |
++   = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
++
++error: casting to the same type is unnecessary (`f32` -> `f32`)
++  --> $DIR/unnecessary_cast.rs:7:5
++   |
++LL |     1f32 as f32;
++   |     ^^^^^^^^^^^
++
++error: casting to the same type is unnecessary (`bool` -> `bool`)
++  --> $DIR/unnecessary_cast.rs:8:5
++   |
++LL |     false as bool;
++   |     ^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fb89a9fce3d5bf91a9ced4dbbd7d5014533c26b1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++// run-rustfix
++
++#![warn(clippy::unnecessary_cast)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation)]
++
++fn main() {
++    // casting integer literal to float is unnecessary
++    100_f32;
++    100_f64;
++    100_f64;
++    // Should not trigger
++    #[rustfmt::skip]
++    let v = vec!(1);
++    &v as &[i32];
++    1.0 as f64;
++    1 as u64;
++    0x10 as f32;
++    0o10 as f32;
++    0b10 as f32;
++    0x11 as f64;
++    0o11 as f64;
++    0b11 as f64;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4a0c8620dc134acfd26c35a535c126653c3ac998
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++// run-rustfix
++
++#![warn(clippy::unnecessary_cast)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation)]
++
++fn main() {
++    // casting integer literal to float is unnecessary
++    100 as f32;
++    100 as f64;
++    100_i32 as f64;
++    // Should not trigger
++    #[rustfmt::skip]
++    let v = vec!(1);
++    &v as &[i32];
++    1.0 as f64;
++    1 as u64;
++    0x10 as f32;
++    0o10 as f32;
++    0b10 as f32;
++    0x11 as f64;
++    0o11 as f64;
++    0b11 as f64;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8ff1e5dea600363fd583099b9e3b51b2aca7facf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: casting integer literal to `f32` is unnecessary
++  --> $DIR/unnecessary_cast_fixable.rs:8:5
++   |
++LL |     100 as f32;
++   |     ^^^^^^^^^^ help: try: `100_f32`
++   |
++   = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
++
++error: casting integer literal to `f64` is unnecessary
++  --> $DIR/unnecessary_cast_fixable.rs:9:5
++   |
++LL |     100 as f64;
++   |     ^^^^^^^^^^ help: try: `100_f64`
++
++error: casting integer literal to `f64` is unnecessary
++  --> $DIR/unnecessary_cast_fixable.rs:10:5
++   |
++LL |     100_i32 as f64;
++   |     ^^^^^^^^^^^^^^ help: try: `100_f64`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f1cc5b564c1dca66329e21bf9b3c02cc22c7ce4d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,117 @@@
++// does not test any rustfixable lints
++
++#![warn(clippy::clone_on_ref_ptr)]
++#![allow(unused, clippy::redundant_clone)]
++
++use std::cell::RefCell;
++use std::rc::{self, Rc};
++use std::sync::{self, Arc};
++
++trait SomeTrait {}
++struct SomeImpl;
++impl SomeTrait for SomeImpl {}
++
++fn main() {}
++
++fn is_ascii(ch: char) -> bool {
++    ch.is_ascii()
++}
++
++fn clone_on_copy() {
++    42.clone();
++
++    vec![1].clone(); // ok, not a Copy type
++    Some(vec![1]).clone(); // ok, not a Copy type
++    (&42).clone();
++
++    let rc = RefCell::new(0);
++    rc.borrow().clone();
++
++    // Issue #4348
++    let mut x = 43;
++    let _ = &x.clone(); // ok, getting a ref
++    'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate
++    is_ascii('z'.clone());
++
++    // Issue #5436
++    let mut vec = Vec::new();
++    vec.push(42.clone());
++}
++
++fn clone_on_ref_ptr() {
++    let rc = Rc::new(true);
++    let arc = Arc::new(true);
++
++    let rcweak = Rc::downgrade(&rc);
++    let arc_weak = Arc::downgrade(&arc);
++
++    rc.clone();
++    Rc::clone(&rc);
++
++    arc.clone();
++    Arc::clone(&arc);
++
++    rcweak.clone();
++    rc::Weak::clone(&rcweak);
++
++    arc_weak.clone();
++    sync::Weak::clone(&arc_weak);
++
++    let x = Arc::new(SomeImpl);
++    let _: Arc<dyn SomeTrait> = x.clone();
++}
++
++fn clone_on_copy_generic<T: Copy>(t: T) {
++    t.clone();
++
++    Some(t).clone();
++}
++
++fn clone_on_double_ref() {
++    let x = vec![1];
++    let y = &&x;
++    let z: &Vec<_> = y.clone();
++
++    println!("{:p} {:p}", *y, z);
++}
++
++mod many_derefs {
++    struct A;
++    struct B;
++    struct C;
++    struct D;
++    #[derive(Copy, Clone)]
++    struct E;
++
++    macro_rules! impl_deref {
++        ($src:ident, $dst:ident) => {
++            impl std::ops::Deref for $src {
++                type Target = $dst;
++                fn deref(&self) -> &Self::Target {
++                    &$dst
++                }
++            }
++        };
++    }
++
++    impl_deref!(A, B);
++    impl_deref!(B, C);
++    impl_deref!(C, D);
++    impl std::ops::Deref for D {
++        type Target = &'static E;
++        fn deref(&self) -> &Self::Target {
++            &&E
++        }
++    }
++
++    fn go1() {
++        let a = A;
++        let _: E = a.clone();
++        let _: E = *****a;
++    }
++
++    fn check(mut encoded: &[u8]) {
++        let _ = &mut encoded.clone();
++        let _ = &encoded.clone();
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6176a2bc4647987a44a5a60c0197dd50c83c2e2d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,130 @@@
++error: using `clone` on a `Copy` type
++  --> $DIR/unnecessary_clone.rs:21:5
++   |
++LL |     42.clone();
++   |     ^^^^^^^^^^ help: try removing the `clone` call: `42`
++   |
++   = note: `-D clippy::clone-on-copy` implied by `-D warnings`
++
++error: using `clone` on a `Copy` type
++  --> $DIR/unnecessary_clone.rs:25:5
++   |
++LL |     (&42).clone();
++   |     ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)`
++
++error: using `clone` on a `Copy` type
++  --> $DIR/unnecessary_clone.rs:28:5
++   |
++LL |     rc.borrow().clone();
++   |     ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()`
++
++error: using `clone` on a `Copy` type
++  --> $DIR/unnecessary_clone.rs:34:14
++   |
++LL |     is_ascii('z'.clone());
++   |              ^^^^^^^^^^^ help: try removing the `clone` call: `'z'`
++
++error: using `clone` on a `Copy` type
++  --> $DIR/unnecessary_clone.rs:38:14
++   |
++LL |     vec.push(42.clone());
++   |              ^^^^^^^^^^ help: try removing the `clone` call: `42`
++
++error: using `.clone()` on a ref-counted pointer
++  --> $DIR/unnecessary_clone.rs:48:5
++   |
++LL |     rc.clone();
++   |     ^^^^^^^^^^ help: try this: `Rc::<bool>::clone(&rc)`
++   |
++   = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings`
++
++error: using `.clone()` on a ref-counted pointer
++  --> $DIR/unnecessary_clone.rs:51:5
++   |
++LL |     arc.clone();
++   |     ^^^^^^^^^^^ help: try this: `Arc::<bool>::clone(&arc)`
++
++error: using `.clone()` on a ref-counted pointer
++  --> $DIR/unnecessary_clone.rs:54:5
++   |
++LL |     rcweak.clone();
++   |     ^^^^^^^^^^^^^^ help: try this: `Weak::<bool>::clone(&rcweak)`
++
++error: using `.clone()` on a ref-counted pointer
++  --> $DIR/unnecessary_clone.rs:57:5
++   |
++LL |     arc_weak.clone();
++   |     ^^^^^^^^^^^^^^^^ help: try this: `Weak::<bool>::clone(&arc_weak)`
++
++error: using `.clone()` on a ref-counted pointer
++  --> $DIR/unnecessary_clone.rs:61:33
++   |
++LL |     let _: Arc<dyn SomeTrait> = x.clone();
++   |                                 ^^^^^^^^^ help: try this: `Arc::<SomeImpl>::clone(&x)`
++
++error: using `clone` on a `Copy` type
++  --> $DIR/unnecessary_clone.rs:65:5
++   |
++LL |     t.clone();
++   |     ^^^^^^^^^ help: try removing the `clone` call: `t`
++
++error: using `clone` on a `Copy` type
++  --> $DIR/unnecessary_clone.rs:67:5
++   |
++LL |     Some(t).clone();
++   |     ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)`
++
++error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type
++  --> $DIR/unnecessary_clone.rs:73:22
++   |
++LL |     let z: &Vec<_> = y.clone();
++   |                      ^^^^^^^^^
++   |
++   = note: `#[deny(clippy::clone_double_ref)]` on by default
++help: try dereferencing it
++   |
++LL |     let z: &Vec<_> = &(*y).clone();
++   |                      ^^^^^^^^^^^^^
++help: or try being explicit if you are sure, that you want to clone a reference
++   |
++LL |     let z: &Vec<_> = <&std::vec::Vec<i32>>::clone(y);
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: using `clone` on a `Copy` type
++  --> $DIR/unnecessary_clone.rs:109:20
++   |
++LL |         let _: E = a.clone();
++   |                    ^^^^^^^^^ help: try dereferencing it: `*****a`
++
++error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type
++  --> $DIR/unnecessary_clone.rs:114:22
++   |
++LL |         let _ = &mut encoded.clone();
++   |                      ^^^^^^^^^^^^^^^
++   |
++help: try dereferencing it
++   |
++LL |         let _ = &mut &(*encoded).clone();
++   |                      ^^^^^^^^^^^^^^^^^^^
++help: or try being explicit if you are sure, that you want to clone a reference
++   |
++LL |         let _ = &mut <&[u8]>::clone(encoded);
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^
++
++error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type
++  --> $DIR/unnecessary_clone.rs:115:18
++   |
++LL |         let _ = &encoded.clone();
++   |                  ^^^^^^^^^^^^^^^
++   |
++help: try dereferencing it
++   |
++LL |         let _ = &&(*encoded).clone();
++   |                  ^^^^^^^^^^^^^^^^^^^
++help: or try being explicit if you are sure, that you want to clone a reference
++   |
++LL |         let _ = &<&[u8]>::clone(encoded);
++   |                  ^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 16 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..af858e4abcf5595f350a87d183bfbee5c4b6ae7d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++fn main() {
++    let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None });
++    let _ = (0..4).filter_map(|x| {
++        if x > 1 {
++            return Some(x);
++        };
++        None
++    });
++    let _ = (0..4).filter_map(|x| match x {
++        0 | 1 => None,
++        _ => Some(x),
++    });
++
++    let _ = (0..4).filter_map(|x| Some(x + 1));
++
++    let _ = (0..4).filter_map(i32::checked_abs);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..041829c3c7855befffd745d298cfd22b07577648
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++error: this `.filter_map` can be written more simply using `.filter`
++  --> $DIR/unnecessary_filter_map.rs:2:13
++   |
++LL |     let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None });
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::unnecessary-filter-map` implied by `-D warnings`
++
++error: this `.filter_map` can be written more simply using `.filter`
++  --> $DIR/unnecessary_filter_map.rs:3:13
++   |
++LL |       let _ = (0..4).filter_map(|x| {
++   |  _____________^
++LL | |         if x > 1 {
++LL | |             return Some(x);
++LL | |         };
++LL | |         None
++LL | |     });
++   | |______^
++
++error: this `.filter_map` can be written more simply using `.filter`
++  --> $DIR/unnecessary_filter_map.rs:9:13
++   |
++LL |       let _ = (0..4).filter_map(|x| match x {
++   |  _____________^
++LL | |         0 | 1 => None,
++LL | |         _ => Some(x),
++LL | |     });
++   | |______^
++
++error: this `.filter_map` can be written more simply using `.map`
++  --> $DIR/unnecessary_filter_map.rs:14:13
++   |
++LL |     let _ = (0..4).filter_map(|x| Some(x + 1));
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dfe3bd47e1394b4517cdbc88ee1b288a86a0c036
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++// run-rustfix
++
++#![allow(unused_imports)]
++#![warn(clippy::flat_map_identity)]
++
++use std::convert;
++
++fn main() {
++    let iterator = [[0, 1], [2, 3], [4, 5]].iter();
++    let _ = iterator.flatten();
++
++    let iterator = [[0, 1], [2, 3], [4, 5]].iter();
++    let _ = iterator.flatten();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..393b95692554ca200ab9e5ac37f998e350f37d99
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++// run-rustfix
++
++#![allow(unused_imports)]
++#![warn(clippy::flat_map_identity)]
++
++use std::convert;
++
++fn main() {
++    let iterator = [[0, 1], [2, 3], [4, 5]].iter();
++    let _ = iterator.flat_map(|x| x);
++
++    let iterator = [[0, 1], [2, 3], [4, 5]].iter();
++    let _ = iterator.flat_map(convert::identity);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a1cd5745e494991ca6cc70dc7cb0f9a3f96f9d96
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: called `flat_map(|x| x)` on an `Iterator`
++  --> $DIR/unnecessary_flat_map.rs:10:22
++   |
++LL |     let _ = iterator.flat_map(|x| x);
++   |                      ^^^^^^^^^^^^^^^ help: try: `flatten()`
++   |
++   = note: `-D clippy::flat-map-identity` implied by `-D warnings`
++
++error: called `flat_map(std::convert::identity)` on an `Iterator`
++  --> $DIR/unnecessary_flat_map.rs:13:22
++   |
++LL |     let _ = iterator.flat_map(convert::identity);
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..52300a3b64061b172a27580789dd5ae516eebfe7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++// run-rustfix
++
++#![allow(dead_code)]
++
++/// Calls which should trigger the `UNNECESSARY_FOLD` lint
++fn unnecessary_fold() {
++    // Can be replaced by .any
++    let _ = (0..3).any(|x| x > 2);
++    // Can be replaced by .all
++    let _ = (0..3).all(|x| x > 2);
++    // Can be replaced by .sum
++    let _: i32 = (0..3).sum();
++    // Can be replaced by .product
++    let _: i32 = (0..3).product();
++}
++
++/// Should trigger the `UNNECESSARY_FOLD` lint, with an error span including exactly `.fold(...)`
++fn unnecessary_fold_span_for_multi_element_chain() {
++    let _: bool = (0..3).map(|x| 2 * x).any(|x| x > 2);
++}
++
++/// Calls which should not trigger the `UNNECESSARY_FOLD` lint
++fn unnecessary_fold_should_ignore() {
++    let _ = (0..3).fold(true, |acc, x| acc || x > 2);
++    let _ = (0..3).fold(false, |acc, x| acc && x > 2);
++    let _ = (0..3).fold(1, |acc, x| acc + x);
++    let _ = (0..3).fold(0, |acc, x| acc * x);
++    let _ = (0..3).fold(0, |acc, x| 1 + acc + x);
++
++    // We only match against an accumulator on the left
++    // hand side. We could lint for .sum and .product when
++    // it's on the right, but don't for now (and this wouldn't
++    // be valid if we extended the lint to cover arbitrary numeric
++    // types).
++    let _ = (0..3).fold(false, |acc, x| x > 2 || acc);
++    let _ = (0..3).fold(true, |acc, x| x > 2 && acc);
++    let _ = (0..3).fold(0, |acc, x| x + acc);
++    let _ = (0..3).fold(1, |acc, x| x * acc);
++
++    let _ = [(0..2), (0..3)].iter().fold(0, |a, b| a + b.len());
++    let _ = [(0..2), (0..3)].iter().fold(1, |a, b| a * b.len());
++}
++
++/// Should lint only the line containing the fold
++fn unnecessary_fold_over_multiple_lines() {
++    let _ = (0..3)
++        .map(|x| x + 1)
++        .filter(|x| x % 2 == 0)
++        .any(|x| x > 2);
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4028d80c0a3cb9efc58f60cde0095ee7151e805a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++// run-rustfix
++
++#![allow(dead_code)]
++
++/// Calls which should trigger the `UNNECESSARY_FOLD` lint
++fn unnecessary_fold() {
++    // Can be replaced by .any
++    let _ = (0..3).fold(false, |acc, x| acc || x > 2);
++    // Can be replaced by .all
++    let _ = (0..3).fold(true, |acc, x| acc && x > 2);
++    // Can be replaced by .sum
++    let _: i32 = (0..3).fold(0, |acc, x| acc + x);
++    // Can be replaced by .product
++    let _: i32 = (0..3).fold(1, |acc, x| acc * x);
++}
++
++/// Should trigger the `UNNECESSARY_FOLD` lint, with an error span including exactly `.fold(...)`
++fn unnecessary_fold_span_for_multi_element_chain() {
++    let _: bool = (0..3).map(|x| 2 * x).fold(false, |acc, x| acc || x > 2);
++}
++
++/// Calls which should not trigger the `UNNECESSARY_FOLD` lint
++fn unnecessary_fold_should_ignore() {
++    let _ = (0..3).fold(true, |acc, x| acc || x > 2);
++    let _ = (0..3).fold(false, |acc, x| acc && x > 2);
++    let _ = (0..3).fold(1, |acc, x| acc + x);
++    let _ = (0..3).fold(0, |acc, x| acc * x);
++    let _ = (0..3).fold(0, |acc, x| 1 + acc + x);
++
++    // We only match against an accumulator on the left
++    // hand side. We could lint for .sum and .product when
++    // it's on the right, but don't for now (and this wouldn't
++    // be valid if we extended the lint to cover arbitrary numeric
++    // types).
++    let _ = (0..3).fold(false, |acc, x| x > 2 || acc);
++    let _ = (0..3).fold(true, |acc, x| x > 2 && acc);
++    let _ = (0..3).fold(0, |acc, x| x + acc);
++    let _ = (0..3).fold(1, |acc, x| x * acc);
++
++    let _ = [(0..2), (0..3)].iter().fold(0, |a, b| a + b.len());
++    let _ = [(0..2), (0..3)].iter().fold(1, |a, b| a * b.len());
++}
++
++/// Should lint only the line containing the fold
++fn unnecessary_fold_over_multiple_lines() {
++    let _ = (0..3)
++        .map(|x| x + 1)
++        .filter(|x| x % 2 == 0)
++        .fold(false, |acc, x| acc || x > 2);
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..22c44588ab7af879b9a7b27d828144c55a9f59e8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: this `.fold` can be written more succinctly using another method
++  --> $DIR/unnecessary_fold.rs:8:20
++   |
++LL |     let _ = (0..3).fold(false, |acc, x| acc || x > 2);
++   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)`
++   |
++   = note: `-D clippy::unnecessary-fold` implied by `-D warnings`
++
++error: this `.fold` can be written more succinctly using another method
++  --> $DIR/unnecessary_fold.rs:10:20
++   |
++LL |     let _ = (0..3).fold(true, |acc, x| acc && x > 2);
++   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `all(|x| x > 2)`
++
++error: this `.fold` can be written more succinctly using another method
++  --> $DIR/unnecessary_fold.rs:12:25
++   |
++LL |     let _: i32 = (0..3).fold(0, |acc, x| acc + x);
++   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `sum()`
++
++error: this `.fold` can be written more succinctly using another method
++  --> $DIR/unnecessary_fold.rs:14:25
++   |
++LL |     let _: i32 = (0..3).fold(1, |acc, x| acc * x);
++   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `product()`
++
++error: this `.fold` can be written more succinctly using another method
++  --> $DIR/unnecessary_fold.rs:19:41
++   |
++LL |     let _: bool = (0..3).map(|x| 2 * x).fold(false, |acc, x| acc || x > 2);
++   |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)`
++
++error: this `.fold` can be written more succinctly using another method
++  --> $DIR/unnecessary_fold.rs:49:10
++   |
++LL |         .fold(false, |acc, x| acc || x > 2);
++   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)`
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2fca96c4cd556654db75f3f0b58ab24c0c9d028a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,79 @@@
++// run-rustfix
++
++#![feature(box_syntax)]
++#![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)]
++#![warn(clippy::unnecessary_operation)]
++
++struct Tuple(i32);
++struct Struct {
++    field: i32,
++}
++enum Enum {
++    Tuple(i32),
++    Struct { field: i32 },
++}
++struct DropStruct {
++    field: i32,
++}
++impl Drop for DropStruct {
++    fn drop(&mut self) {}
++}
++struct DropTuple(i32);
++impl Drop for DropTuple {
++    fn drop(&mut self) {}
++}
++enum DropEnum {
++    Tuple(i32),
++    Struct { field: i32 },
++}
++impl Drop for DropEnum {
++    fn drop(&mut self) {}
++}
++struct FooString {
++    s: String,
++}
++
++fn get_number() -> i32 {
++    0
++}
++
++fn get_usize() -> usize {
++    0
++}
++fn get_struct() -> Struct {
++    Struct { field: 0 }
++}
++fn get_drop_struct() -> DropStruct {
++    DropStruct { field: 0 }
++}
++
++fn main() {
++    get_number();
++    get_number();
++    get_struct();
++    get_number();
++    get_number();
++    5;get_number();
++    get_number();
++    get_number();
++    5;6;get_number();
++    get_number();
++    get_number();
++    get_number();
++    5;get_number();
++    42;get_number();
++    [42, 55];get_usize();
++    42;get_number();
++    get_number();
++    [42; 55];get_usize();
++    get_number();
++    String::from("blah");
++
++    // Do not warn
++    DropTuple(get_number());
++    DropStruct { field: get_number() };
++    DropStruct { field: get_number() };
++    DropStruct { ..get_drop_struct() };
++    DropEnum::Tuple(get_number());
++    DropEnum::Struct { field: get_number() };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..08cb9ab522ee0d7f22ea647908102b06b73b36a7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,83 @@@
++// run-rustfix
++
++#![feature(box_syntax)]
++#![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)]
++#![warn(clippy::unnecessary_operation)]
++
++struct Tuple(i32);
++struct Struct {
++    field: i32,
++}
++enum Enum {
++    Tuple(i32),
++    Struct { field: i32 },
++}
++struct DropStruct {
++    field: i32,
++}
++impl Drop for DropStruct {
++    fn drop(&mut self) {}
++}
++struct DropTuple(i32);
++impl Drop for DropTuple {
++    fn drop(&mut self) {}
++}
++enum DropEnum {
++    Tuple(i32),
++    Struct { field: i32 },
++}
++impl Drop for DropEnum {
++    fn drop(&mut self) {}
++}
++struct FooString {
++    s: String,
++}
++
++fn get_number() -> i32 {
++    0
++}
++
++fn get_usize() -> usize {
++    0
++}
++fn get_struct() -> Struct {
++    Struct { field: 0 }
++}
++fn get_drop_struct() -> DropStruct {
++    DropStruct { field: 0 }
++}
++
++fn main() {
++    Tuple(get_number());
++    Struct { field: get_number() };
++    Struct { ..get_struct() };
++    Enum::Tuple(get_number());
++    Enum::Struct { field: get_number() };
++    5 + get_number();
++    *&get_number();
++    &get_number();
++    (5, 6, get_number());
++    box get_number();
++    get_number()..;
++    ..get_number();
++    5..get_number();
++    [42, get_number()];
++    [42, 55][get_usize()];
++    (42, get_number()).1;
++    [get_number(); 55];
++    [42; 55][get_usize()];
++    {
++        get_number()
++    };
++    FooString {
++        s: String::from("blah"),
++    };
++
++    // Do not warn
++    DropTuple(get_number());
++    DropStruct { field: get_number() };
++    DropStruct { field: get_number() };
++    DropStruct { ..get_drop_struct() };
++    DropEnum::Tuple(get_number());
++    DropEnum::Struct { field: get_number() };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f88c9f9908beabdf4c2bef4afaebf708ee533059
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,128 @@@
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:51:5
++   |
++LL |     Tuple(get_number());
++   |     ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++   |
++   = note: `-D clippy::unnecessary-operation` implied by `-D warnings`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:52:5
++   |
++LL |     Struct { field: get_number() };
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:53:5
++   |
++LL |     Struct { ..get_struct() };
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_struct();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:54:5
++   |
++LL |     Enum::Tuple(get_number());
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:55:5
++   |
++LL |     Enum::Struct { field: get_number() };
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:56:5
++   |
++LL |     5 + get_number();
++   |     ^^^^^^^^^^^^^^^^^ help: replace it with: `5;get_number();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:57:5
++   |
++LL |     *&get_number();
++   |     ^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:58:5
++   |
++LL |     &get_number();
++   |     ^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:59:5
++   |
++LL |     (5, 6, get_number());
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `5;6;get_number();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:60:5
++   |
++LL |     box get_number();
++   |     ^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:61:5
++   |
++LL |     get_number()..;
++   |     ^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:62:5
++   |
++LL |     ..get_number();
++   |     ^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:63:5
++   |
++LL |     5..get_number();
++   |     ^^^^^^^^^^^^^^^^ help: replace it with: `5;get_number();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:64:5
++   |
++LL |     [42, get_number()];
++   |     ^^^^^^^^^^^^^^^^^^^ help: replace it with: `42;get_number();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:65:5
++   |
++LL |     [42, 55][get_usize()];
++   |     ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `[42, 55];get_usize();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:66:5
++   |
++LL |     (42, get_number()).1;
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `42;get_number();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:67:5
++   |
++LL |     [get_number(); 55];
++   |     ^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:68:5
++   |
++LL |     [42; 55][get_usize()];
++   |     ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `[42; 55];get_usize();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:69:5
++   |
++LL | /     {
++LL | |         get_number()
++LL | |     };
++   | |______^ help: replace it with: `get_number();`
++
++error: statement can be reduced
++  --> $DIR/unnecessary_operation.rs:72:5
++   |
++LL | /     FooString {
++LL | |         s: String::from("blah"),
++LL | |     };
++   | |______^ help: replace it with: `String::from("blah");`
++
++error: aborting due to 20 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f7b94118d4e86690221be3c274330f8907d109e0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++// run-rustfix
++
++#![feature(stmt_expr_attributes)]
++#![allow(unused_variables)]
++
++struct Outer {
++    inner: u32,
++}
++
++#[deny(clippy::ref_in_deref)]
++fn main() {
++    let outer = Outer { inner: 0 };
++    let inner = outer.inner;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4e585b9b96ba9461aaf082f014f5570177b11cce
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++// run-rustfix
++
++#![feature(stmt_expr_attributes)]
++#![allow(unused_variables)]
++
++struct Outer {
++    inner: u32,
++}
++
++#[deny(clippy::ref_in_deref)]
++fn main() {
++    let outer = Outer { inner: 0 };
++    let inner = (&outer).inner;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..34ba167a947906602a4267f2a3125906f653432a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++error: Creating a reference that is immediately dereferenced.
++  --> $DIR/unnecessary_ref.rs:13:17
++   |
++LL |     let inner = (&outer).inner;
++   |                 ^^^^^^^^ help: try this: `outer`
++   |
++note: the lint level is defined here
++  --> $DIR/unnecessary_ref.rs:10:8
++   |
++LL | #[deny(clippy::ref_in_deref)]
++   |        ^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to previous error
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fa639aa70d61d8574335e1a22399a65c641eb3fb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++#![warn(clippy::unneeded_field_pattern)]
++#[allow(dead_code, unused)]
++
++struct Foo {
++    a: i32,
++    b: i32,
++    c: i32,
++}
++
++fn main() {
++    let f = Foo { a: 0, b: 0, c: 0 };
++
++    match f {
++        Foo { a: _, b: 0, .. } => {},
++
++        Foo { a: _, b: _, c: _ } => {},
++    }
++    match f {
++        Foo { b: 0, .. } => {}, // should be OK
++        Foo { .. } => {},       // and the Force might be with this one
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e7b92ce1e197b3631cede7d5fb7090260f54f87f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++error: You matched a field with a wildcard pattern. Consider using `..` instead
++  --> $DIR/unneeded_field_pattern.rs:14:15
++   |
++LL |         Foo { a: _, b: 0, .. } => {},
++   |               ^^^^
++   |
++   = note: `-D clippy::unneeded-field-pattern` implied by `-D warnings`
++   = help: Try with `Foo { b: 0, .. }`
++
++error: All the struct fields are matched to a wildcard pattern, consider using `..`.
++  --> $DIR/unneeded_field_pattern.rs:16:9
++   |
++LL |         Foo { a: _, b: _, c: _ } => {},
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: Try with `Foo { .. }` instead
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..12c3461c9557982d9b12a523ac3048021c0c5cec
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++// run-rustfix
++#![feature(stmt_expr_attributes)]
++#![deny(clippy::unneeded_wildcard_pattern)]
++
++fn main() {
++    let t = (0, 1, 2, 3);
++
++    if let (0, ..) = t {};
++    if let (0, ..) = t {};
++    if let (.., 0) = t {};
++    if let (.., 0) = t {};
++    if let (0, ..) = t {};
++    if let (0, ..) = t {};
++    if let (_, 0, ..) = t {};
++    if let (.., 0, _) = t {};
++    if let (0, _, _, _) = t {};
++    if let (0, ..) = t {};
++    if let (.., 0) = t {};
++
++    #[rustfmt::skip]
++    {
++        if let (0, ..,) = t {};
++    }
++
++    struct S(usize, usize, usize, usize);
++
++    let s = S(0, 1, 2, 3);
++
++    if let S(0, ..) = s {};
++    if let S(0, ..) = s {};
++    if let S(.., 0) = s {};
++    if let S(.., 0) = s {};
++    if let S(0, ..) = s {};
++    if let S(0, ..) = s {};
++    if let S(_, 0, ..) = s {};
++    if let S(.., 0, _) = s {};
++    if let S(0, _, _, _) = s {};
++    if let S(0, ..) = s {};
++    if let S(.., 0) = s {};
++
++    #[rustfmt::skip]
++    {
++        if let S(0, ..,) = s {};
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4ac01d5d23b0476eb99664d227dd679a79e046c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++// run-rustfix
++#![feature(stmt_expr_attributes)]
++#![deny(clippy::unneeded_wildcard_pattern)]
++
++fn main() {
++    let t = (0, 1, 2, 3);
++
++    if let (0, .., _) = t {};
++    if let (0, _, ..) = t {};
++    if let (_, .., 0) = t {};
++    if let (.., _, 0) = t {};
++    if let (0, _, _, ..) = t {};
++    if let (0, .., _, _) = t {};
++    if let (_, 0, ..) = t {};
++    if let (.., 0, _) = t {};
++    if let (0, _, _, _) = t {};
++    if let (0, ..) = t {};
++    if let (.., 0) = t {};
++
++    #[rustfmt::skip]
++    {
++        if let (0, .., _, _,) = t {};
++    }
++
++    struct S(usize, usize, usize, usize);
++
++    let s = S(0, 1, 2, 3);
++
++    if let S(0, .., _) = s {};
++    if let S(0, _, ..) = s {};
++    if let S(_, .., 0) = s {};
++    if let S(.., _, 0) = s {};
++    if let S(0, _, _, ..) = s {};
++    if let S(0, .., _, _) = s {};
++    if let S(_, 0, ..) = s {};
++    if let S(.., 0, _) = s {};
++    if let S(0, _, _, _) = s {};
++    if let S(0, ..) = s {};
++    if let S(.., 0) = s {};
++
++    #[rustfmt::skip]
++    {
++        if let S(0, .., _, _,) = s {};
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..716d9ecff89af617464eb6c57d5ca7b1546eec26
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,92 @@@
++error: this pattern is unneeded as the `..` pattern can match that element
++  --> $DIR/unneeded_wildcard_pattern.rs:8:18
++   |
++LL |     if let (0, .., _) = t {};
++   |                  ^^^ help: remove it
++   |
++note: the lint level is defined here
++  --> $DIR/unneeded_wildcard_pattern.rs:3:9
++   |
++LL | #![deny(clippy::unneeded_wildcard_pattern)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: this pattern is unneeded as the `..` pattern can match that element
++  --> $DIR/unneeded_wildcard_pattern.rs:9:16
++   |
++LL |     if let (0, _, ..) = t {};
++   |                ^^^ help: remove it
++
++error: this pattern is unneeded as the `..` pattern can match that element
++  --> $DIR/unneeded_wildcard_pattern.rs:10:13
++   |
++LL |     if let (_, .., 0) = t {};
++   |             ^^^ help: remove it
++
++error: this pattern is unneeded as the `..` pattern can match that element
++  --> $DIR/unneeded_wildcard_pattern.rs:11:15
++   |
++LL |     if let (.., _, 0) = t {};
++   |               ^^^ help: remove it
++
++error: these patterns are unneeded as the `..` pattern can match those elements
++  --> $DIR/unneeded_wildcard_pattern.rs:12:16
++   |
++LL |     if let (0, _, _, ..) = t {};
++   |                ^^^^^^ help: remove them
++
++error: these patterns are unneeded as the `..` pattern can match those elements
++  --> $DIR/unneeded_wildcard_pattern.rs:13:18
++   |
++LL |     if let (0, .., _, _) = t {};
++   |                  ^^^^^^ help: remove them
++
++error: these patterns are unneeded as the `..` pattern can match those elements
++  --> $DIR/unneeded_wildcard_pattern.rs:22:22
++   |
++LL |         if let (0, .., _, _,) = t {};
++   |                      ^^^^^^ help: remove them
++
++error: this pattern is unneeded as the `..` pattern can match that element
++  --> $DIR/unneeded_wildcard_pattern.rs:29:19
++   |
++LL |     if let S(0, .., _) = s {};
++   |                   ^^^ help: remove it
++
++error: this pattern is unneeded as the `..` pattern can match that element
++  --> $DIR/unneeded_wildcard_pattern.rs:30:17
++   |
++LL |     if let S(0, _, ..) = s {};
++   |                 ^^^ help: remove it
++
++error: this pattern is unneeded as the `..` pattern can match that element
++  --> $DIR/unneeded_wildcard_pattern.rs:31:14
++   |
++LL |     if let S(_, .., 0) = s {};
++   |              ^^^ help: remove it
++
++error: this pattern is unneeded as the `..` pattern can match that element
++  --> $DIR/unneeded_wildcard_pattern.rs:32:16
++   |
++LL |     if let S(.., _, 0) = s {};
++   |                ^^^ help: remove it
++
++error: these patterns are unneeded as the `..` pattern can match those elements
++  --> $DIR/unneeded_wildcard_pattern.rs:33:17
++   |
++LL |     if let S(0, _, _, ..) = s {};
++   |                 ^^^^^^ help: remove them
++
++error: these patterns are unneeded as the `..` pattern can match those elements
++  --> $DIR/unneeded_wildcard_pattern.rs:34:19
++   |
++LL |     if let S(0, .., _, _) = s {};
++   |                   ^^^^^^ help: remove them
++
++error: these patterns are unneeded as the `..` pattern can match those elements
++  --> $DIR/unneeded_wildcard_pattern.rs:43:23
++   |
++LL |         if let S(0, .., _, _,) = s {};
++   |                       ^^^^^^ help: remove them
++
++error: aborting due to 14 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3f358d9ecaa0a9529cc345f5d030b3068e8293b0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++// run-rustfix
++
++#![warn(clippy::unreadable_literal)]
++
++struct Foo(u64);
++
++macro_rules! foo {
++    () => {
++        Foo(123123123123)
++    };
++}
++
++fn main() {
++    let _good = (
++        0b1011_i64,
++        0o1_234_u32,
++        0x1_234_567,
++        65536,
++        1_2345_6789,
++        1234_f32,
++        1_234.12_f32,
++        1_234.123_f32,
++        1.123_4_f32,
++    );
++    let _bad = (0b11_0110_i64, 0xcafe_babe_usize, 123_456_f32, 1.234_567_f32);
++    let _good_sci = 1.1234e1;
++    let _bad_sci = 1.123_456e1;
++
++    let _fail9 = 0x00ab_cdef;
++    let _fail10: u32 = 0xBAFE_BAFE;
++    let _fail11 = 0x0abc_deff;
++    let _fail12: i128 = 0x00ab_cabc_abca_bcab_cabc;
++
++    let _ = foo!();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e658a5f28c90e6b2b036047e9289b0096041643e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++// run-rustfix
++
++#![warn(clippy::unreadable_literal)]
++
++struct Foo(u64);
++
++macro_rules! foo {
++    () => {
++        Foo(123123123123)
++    };
++}
++
++fn main() {
++    let _good = (
++        0b1011_i64,
++        0o1_234_u32,
++        0x1_234_567,
++        65536,
++        1_2345_6789,
++        1234_f32,
++        1_234.12_f32,
++        1_234.123_f32,
++        1.123_4_f32,
++    );
++    let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32);
++    let _good_sci = 1.1234e1;
++    let _bad_sci = 1.123456e1;
++
++    let _fail9 = 0xabcdef;
++    let _fail10: u32 = 0xBAFEBAFE;
++    let _fail11 = 0xabcdeff;
++    let _fail12: i128 = 0xabcabcabcabcabcabc;
++
++    let _ = foo!();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1b2ff6bff048ccdde88b7addbf3a07932e46edd6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++error: long literal lacking separators
++  --> $DIR/unreadable_literal.rs:25:17
++   |
++LL |     let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32);
++   |                 ^^^^^^^^^^^^ help: consider: `0b11_0110_i64`
++   |
++   = note: `-D clippy::unreadable-literal` implied by `-D warnings`
++
++error: long literal lacking separators
++  --> $DIR/unreadable_literal.rs:25:31
++   |
++LL |     let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32);
++   |                               ^^^^^^^^^^^^^^^^ help: consider: `0xcafe_babe_usize`
++
++error: long literal lacking separators
++  --> $DIR/unreadable_literal.rs:25:49
++   |
++LL |     let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32);
++   |                                                 ^^^^^^^^^^ help: consider: `123_456_f32`
++
++error: long literal lacking separators
++  --> $DIR/unreadable_literal.rs:25:61
++   |
++LL |     let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32);
++   |                                                             ^^^^^^^^^^^^ help: consider: `1.234_567_f32`
++
++error: long literal lacking separators
++  --> $DIR/unreadable_literal.rs:27:20
++   |
++LL |     let _bad_sci = 1.123456e1;
++   |                    ^^^^^^^^^^ help: consider: `1.123_456e1`
++
++error: long literal lacking separators
++  --> $DIR/unreadable_literal.rs:29:18
++   |
++LL |     let _fail9 = 0xabcdef;
++   |                  ^^^^^^^^ help: consider: `0x00ab_cdef`
++
++error: long literal lacking separators
++  --> $DIR/unreadable_literal.rs:30:24
++   |
++LL |     let _fail10: u32 = 0xBAFEBAFE;
++   |                        ^^^^^^^^^^ help: consider: `0xBAFE_BAFE`
++
++error: long literal lacking separators
++  --> $DIR/unreadable_literal.rs:31:19
++   |
++LL |     let _fail11 = 0xabcdeff;
++   |                   ^^^^^^^^^ help: consider: `0x0abc_deff`
++
++error: long literal lacking separators
++  --> $DIR/unreadable_literal.rs:32:25
++   |
++LL |     let _fail12: i128 = 0xabcabcabcabcabcabc;
++   |                         ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc`
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7bee9c499e1f32373090b85cdbd4dcf5755f832d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,60 @@@
++#![warn(clippy::unsafe_derive_deserialize)]
++#![allow(unused, clippy::missing_safety_doc)]
++
++extern crate serde;
++
++use serde::Deserialize;
++
++#[derive(Deserialize)]
++pub struct A {}
++impl A {
++    pub unsafe fn new(_a: i32, _b: i32) -> Self {
++        Self {}
++    }
++}
++
++#[derive(Deserialize)]
++pub struct B {}
++impl B {
++    pub unsafe fn unsafe_method(&self) {}
++}
++
++#[derive(Deserialize)]
++pub struct C {}
++impl C {
++    pub fn unsafe_block(&self) {
++        unsafe {}
++    }
++}
++
++#[derive(Deserialize)]
++pub struct D {}
++impl D {
++    pub fn inner_unsafe_fn(&self) {
++        unsafe fn inner() {}
++    }
++}
++
++// Does not derive `Deserialize`, should be ignored
++pub struct E {}
++impl E {
++    pub unsafe fn new(_a: i32, _b: i32) -> Self {
++        Self {}
++    }
++
++    pub unsafe fn unsafe_method(&self) {}
++
++    pub fn unsafe_block(&self) {
++        unsafe {}
++    }
++
++    pub fn inner_unsafe_fn(&self) {
++        unsafe fn inner() {}
++    }
++}
++
++// Does not have methods using `unsafe`, should be ignored
++#[derive(Deserialize)]
++pub struct F {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1978bd95a67035f6b90403927757fd84dfa63cd3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
++  --> $DIR/unsafe_derive_deserialize.rs:8:10
++   |
++LL | #[derive(Deserialize)]
++   |          ^^^^^^^^^^^
++   |
++   = note: `-D clippy::unsafe-derive-deserialize` implied by `-D warnings`
++   = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html
++   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
++  --> $DIR/unsafe_derive_deserialize.rs:16:10
++   |
++LL | #[derive(Deserialize)]
++   |          ^^^^^^^^^^^
++   |
++   = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html
++   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
++  --> $DIR/unsafe_derive_deserialize.rs:22:10
++   |
++LL | #[derive(Deserialize)]
++   |          ^^^^^^^^^^^
++   |
++   = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html
++   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
++  --> $DIR/unsafe_derive_deserialize.rs:30:10
++   |
++LL | #[derive(Deserialize)]
++   |          ^^^^^^^^^^^
++   |
++   = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html
++   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a1f616733bd920a277f43afb1e9ce8bdfd865cc3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++#![allow(unused_imports)]
++#![allow(dead_code)]
++#![warn(clippy::unsafe_removed_from_name)]
++
++use std::cell::UnsafeCell as TotallySafeCell;
++
++use std::cell::UnsafeCell as TotallySafeCellAgain;
++
++// Shouldn't error
++use std::cell::RefCell as ProbablyNotUnsafe;
++use std::cell::RefCell as RefCellThatCantBeUnsafe;
++use std::cell::UnsafeCell as SuperDangerousUnsafeCell;
++use std::cell::UnsafeCell as Dangerunsafe;
++use std::cell::UnsafeCell as Bombsawayunsafe;
++
++mod mod_with_some_unsafe_things {
++    pub struct Safe {}
++    pub struct Unsafe {}
++}
++
++use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety;
++
++// Shouldn't error
++use mod_with_some_unsafe_things::Safe as IPromiseItsSafeThisTime;
++use mod_with_some_unsafe_things::Unsafe as SuperUnsafeModThing;
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4f871cbe41b06a0588fa1f9c95fade5c4e47f254
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCell`
++  --> $DIR/unsafe_removed_from_name.rs:5:1
++   |
++LL | use std::cell::UnsafeCell as TotallySafeCell;
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::unsafe-removed-from-name` implied by `-D warnings`
++
++error: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCellAgain`
++  --> $DIR/unsafe_removed_from_name.rs:7:1
++   |
++LL | use std::cell::UnsafeCell as TotallySafeCellAgain;
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: removed `unsafe` from the name of `Unsafe` in use as `LieAboutModSafety`
++  --> $DIR/unsafe_removed_from_name.rs:21:1
++   |
++LL | use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety;
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3c422cc4fee72c5c8cfb2462e173cd88c84c40f7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++// run-rustfix
++
++#![warn(clippy::unseparated_literal_suffix)]
++#![allow(dead_code)]
++
++#[macro_use]
++extern crate clippy_mini_macro_test;
++
++// Test for proc-macro attribute
++#[derive(ClippyMiniMacroTest)]
++struct Foo;
++
++macro_rules! lit_from_macro {
++    () => {
++        42_usize
++    };
++}
++
++fn main() {
++    let _ok1 = 1234_i32;
++    let _ok2 = 1234_isize;
++    let _ok3 = 0x123_isize;
++    let _fail1 = 1234_i32;
++    let _fail2 = 1234_u32;
++    let _fail3 = 1234_isize;
++    let _fail4 = 1234_usize;
++    let _fail5 = 0x123_isize;
++
++    let _okf1 = 1.5_f32;
++    let _okf2 = 1_f32;
++    let _failf1 = 1.5_f32;
++    let _failf2 = 1_f32;
++
++    // Test for macro
++    let _ = lit_from_macro!();
++
++    // Counter example
++    let _ = line!();
++    // Because `assert!` contains `line!()` macro.
++    assert_eq!(4897_u32, 32223);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..09608661e0ef58585afbb94a1d2fdc1fa2c36fee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++// run-rustfix
++
++#![warn(clippy::unseparated_literal_suffix)]
++#![allow(dead_code)]
++
++#[macro_use]
++extern crate clippy_mini_macro_test;
++
++// Test for proc-macro attribute
++#[derive(ClippyMiniMacroTest)]
++struct Foo;
++
++macro_rules! lit_from_macro {
++    () => {
++        42usize
++    };
++}
++
++fn main() {
++    let _ok1 = 1234_i32;
++    let _ok2 = 1234_isize;
++    let _ok3 = 0x123_isize;
++    let _fail1 = 1234i32;
++    let _fail2 = 1234u32;
++    let _fail3 = 1234isize;
++    let _fail4 = 1234usize;
++    let _fail5 = 0x123isize;
++
++    let _okf1 = 1.5_f32;
++    let _okf2 = 1_f32;
++    let _failf1 = 1.5f32;
++    let _failf2 = 1f32;
++
++    // Test for macro
++    let _ = lit_from_macro!();
++
++    // Counter example
++    let _ = line!();
++    // Because `assert!` contains `line!()` macro.
++    assert_eq!(4897u32, 32223);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d7dd526bcb9af431f2f93291401e3781be90f64f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++error: integer type suffix should be separated by an underscore
++  --> $DIR/unseparated_prefix_literals.rs:23:18
++   |
++LL |     let _fail1 = 1234i32;
++   |                  ^^^^^^^ help: add an underscore: `1234_i32`
++   |
++   = note: `-D clippy::unseparated-literal-suffix` implied by `-D warnings`
++
++error: integer type suffix should be separated by an underscore
++  --> $DIR/unseparated_prefix_literals.rs:24:18
++   |
++LL |     let _fail2 = 1234u32;
++   |                  ^^^^^^^ help: add an underscore: `1234_u32`
++
++error: integer type suffix should be separated by an underscore
++  --> $DIR/unseparated_prefix_literals.rs:25:18
++   |
++LL |     let _fail3 = 1234isize;
++   |                  ^^^^^^^^^ help: add an underscore: `1234_isize`
++
++error: integer type suffix should be separated by an underscore
++  --> $DIR/unseparated_prefix_literals.rs:26:18
++   |
++LL |     let _fail4 = 1234usize;
++   |                  ^^^^^^^^^ help: add an underscore: `1234_usize`
++
++error: integer type suffix should be separated by an underscore
++  --> $DIR/unseparated_prefix_literals.rs:27:18
++   |
++LL |     let _fail5 = 0x123isize;
++   |                  ^^^^^^^^^^ help: add an underscore: `0x123_isize`
++
++error: float type suffix should be separated by an underscore
++  --> $DIR/unseparated_prefix_literals.rs:31:19
++   |
++LL |     let _failf1 = 1.5f32;
++   |                   ^^^^^^ help: add an underscore: `1.5_f32`
++
++error: float type suffix should be separated by an underscore
++  --> $DIR/unseparated_prefix_literals.rs:32:19
++   |
++LL |     let _failf2 = 1f32;
++   |                   ^^^^ help: add an underscore: `1_f32`
++
++error: integer type suffix should be separated by an underscore
++  --> $DIR/unseparated_prefix_literals.rs:15:9
++   |
++LL |         42usize
++   |         ^^^^^^^ help: add an underscore: `42_usize`
++...
++LL |     let _ = lit_from_macro!();
++   |             ----------------- in this macro invocation
++   |
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: integer type suffix should be separated by an underscore
++  --> $DIR/unseparated_prefix_literals.rs:40:16
++   |
++LL |     assert_eq!(4897u32, 32223);
++   |                ^^^^^^^ help: add an underscore: `4897_u32`
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ebaba9629db16d515367030d40d31f792754b057
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++#![allow(dead_code)]
++#![warn(clippy::unused_io_amount)]
++
++use std::io;
++
++fn question_mark<T: io::Read + io::Write>(s: &mut T) -> io::Result<()> {
++    s.write(b"test")?;
++    let mut buf = [0u8; 4];
++    s.read(&mut buf)?;
++    Ok(())
++}
++
++fn unwrap<T: io::Read + io::Write>(s: &mut T) {
++    s.write(b"test").unwrap();
++    let mut buf = [0u8; 4];
++    s.read(&mut buf).unwrap();
++}
++
++fn vectored<T: io::Read + io::Write>(s: &mut T) -> io::Result<()> {
++    s.read_vectored(&mut [io::IoSliceMut::new(&mut [])])?;
++    s.write_vectored(&[io::IoSlice::new(&[])])?;
++    Ok(())
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5219d63980b4b164f2f9411a59fac8d95a55ccd1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: written amount is not handled. Use `Write::write_all` instead
++  --> $DIR/unused_io_amount.rs:7:5
++   |
++LL |     s.write(b"test")?;
++   |     ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::unused-io-amount` implied by `-D warnings`
++
++error: read amount is not handled. Use `Read::read_exact` instead
++  --> $DIR/unused_io_amount.rs:9:5
++   |
++LL |     s.read(&mut buf)?;
++   |     ^^^^^^^^^^^^^^^^^
++
++error: written amount is not handled. Use `Write::write_all` instead
++  --> $DIR/unused_io_amount.rs:14:5
++   |
++LL |     s.write(b"test").unwrap();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: read amount is not handled. Use `Read::read_exact` instead
++  --> $DIR/unused_io_amount.rs:16:5
++   |
++LL |     s.read(&mut buf).unwrap();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: read amount is not handled
++  --> $DIR/unused_io_amount.rs:20:5
++   |
++LL |     s.read_vectored(&mut [io::IoSliceMut::new(&mut [])])?;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: written amount is not handled
++  --> $DIR/unused_io_amount.rs:21:5
++   |
++LL |     s.write_vectored(&[io::IoSlice::new(&[])])?;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7a4bbdda1ab273879245443c17e6275c5a240737
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,140 @@@
++#![warn(clippy::unused_self)]
++#![allow(clippy::boxed_local, clippy::fn_params_excessive_bools)]
++
++mod unused_self {
++    use std::pin::Pin;
++    use std::sync::{Arc, Mutex};
++
++    struct A {}
++
++    impl A {
++        fn unused_self_move(self) {}
++        fn unused_self_ref(&self) {}
++        fn unused_self_mut_ref(&mut self) {}
++        fn unused_self_pin_ref(self: Pin<&Self>) {}
++        fn unused_self_pin_mut_ref(self: Pin<&mut Self>) {}
++        fn unused_self_pin_nested(self: Pin<Arc<Self>>) {}
++        fn unused_self_box(self: Box<Self>) {}
++        fn unused_with_other_used_args(&self, x: u8, y: u8) -> u8 {
++            x + y
++        }
++        fn unused_self_class_method(&self) {
++            Self::static_method();
++        }
++
++        fn static_method() {}
++    }
++}
++
++mod unused_self_allow {
++    struct A {}
++
++    impl A {
++        // shouldn't trigger
++        #[allow(clippy::unused_self)]
++        fn unused_self_move(self) {}
++    }
++
++    struct B {}
++
++    // shouldn't trigger
++    #[allow(clippy::unused_self)]
++    impl B {
++        fn unused_self_move(self) {}
++    }
++
++    struct C {}
++
++    #[allow(clippy::unused_self)]
++    impl C {
++        #[warn(clippy::unused_self)]
++        fn some_fn((): ()) {}
++
++        // shouldn't trigger
++        fn unused_self_move(self) {}
++    }
++}
++
++mod used_self {
++    use std::pin::Pin;
++
++    struct A {
++        x: u8,
++    }
++
++    impl A {
++        fn used_self_move(self) -> u8 {
++            self.x
++        }
++        fn used_self_ref(&self) -> u8 {
++            self.x
++        }
++        fn used_self_mut_ref(&mut self) {
++            self.x += 1
++        }
++        fn used_self_pin_ref(self: Pin<&Self>) -> u8 {
++            self.x
++        }
++        fn used_self_box(self: Box<Self>) -> u8 {
++            self.x
++        }
++        fn used_self_with_other_unused_args(&self, x: u8, y: u8) -> u8 {
++            self.x
++        }
++        fn used_in_nested_closure(&self) -> u8 {
++            let mut a = || -> u8 { self.x };
++            a()
++        }
++
++        #[allow(clippy::collapsible_if)]
++        fn used_self_method_nested_conditions(&self, a: bool, b: bool, c: bool, d: bool) {
++            if a {
++                if b {
++                    if c {
++                        if d {
++                            self.used_self_ref();
++                        }
++                    }
++                }
++            }
++        }
++
++        fn foo(&self) -> u32 {
++            let mut sum = 0u32;
++            for i in 0..self.x {
++                sum += i as u32;
++            }
++            sum
++        }
++
++        fn bar(&mut self, x: u8) -> u32 {
++            let mut y = 0u32;
++            for i in 0..x {
++                y += self.foo()
++            }
++            y
++        }
++    }
++}
++
++mod not_applicable {
++    use std::fmt;
++
++    struct A {}
++
++    impl fmt::Debug for A {
++        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++            write!(f, "A")
++        }
++    }
++
++    impl A {
++        fn method(x: u8, y: u8) {}
++    }
++
++    trait B {
++        fn method(&self) {}
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0534b40eabb75ebb2de8465990b82f185f89c97e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,75 @@@
++error: unused `self` argument
++  --> $DIR/unused_self.rs:11:29
++   |
++LL |         fn unused_self_move(self) {}
++   |                             ^^^^
++   |
++   = note: `-D clippy::unused-self` implied by `-D warnings`
++   = help: consider refactoring to a associated function
++
++error: unused `self` argument
++  --> $DIR/unused_self.rs:12:28
++   |
++LL |         fn unused_self_ref(&self) {}
++   |                            ^^^^^
++   |
++   = help: consider refactoring to a associated function
++
++error: unused `self` argument
++  --> $DIR/unused_self.rs:13:32
++   |
++LL |         fn unused_self_mut_ref(&mut self) {}
++   |                                ^^^^^^^^^
++   |
++   = help: consider refactoring to a associated function
++
++error: unused `self` argument
++  --> $DIR/unused_self.rs:14:32
++   |
++LL |         fn unused_self_pin_ref(self: Pin<&Self>) {}
++   |                                ^^^^
++   |
++   = help: consider refactoring to a associated function
++
++error: unused `self` argument
++  --> $DIR/unused_self.rs:15:36
++   |
++LL |         fn unused_self_pin_mut_ref(self: Pin<&mut Self>) {}
++   |                                    ^^^^
++   |
++   = help: consider refactoring to a associated function
++
++error: unused `self` argument
++  --> $DIR/unused_self.rs:16:35
++   |
++LL |         fn unused_self_pin_nested(self: Pin<Arc<Self>>) {}
++   |                                   ^^^^
++   |
++   = help: consider refactoring to a associated function
++
++error: unused `self` argument
++  --> $DIR/unused_self.rs:17:28
++   |
++LL |         fn unused_self_box(self: Box<Self>) {}
++   |                            ^^^^
++   |
++   = help: consider refactoring to a associated function
++
++error: unused `self` argument
++  --> $DIR/unused_self.rs:18:40
++   |
++LL |         fn unused_with_other_used_args(&self, x: u8, y: u8) -> u8 {
++   |                                        ^^^^^
++   |
++   = help: consider refactoring to a associated function
++
++error: unused `self` argument
++  --> $DIR/unused_self.rs:21:37
++   |
++LL |         fn unused_self_class_method(&self) {
++   |                                     ^^^^^
++   |
++   = help: consider refactoring to a associated function
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3f63624720f7553276aea8c6f721bed2c573066e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,59 @@@
++// run-rustfix
++
++// The output for humans should just highlight the whole span without showing
++// the suggested replacement, but we also want to test that suggested
++// replacement only removes one set of parentheses, rather than naïvely
++// stripping away any starting or ending parenthesis characters—hence this
++// test of the JSON error format.
++
++#![feature(custom_inner_attributes)]
++#![rustfmt::skip]
++
++#![deny(clippy::unused_unit)]
++#![allow(dead_code)]
++
++struct Unitter;
++impl Unitter {
++    // try to disorient the lint with multiple unit returns and newlines
++    #[allow(clippy::no_effect)]
++    pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) 
++    where G: Fn() -> () {
++        let _y: &dyn Fn() -> () = &f;
++        (); // this should not lint, as it's not in return type position
++    }
++}
++
++impl Into<()> for Unitter {
++    #[rustfmt::skip]
++    fn into(self)  {
++        
++    }
++}
++
++fn return_unit()  {  }
++
++#[allow(clippy::needless_return)]
++#[allow(clippy::never_loop)]
++#[allow(clippy::unit_cmp)]
++fn main() {
++    let u = Unitter;
++    assert_eq!(u.get_unit(|| {}, return_unit), u.into());
++    return_unit();
++    loop {
++        break;
++    }
++    return;
++}
++
++// https://github.com/rust-lang/rust-clippy/issues/4076
++fn foo() {
++    macro_rules! foo {
++        (recv($r:expr) -> $res:pat => $body:expr) => {
++            $body
++        }
++    }
++
++    foo! {
++        recv(rx) -> _x => ()
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8fc072ebd69f848ffc41dd09306febf07e459a96
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,60 @@@
++// run-rustfix
++
++// The output for humans should just highlight the whole span without showing
++// the suggested replacement, but we also want to test that suggested
++// replacement only removes one set of parentheses, rather than naïvely
++// stripping away any starting or ending parenthesis characters—hence this
++// test of the JSON error format.
++
++#![feature(custom_inner_attributes)]
++#![rustfmt::skip]
++
++#![deny(clippy::unused_unit)]
++#![allow(dead_code)]
++
++struct Unitter;
++impl Unitter {
++    // try to disorient the lint with multiple unit returns and newlines
++    #[allow(clippy::no_effect)]
++    pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) ->
++        ()
++    where G: Fn() -> () {
++        let _y: &dyn Fn() -> () = &f;
++        (); // this should not lint, as it's not in return type position
++    }
++}
++
++impl Into<()> for Unitter {
++    #[rustfmt::skip]
++    fn into(self) -> () {
++        ()
++    }
++}
++
++fn return_unit() -> () { () }
++
++#[allow(clippy::needless_return)]
++#[allow(clippy::never_loop)]
++#[allow(clippy::unit_cmp)]
++fn main() {
++    let u = Unitter;
++    assert_eq!(u.get_unit(|| {}, return_unit), u.into());
++    return_unit();
++    loop {
++        break();
++    }
++    return();
++}
++
++// https://github.com/rust-lang/rust-clippy/issues/4076
++fn foo() {
++    macro_rules! foo {
++        (recv($r:expr) -> $res:pat => $body:expr) => {
++            $body
++        }
++    }
++
++    foo! {
++        recv(rx) -> _x => ()
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a013d2b3495ba40a4d824e042e71ce6f3d7e873e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++error: unneeded unit return type
++  --> $DIR/unused_unit.rs:19:59
++   |
++LL |       pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) ->
++   |  ___________________________________________________________^
++LL | |         ()
++   | |__________^ help: remove the `-> ()`
++   |
++note: the lint level is defined here
++  --> $DIR/unused_unit.rs:12:9
++   |
++LL | #![deny(clippy::unused_unit)]
++   |         ^^^^^^^^^^^^^^^^^^^
++
++error: unneeded unit return type
++  --> $DIR/unused_unit.rs:29:19
++   |
++LL |     fn into(self) -> () {
++   |                   ^^^^^ help: remove the `-> ()`
++
++error: unneeded unit expression
++  --> $DIR/unused_unit.rs:30:9
++   |
++LL |         ()
++   |         ^^ help: remove the final `()`
++
++error: unneeded unit return type
++  --> $DIR/unused_unit.rs:34:18
++   |
++LL | fn return_unit() -> () { () }
++   |                  ^^^^^ help: remove the `-> ()`
++
++error: unneeded unit expression
++  --> $DIR/unused_unit.rs:34:26
++   |
++LL | fn return_unit() -> () { () }
++   |                          ^^ help: remove the final `()`
++
++error: unneeded `()`
++  --> $DIR/unused_unit.rs:44:14
++   |
++LL |         break();
++   |              ^^ help: remove the `()`
++
++error: unneeded `()`
++  --> $DIR/unused_unit.rs:46:11
++   |
++LL |     return();
++   |           ^^ help: remove the `()`
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fcd1fcd14d48f67590a89b0c33848af7d96f5dd9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++#![warn(clippy::option_unwrap_used, clippy::result_unwrap_used)]
++
++fn unwrap_option() {
++    let opt = Some(0);
++    let _ = opt.unwrap();
++}
++
++fn unwrap_result() {
++    let res: Result<u8, ()> = Ok(0);
++    let _ = res.unwrap();
++}
++
++fn main() {
++    unwrap_option();
++    unwrap_result();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b90ce68fa97ac76c064465d579c2b85e315cb9af
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++error: used `unwrap()` on `an Option` value
++  --> $DIR/unwrap.rs:5:13
++   |
++LL |     let _ = opt.unwrap();
++   |             ^^^^^^^^^^^^
++   |
++   = note: `-D clippy::option-unwrap-used` implied by `-D warnings`
++   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
++
++error: used `unwrap()` on `a Result` value
++  --> $DIR/unwrap.rs:10:13
++   |
++LL |     let _ = res.unwrap();
++   |             ^^^^^^^^^^^^
++   |
++   = note: `-D clippy::result-unwrap-used` implied by `-D warnings`
++   = help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bfb41e4394731e03969834ae9815017d6a91fcd2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++#![warn(clippy::all)]
++
++fn main() {
++    let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
++}
++
++fn new_lines() {
++    let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c3a7464fd470ea147c52f90cd9c242dc9557ec97
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: use of `unwrap_or` followed by a function call
++  --> $DIR/unwrap_or.rs:4:47
++   |
++LL |     let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
++   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "Fail".to_string())`
++   |
++   = note: `-D clippy::or-fun-call` implied by `-D warnings`
++
++error: use of `unwrap_or` followed by a function call
++  --> $DIR/unwrap_or.rs:8:47
++   |
++LL |     let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
++   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "Fail".to_string())`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..30ba9188db43d35079838dd84f56c72e46987965
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++#!/bin/bash
++
++# A script to update the references for all tests. The idea is that
++# you do a run, which will generate files in the build directory
++# containing the (normalized) actual output of the compiler. You then
++# run this script, which will copy those files over. If you find
++# yourself manually editing a foo.stderr file, you're doing it wrong.
++#
++# See all `update-references.sh`, if you just want to update a single test.
++
++if [[ "$1" == "--help" || "$1" == "-h" ]]; then
++    echo "usage: $0"
++fi
++
++CARGO_TARGET_DIR=${CARGO_TARGET_DIR:-$PWD/target}
++PROFILE=${PROFILE:-debug}
++BUILD_DIR=${CARGO_TARGET_DIR}/${PROFILE}/test_build_base
++
++MY_DIR=$(dirname "$0")
++cd "$MY_DIR" || exit
++find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2c13c327d79802247ea86914133f9c36df1682b4
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++#!/bin/bash
++
++# A script to update the references for particular tests. The idea is
++# that you do a run, which will generate files in the build directory
++# containing the (normalized) actual output of the compiler. This
++# script will then copy that output and replace the "expected output"
++# files. You can then commit the changes.
++#
++# If you find yourself manually editing a `foo.stderr` file, you're
++# doing it wrong.
++
++if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then
++    echo "usage: $0 <build-directory> <relative-path-to-rs-files>"
++    echo ""
++    echo "For example:"
++    echo "   $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs"
++fi
++
++MYDIR=$(dirname "$0")
++
++BUILD_DIR="$1"
++shift
++
++while [[ "$1" != "" ]]; do
++    STDERR_NAME="${1/%.rs/.stderr}"
++    STDOUT_NAME="${1/%.rs/.stdout}"
++    FIXED_NAME="${1/%.rs/.fixed}"
++    shift
++    if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \
++           ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then
++        echo updating "$MYDIR"/"$STDOUT_NAME"
++        cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"
++    fi
++    if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \
++           ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then
++        echo updating "$MYDIR"/"$STDERR_NAME"
++        cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"
++    fi
++    if [[ -f "$BUILD_DIR"/"$FIXED_NAME" ]] && \
++           ! (cmp -s -- "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME"); then
++        echo updating "$MYDIR"/"$FIXED_NAME"
++        cp "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME"
++    fi
++done
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ebb3aa28daf3d80d0cbc862b80e88aa22b07034e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,253 @@@
++// run-rustfix
++// edition:2018
++
++#![warn(clippy::use_self)]
++#![allow(dead_code)]
++#![allow(clippy::should_implement_trait)]
++
++fn main() {}
++
++mod use_self {
++    struct Foo {}
++
++    impl Foo {
++        fn new() -> Self {
++            Self {}
++        }
++        fn test() -> Self {
++            Self::new()
++        }
++    }
++
++    impl Default for Foo {
++        fn default() -> Self {
++            Self::new()
++        }
++    }
++}
++
++mod better {
++    struct Foo {}
++
++    impl Foo {
++        fn new() -> Self {
++            Self {}
++        }
++        fn test() -> Self {
++            Self::new()
++        }
++    }
++
++    impl Default for Foo {
++        fn default() -> Self {
++            Self::new()
++        }
++    }
++}
++
++mod lifetimes {
++    struct Foo<'a> {
++        foo_str: &'a str,
++    }
++
++    impl<'a> Foo<'a> {
++        // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
++        // Foo<'b>`
++        fn foo(s: &str) -> Foo {
++            Foo { foo_str: s }
++        }
++        // cannot replace with `Self`, because that's `Foo<'a>`
++        fn bar() -> Foo<'static> {
++            Foo { foo_str: "foo" }
++        }
++
++        // FIXME: the lint does not handle lifetimed struct
++        // `Self` should be applicable here
++        fn clone(&self) -> Foo<'a> {
++            Foo { foo_str: self.foo_str }
++        }
++    }
++}
++
++mod issue2894 {
++    trait IntoBytes {
++        fn into_bytes(&self) -> Vec<u8>;
++    }
++
++    // This should not be linted
++    impl IntoBytes for u8 {
++        fn into_bytes(&self) -> Vec<u8> {
++            vec![*self]
++        }
++    }
++}
++
++mod existential {
++    struct Foo;
++
++    impl Foo {
++        fn bad(foos: &[Self]) -> impl Iterator<Item = &Self> {
++            foos.iter()
++        }
++
++        fn good(foos: &[Self]) -> impl Iterator<Item = &Self> {
++            foos.iter()
++        }
++    }
++}
++
++mod tuple_structs {
++    pub struct TS(i32);
++
++    impl TS {
++        pub fn ts() -> Self {
++            Self(0)
++        }
++    }
++}
++
++mod macros {
++    macro_rules! use_self_expand {
++        () => {
++            fn new() -> Self {
++                Self {}
++            }
++        };
++    }
++
++    struct Foo {}
++
++    impl Foo {
++        use_self_expand!(); // Should lint in local macros
++    }
++}
++
++mod nesting {
++    struct Foo {}
++    impl Foo {
++        fn foo() {
++            #[allow(unused_imports)]
++            use self::Foo; // Can't use Self here
++            struct Bar {
++                foo: Foo, // Foo != Self
++            }
++
++            impl Bar {
++                fn bar() -> Self {
++                    Self { foo: Foo {} }
++                }
++            }
++
++            // Can't use Self here
++            fn baz() -> Foo {
++                Foo {}
++            }
++        }
++
++        // Should lint here
++        fn baz() -> Self {
++            Self {}
++        }
++    }
++
++    enum Enum {
++        A,
++        B(u64),
++        C { field: bool },
++    }
++    impl Enum {
++        fn method() {
++            #[allow(unused_imports)]
++            use self::Enum::*; // Issue 3425
++            static STATIC: Enum = Enum::A; // Can't use Self as type
++        }
++
++        fn method2() {
++            let _ = Self::B(42);
++            let _ = Self::C { field: true };
++            let _ = Self::A;
++        }
++    }
++}
++
++mod issue3410 {
++
++    struct A;
++    struct B;
++
++    trait Trait<T> {
++        fn a(v: T);
++    }
++
++    impl Trait<Vec<A>> for Vec<B> {
++        fn a(_: Vec<A>) {}
++    }
++}
++
++#[allow(clippy::no_effect, path_statements)]
++mod rustfix {
++    mod nested {
++        pub struct A {}
++    }
++
++    impl nested::A {
++        const A: bool = true;
++
++        fn fun_1() {}
++
++        fn fun_2() {
++            Self::fun_1();
++            Self::A;
++
++            Self {};
++        }
++    }
++}
++
++mod issue3567 {
++    struct TestStruct {}
++    impl TestStruct {
++        fn from_something() -> Self {
++            Self {}
++        }
++    }
++
++    trait Test {
++        fn test() -> TestStruct;
++    }
++
++    impl Test for TestStruct {
++        fn test() -> TestStruct {
++            Self::from_something()
++        }
++    }
++}
++
++mod paths_created_by_lowering {
++    use std::ops::Range;
++
++    struct S {}
++
++    impl S {
++        const A: usize = 0;
++        const B: usize = 1;
++
++        async fn g() -> Self {
++            Self {}
++        }
++
++        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
++            &p[Self::A..Self::B]
++        }
++    }
++
++    trait T {
++        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8];
++    }
++
++    impl T for Range<u8> {
++        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
++            &p[0..1]
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8a182192ab34d67a070ee4990c801e87cbf9d669
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,253 @@@
++// run-rustfix
++// edition:2018
++
++#![warn(clippy::use_self)]
++#![allow(dead_code)]
++#![allow(clippy::should_implement_trait)]
++
++fn main() {}
++
++mod use_self {
++    struct Foo {}
++
++    impl Foo {
++        fn new() -> Foo {
++            Foo {}
++        }
++        fn test() -> Foo {
++            Foo::new()
++        }
++    }
++
++    impl Default for Foo {
++        fn default() -> Foo {
++            Foo::new()
++        }
++    }
++}
++
++mod better {
++    struct Foo {}
++
++    impl Foo {
++        fn new() -> Self {
++            Self {}
++        }
++        fn test() -> Self {
++            Self::new()
++        }
++    }
++
++    impl Default for Foo {
++        fn default() -> Self {
++            Self::new()
++        }
++    }
++}
++
++mod lifetimes {
++    struct Foo<'a> {
++        foo_str: &'a str,
++    }
++
++    impl<'a> Foo<'a> {
++        // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
++        // Foo<'b>`
++        fn foo(s: &str) -> Foo {
++            Foo { foo_str: s }
++        }
++        // cannot replace with `Self`, because that's `Foo<'a>`
++        fn bar() -> Foo<'static> {
++            Foo { foo_str: "foo" }
++        }
++
++        // FIXME: the lint does not handle lifetimed struct
++        // `Self` should be applicable here
++        fn clone(&self) -> Foo<'a> {
++            Foo { foo_str: self.foo_str }
++        }
++    }
++}
++
++mod issue2894 {
++    trait IntoBytes {
++        fn into_bytes(&self) -> Vec<u8>;
++    }
++
++    // This should not be linted
++    impl IntoBytes for u8 {
++        fn into_bytes(&self) -> Vec<u8> {
++            vec![*self]
++        }
++    }
++}
++
++mod existential {
++    struct Foo;
++
++    impl Foo {
++        fn bad(foos: &[Self]) -> impl Iterator<Item = &Foo> {
++            foos.iter()
++        }
++
++        fn good(foos: &[Self]) -> impl Iterator<Item = &Self> {
++            foos.iter()
++        }
++    }
++}
++
++mod tuple_structs {
++    pub struct TS(i32);
++
++    impl TS {
++        pub fn ts() -> Self {
++            TS(0)
++        }
++    }
++}
++
++mod macros {
++    macro_rules! use_self_expand {
++        () => {
++            fn new() -> Foo {
++                Foo {}
++            }
++        };
++    }
++
++    struct Foo {}
++
++    impl Foo {
++        use_self_expand!(); // Should lint in local macros
++    }
++}
++
++mod nesting {
++    struct Foo {}
++    impl Foo {
++        fn foo() {
++            #[allow(unused_imports)]
++            use self::Foo; // Can't use Self here
++            struct Bar {
++                foo: Foo, // Foo != Self
++            }
++
++            impl Bar {
++                fn bar() -> Bar {
++                    Bar { foo: Foo {} }
++                }
++            }
++
++            // Can't use Self here
++            fn baz() -> Foo {
++                Foo {}
++            }
++        }
++
++        // Should lint here
++        fn baz() -> Foo {
++            Foo {}
++        }
++    }
++
++    enum Enum {
++        A,
++        B(u64),
++        C { field: bool },
++    }
++    impl Enum {
++        fn method() {
++            #[allow(unused_imports)]
++            use self::Enum::*; // Issue 3425
++            static STATIC: Enum = Enum::A; // Can't use Self as type
++        }
++
++        fn method2() {
++            let _ = Enum::B(42);
++            let _ = Enum::C { field: true };
++            let _ = Enum::A;
++        }
++    }
++}
++
++mod issue3410 {
++
++    struct A;
++    struct B;
++
++    trait Trait<T> {
++        fn a(v: T);
++    }
++
++    impl Trait<Vec<A>> for Vec<B> {
++        fn a(_: Vec<A>) {}
++    }
++}
++
++#[allow(clippy::no_effect, path_statements)]
++mod rustfix {
++    mod nested {
++        pub struct A {}
++    }
++
++    impl nested::A {
++        const A: bool = true;
++
++        fn fun_1() {}
++
++        fn fun_2() {
++            nested::A::fun_1();
++            nested::A::A;
++
++            nested::A {};
++        }
++    }
++}
++
++mod issue3567 {
++    struct TestStruct {}
++    impl TestStruct {
++        fn from_something() -> Self {
++            Self {}
++        }
++    }
++
++    trait Test {
++        fn test() -> TestStruct;
++    }
++
++    impl Test for TestStruct {
++        fn test() -> TestStruct {
++            TestStruct::from_something()
++        }
++    }
++}
++
++mod paths_created_by_lowering {
++    use std::ops::Range;
++
++    struct S {}
++
++    impl S {
++        const A: usize = 0;
++        const B: usize = 1;
++
++        async fn g() -> S {
++            S {}
++        }
++
++        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
++            &p[S::A..S::B]
++        }
++    }
++
++    trait T {
++        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8];
++    }
++
++    impl T for Range<u8> {
++        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
++            &p[0..1]
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b33928597c1455534bd6f8327aec24263f200990
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,164 @@@
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:14:21
++   |
++LL |         fn new() -> Foo {
++   |                     ^^^ help: use the applicable keyword: `Self`
++   |
++   = note: `-D clippy::use-self` implied by `-D warnings`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:15:13
++   |
++LL |             Foo {}
++   |             ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:17:22
++   |
++LL |         fn test() -> Foo {
++   |                      ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:18:13
++   |
++LL |             Foo::new()
++   |             ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:23:25
++   |
++LL |         fn default() -> Foo {
++   |                         ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:24:13
++   |
++LL |             Foo::new()
++   |             ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:89:56
++   |
++LL |         fn bad(foos: &[Self]) -> impl Iterator<Item = &Foo> {
++   |                                                        ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:104:13
++   |
++LL |             TS(0)
++   |             ^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:112:25
++   |
++LL |             fn new() -> Foo {
++   |                         ^^^ help: use the applicable keyword: `Self`
++...
++LL |         use_self_expand!(); // Should lint in local macros
++   |         ------------------- in this macro invocation
++   |
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:113:17
++   |
++LL |                 Foo {}
++   |                 ^^^ help: use the applicable keyword: `Self`
++...
++LL |         use_self_expand!(); // Should lint in local macros
++   |         ------------------- in this macro invocation
++   |
++   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:148:21
++   |
++LL |         fn baz() -> Foo {
++   |                     ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:149:13
++   |
++LL |             Foo {}
++   |             ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:136:29
++   |
++LL |                 fn bar() -> Bar {
++   |                             ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:137:21
++   |
++LL |                     Bar { foo: Foo {} }
++   |                     ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:166:21
++   |
++LL |             let _ = Enum::B(42);
++   |                     ^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:167:21
++   |
++LL |             let _ = Enum::C { field: true };
++   |                     ^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:168:21
++   |
++LL |             let _ = Enum::A;
++   |                     ^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:199:13
++   |
++LL |             nested::A::fun_1();
++   |             ^^^^^^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:200:13
++   |
++LL |             nested::A::A;
++   |             ^^^^^^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:202:13
++   |
++LL |             nested::A {};
++   |             ^^^^^^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:221:13
++   |
++LL |             TestStruct::from_something()
++   |             ^^^^^^^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:235:25
++   |
++LL |         async fn g() -> S {
++   |                         ^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:236:13
++   |
++LL |             S {}
++   |             ^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:240:16
++   |
++LL |             &p[S::A..S::B]
++   |                ^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:240:22
++   |
++LL |             &p[S::A..S::B]
++   |                      ^ help: use the applicable keyword: `Self`
++
++error: aborting due to 25 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1582ae114bf4c4b486af912d3e9d297171a49cda
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,114 @@@
++// run-rustfix
++
++#![warn(clippy::use_self)]
++#![allow(dead_code)]
++#![allow(clippy::should_implement_trait, clippy::boxed_local)]
++
++use std::ops::Mul;
++
++trait SelfTrait {
++    fn refs(p1: &Self) -> &Self;
++    fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self;
++    fn mut_refs(p1: &mut Self) -> &mut Self;
++    fn nested(p1: Box<Self>, p2: (&u8, &Self));
++    fn vals(r: Self) -> Self;
++}
++
++#[derive(Default)]
++struct Bad;
++
++impl SelfTrait for Bad {
++    fn refs(p1: &Self) -> &Self {
++        p1
++    }
++
++    fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
++        p1
++    }
++
++    fn mut_refs(p1: &mut Self) -> &mut Self {
++        p1
++    }
++
++    fn nested(_p1: Box<Self>, _p2: (&u8, &Self)) {}
++
++    fn vals(_: Self) -> Self {
++        Self::default()
++    }
++}
++
++impl Mul for Bad {
++    type Output = Self;
++
++    fn mul(self, rhs: Self) -> Self {
++        rhs
++    }
++}
++
++impl Clone for Bad {
++    fn clone(&self) -> Self {
++        Self
++    }
++}
++
++#[derive(Default)]
++struct Good;
++
++impl SelfTrait for Good {
++    fn refs(p1: &Self) -> &Self {
++        p1
++    }
++
++    fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
++        p1
++    }
++
++    fn mut_refs(p1: &mut Self) -> &mut Self {
++        p1
++    }
++
++    fn nested(_p1: Box<Self>, _p2: (&u8, &Self)) {}
++
++    fn vals(_: Self) -> Self {
++        Self::default()
++    }
++}
++
++impl Mul for Good {
++    type Output = Self;
++
++    fn mul(self, rhs: Self) -> Self {
++        rhs
++    }
++}
++
++trait NameTrait {
++    fn refs(p1: &u8) -> &u8;
++    fn ref_refs<'a>(p1: &'a &'a u8) -> &'a &'a u8;
++    fn mut_refs(p1: &mut u8) -> &mut u8;
++    fn nested(p1: Box<u8>, p2: (&u8, &u8));
++    fn vals(p1: u8) -> u8;
++}
++
++// Using `Self` instead of the type name is OK
++impl NameTrait for u8 {
++    fn refs(p1: &Self) -> &Self {
++        p1
++    }
++
++    fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
++        p1
++    }
++
++    fn mut_refs(p1: &mut Self) -> &mut Self {
++        p1
++    }
++
++    fn nested(_p1: Box<Self>, _p2: (&Self, &Self)) {}
++
++    fn vals(_: Self) -> Self {
++        Self::default()
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..70667b9797e762c0ec4f383d58d0a1ced2316ce3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,114 @@@
++// run-rustfix
++
++#![warn(clippy::use_self)]
++#![allow(dead_code)]
++#![allow(clippy::should_implement_trait, clippy::boxed_local)]
++
++use std::ops::Mul;
++
++trait SelfTrait {
++    fn refs(p1: &Self) -> &Self;
++    fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self;
++    fn mut_refs(p1: &mut Self) -> &mut Self;
++    fn nested(p1: Box<Self>, p2: (&u8, &Self));
++    fn vals(r: Self) -> Self;
++}
++
++#[derive(Default)]
++struct Bad;
++
++impl SelfTrait for Bad {
++    fn refs(p1: &Bad) -> &Bad {
++        p1
++    }
++
++    fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad {
++        p1
++    }
++
++    fn mut_refs(p1: &mut Bad) -> &mut Bad {
++        p1
++    }
++
++    fn nested(_p1: Box<Bad>, _p2: (&u8, &Bad)) {}
++
++    fn vals(_: Bad) -> Bad {
++        Bad::default()
++    }
++}
++
++impl Mul for Bad {
++    type Output = Bad;
++
++    fn mul(self, rhs: Bad) -> Bad {
++        rhs
++    }
++}
++
++impl Clone for Bad {
++    fn clone(&self) -> Self {
++        Bad
++    }
++}
++
++#[derive(Default)]
++struct Good;
++
++impl SelfTrait for Good {
++    fn refs(p1: &Self) -> &Self {
++        p1
++    }
++
++    fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
++        p1
++    }
++
++    fn mut_refs(p1: &mut Self) -> &mut Self {
++        p1
++    }
++
++    fn nested(_p1: Box<Self>, _p2: (&u8, &Self)) {}
++
++    fn vals(_: Self) -> Self {
++        Self::default()
++    }
++}
++
++impl Mul for Good {
++    type Output = Self;
++
++    fn mul(self, rhs: Self) -> Self {
++        rhs
++    }
++}
++
++trait NameTrait {
++    fn refs(p1: &u8) -> &u8;
++    fn ref_refs<'a>(p1: &'a &'a u8) -> &'a &'a u8;
++    fn mut_refs(p1: &mut u8) -> &mut u8;
++    fn nested(p1: Box<u8>, p2: (&u8, &u8));
++    fn vals(p1: u8) -> u8;
++}
++
++// Using `Self` instead of the type name is OK
++impl NameTrait for u8 {
++    fn refs(p1: &Self) -> &Self {
++        p1
++    }
++
++    fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
++        p1
++    }
++
++    fn mut_refs(p1: &mut Self) -> &mut Self {
++        p1
++    }
++
++    fn nested(_p1: Box<Self>, _p2: (&Self, &Self)) {}
++
++    fn vals(_: Self) -> Self {
++        Self::default()
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4f2506cc1192fecd9514b2dcb36ce070f954642c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,94 @@@
++error: unnecessary structure name repetition
++  --> $DIR/use_self_trait.rs:21:18
++   |
++LL |     fn refs(p1: &Bad) -> &Bad {
++   |                  ^^^ help: use the applicable keyword: `Self`
++   |
++   = note: `-D clippy::use-self` implied by `-D warnings`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self_trait.rs:21:27
++   |
++LL |     fn refs(p1: &Bad) -> &Bad {
++   |                           ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self_trait.rs:25:33
++   |
++LL |     fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad {
++   |                                 ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self_trait.rs:25:49
++   |
++LL |     fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad {
++   |                                                 ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self_trait.rs:29:26
++   |
++LL |     fn mut_refs(p1: &mut Bad) -> &mut Bad {
++   |                          ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self_trait.rs:29:39
++   |
++LL |     fn mut_refs(p1: &mut Bad) -> &mut Bad {
++   |                                       ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self_trait.rs:33:24
++   |
++LL |     fn nested(_p1: Box<Bad>, _p2: (&u8, &Bad)) {}
++   |                        ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self_trait.rs:33:42
++   |
++LL |     fn nested(_p1: Box<Bad>, _p2: (&u8, &Bad)) {}
++   |                                          ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self_trait.rs:35:16
++   |
++LL |     fn vals(_: Bad) -> Bad {
++   |                ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self_trait.rs:35:24
++   |
++LL |     fn vals(_: Bad) -> Bad {
++   |                        ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self_trait.rs:36:9
++   |
++LL |         Bad::default()
++   |         ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self_trait.rs:41:19
++   |
++LL |     type Output = Bad;
++   |                   ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self_trait.rs:43:23
++   |
++LL |     fn mul(self, rhs: Bad) -> Bad {
++   |                       ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self_trait.rs:43:31
++   |
++LL |     fn mul(self, rhs: Bad) -> Bad {
++   |                               ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self_trait.rs:50:9
++   |
++LL |         Bad
++   |         ^^^ help: use the applicable keyword: `Self`
++
++error: aborting due to 15 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8e0243c49aaa038405796a66695a6f264f38b569
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,119 @@@
++// edition:2018
++// aux-build:proc_macro_derive.rs
++
++#![feature(rustc_private)]
++#![warn(clippy::all)]
++#![allow(clippy::blacklisted_name)]
++#![warn(clippy::used_underscore_binding)]
++
++#[macro_use]
++extern crate proc_macro_derive;
++
++// This should not trigger the lint. There's underscore binding inside the external derive that
++// would trigger the `used_underscore_binding` lint.
++#[derive(DeriveSomething)]
++struct Baz;
++
++macro_rules! test_macro {
++    () => {{
++        let _foo = 42;
++        _foo + 1
++    }};
++}
++
++/// Tests that we lint if we use a binding with a single leading underscore
++fn prefix_underscore(_foo: u32) -> u32 {
++    _foo + 1
++}
++
++/// Tests that we lint if we use a `_`-variable defined outside within a macro expansion
++fn in_macro_or_desugar(_foo: u32) {
++    println!("{}", _foo);
++    assert_eq!(_foo, _foo);
++
++    test_macro!() + 1;
++}
++
++// Struct for testing use of fields prefixed with an underscore
++struct StructFieldTest {
++    _underscore_field: u32,
++}
++
++/// Tests that we lint the use of a struct field which is prefixed with an underscore
++fn in_struct_field() {
++    let mut s = StructFieldTest { _underscore_field: 0 };
++    s._underscore_field += 1;
++}
++
++/// Tests that we do not lint if the underscore is not a prefix
++fn non_prefix_underscore(some_foo: u32) -> u32 {
++    some_foo + 1
++}
++
++/// Tests that we do not lint if we do not use the binding (simple case)
++fn unused_underscore_simple(_foo: u32) -> u32 {
++    1
++}
++
++/// Tests that we do not lint if we do not use the binding (complex case). This checks for
++/// compatibility with the built-in `unused_variables` lint.
++fn unused_underscore_complex(mut _foo: u32) -> u32 {
++    _foo += 1;
++    _foo = 2;
++    1
++}
++
++/// Test that we do not lint for multiple underscores
++fn multiple_underscores(__foo: u32) -> u32 {
++    __foo + 1
++}
++
++// Non-variable bindings with preceding underscore
++fn _fn_test() {}
++struct _StructTest;
++enum _EnumTest {
++    _Empty,
++    _Value(_StructTest),
++}
++
++/// Tests that we do not lint for non-variable bindings
++fn non_variables() {
++    _fn_test();
++    let _s = _StructTest;
++    let _e = match _EnumTest::_Value(_StructTest) {
++        _EnumTest::_Empty => 0,
++        _EnumTest::_Value(_st) => 1,
++    };
++    let f = _fn_test;
++    f();
++}
++
++// Tests that we do not lint if the binding comes from await desugaring,
++// but we do lint the awaited expression. See issue 5360.
++async fn await_desugaring() {
++    async fn foo() {}
++    fn uses_i(_i: i32) {}
++
++    foo().await;
++    ({
++        let _i = 5;
++        uses_i(_i);
++        foo()
++    })
++    .await
++}
++
++fn main() {
++    let foo = 0u32;
++    // tests of unused_underscore lint
++    let _ = prefix_underscore(foo);
++    in_macro_or_desugar(foo);
++    in_struct_field();
++    // possible false positives
++    let _ = non_prefix_underscore(foo);
++    let _ = unused_underscore_simple(foo);
++    let _ = unused_underscore_complex(foo);
++    let _ = multiple_underscores(foo);
++    non_variables();
++    await_desugaring();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..68e96148093d2ad1bd58bf082d9574e340aa97b7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used.
++  --> $DIR/used_underscore_binding.rs:26:5
++   |
++LL |     _foo + 1
++   |     ^^^^
++   |
++   = note: `-D clippy::used-underscore-binding` implied by `-D warnings`
++
++error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used.
++  --> $DIR/used_underscore_binding.rs:31:20
++   |
++LL |     println!("{}", _foo);
++   |                    ^^^^
++
++error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used.
++  --> $DIR/used_underscore_binding.rs:32:16
++   |
++LL |     assert_eq!(_foo, _foo);
++   |                ^^^^
++
++error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used.
++  --> $DIR/used_underscore_binding.rs:32:22
++   |
++LL |     assert_eq!(_foo, _foo);
++   |                      ^^^^
++
++error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used.
++  --> $DIR/used_underscore_binding.rs:45:5
++   |
++LL |     s._underscore_field += 1;
++   |     ^^^^^^^^^^^^^^^^^^^
++
++error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used.
++  --> $DIR/used_underscore_binding.rs:100:16
++   |
++LL |         uses_i(_i);
++   |                ^^
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a9f0170a79cd90d27b1ace750d7ea945d50e2d39
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++#![deny(clippy::useless_asref)]
++
++trait Trait {
++    fn as_ptr(&self);
++}
++
++impl<'a> Trait for &'a [u8] {
++    fn as_ptr(&self) {
++        self.as_ref().as_ptr();
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e356f13d087b1fcdeffbb7a3c4f15d52b55b183e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,135 @@@
++// run-rustfix
++
++#![deny(clippy::useless_asref)]
++
++use std::fmt::Debug;
++
++struct FakeAsRef;
++
++#[allow(clippy::should_implement_trait)]
++impl FakeAsRef {
++    fn as_ref(&self) -> &Self {
++        self
++    }
++}
++
++struct MoreRef;
++
++impl<'a, 'b, 'c> AsRef<&'a &'b &'c MoreRef> for MoreRef {
++    fn as_ref(&self) -> &&'a &'b &'c MoreRef {
++        &&&&MoreRef
++    }
++}
++
++fn foo_rstr(x: &str) {
++    println!("{:?}", x);
++}
++fn foo_rslice(x: &[i32]) {
++    println!("{:?}", x);
++}
++fn foo_mrslice(x: &mut [i32]) {
++    println!("{:?}", x);
++}
++fn foo_rrrrmr(_: &&&&MoreRef) {
++    println!("so many refs");
++}
++
++fn not_ok() {
++    let rstr: &str = "hello";
++    let mut mrslice: &mut [i32] = &mut [1, 2, 3];
++
++    {
++        let rslice: &[i32] = &*mrslice;
++        foo_rstr(rstr);
++        foo_rstr(rstr);
++        foo_rslice(rslice);
++        foo_rslice(rslice);
++    }
++    {
++        foo_mrslice(mrslice);
++        foo_mrslice(mrslice);
++        foo_rslice(mrslice);
++        foo_rslice(mrslice);
++    }
++
++    {
++        let rrrrrstr = &&&&rstr;
++        let rrrrrslice = &&&&&*mrslice;
++        foo_rslice(rrrrrslice);
++        foo_rslice(rrrrrslice);
++        foo_rstr(rrrrrstr);
++        foo_rstr(rrrrrstr);
++    }
++    {
++        let mrrrrrslice = &mut &mut &mut &mut mrslice;
++        foo_mrslice(mrrrrrslice);
++        foo_mrslice(mrrrrrslice);
++        foo_rslice(mrrrrrslice);
++        foo_rslice(mrrrrrslice);
++    }
++    #[allow(unused_parens, clippy::double_parens)]
++    foo_rrrrmr((&&&&MoreRef));
++
++    generic_not_ok(mrslice);
++    generic_ok(mrslice);
++}
++
++fn ok() {
++    let string = "hello".to_owned();
++    let mut arr = [1, 2, 3];
++    let mut vec = vec![1, 2, 3];
++
++    {
++        foo_rstr(string.as_ref());
++        foo_rslice(arr.as_ref());
++        foo_rslice(vec.as_ref());
++    }
++    {
++        foo_mrslice(arr.as_mut());
++        foo_mrslice(vec.as_mut());
++    }
++
++    {
++        let rrrrstring = &&&&string;
++        let rrrrarr = &&&&arr;
++        let rrrrvec = &&&&vec;
++        foo_rstr(rrrrstring.as_ref());
++        foo_rslice(rrrrarr.as_ref());
++        foo_rslice(rrrrvec.as_ref());
++    }
++    {
++        let mrrrrarr = &mut &mut &mut &mut arr;
++        let mrrrrvec = &mut &mut &mut &mut vec;
++        foo_mrslice(mrrrrarr.as_mut());
++        foo_mrslice(mrrrrvec.as_mut());
++    }
++    FakeAsRef.as_ref();
++    foo_rrrrmr(MoreRef.as_ref());
++
++    generic_not_ok(arr.as_mut());
++    generic_ok(&mut arr);
++}
++
++fn foo_mrt<T: Debug + ?Sized>(t: &mut T) {
++    println!("{:?}", t);
++}
++fn foo_rt<T: Debug + ?Sized>(t: &T) {
++    println!("{:?}", t);
++}
++
++fn generic_not_ok<T: AsMut<T> + AsRef<T> + Debug + ?Sized>(mrt: &mut T) {
++    foo_mrt(mrt);
++    foo_mrt(mrt);
++    foo_rt(mrt);
++    foo_rt(mrt);
++}
++
++fn generic_ok<U: AsMut<T> + AsRef<T> + ?Sized, T: Debug + ?Sized>(mru: &mut U) {
++    foo_mrt(mru.as_mut());
++    foo_rt(mru.as_ref());
++}
++
++fn main() {
++    not_ok();
++    ok();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2a80291f5d837b4614de012ac60f9fb1d2b9b256
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,135 @@@
++// run-rustfix
++
++#![deny(clippy::useless_asref)]
++
++use std::fmt::Debug;
++
++struct FakeAsRef;
++
++#[allow(clippy::should_implement_trait)]
++impl FakeAsRef {
++    fn as_ref(&self) -> &Self {
++        self
++    }
++}
++
++struct MoreRef;
++
++impl<'a, 'b, 'c> AsRef<&'a &'b &'c MoreRef> for MoreRef {
++    fn as_ref(&self) -> &&'a &'b &'c MoreRef {
++        &&&&MoreRef
++    }
++}
++
++fn foo_rstr(x: &str) {
++    println!("{:?}", x);
++}
++fn foo_rslice(x: &[i32]) {
++    println!("{:?}", x);
++}
++fn foo_mrslice(x: &mut [i32]) {
++    println!("{:?}", x);
++}
++fn foo_rrrrmr(_: &&&&MoreRef) {
++    println!("so many refs");
++}
++
++fn not_ok() {
++    let rstr: &str = "hello";
++    let mut mrslice: &mut [i32] = &mut [1, 2, 3];
++
++    {
++        let rslice: &[i32] = &*mrslice;
++        foo_rstr(rstr.as_ref());
++        foo_rstr(rstr);
++        foo_rslice(rslice.as_ref());
++        foo_rslice(rslice);
++    }
++    {
++        foo_mrslice(mrslice.as_mut());
++        foo_mrslice(mrslice);
++        foo_rslice(mrslice.as_ref());
++        foo_rslice(mrslice);
++    }
++
++    {
++        let rrrrrstr = &&&&rstr;
++        let rrrrrslice = &&&&&*mrslice;
++        foo_rslice(rrrrrslice.as_ref());
++        foo_rslice(rrrrrslice);
++        foo_rstr(rrrrrstr.as_ref());
++        foo_rstr(rrrrrstr);
++    }
++    {
++        let mrrrrrslice = &mut &mut &mut &mut mrslice;
++        foo_mrslice(mrrrrrslice.as_mut());
++        foo_mrslice(mrrrrrslice);
++        foo_rslice(mrrrrrslice.as_ref());
++        foo_rslice(mrrrrrslice);
++    }
++    #[allow(unused_parens, clippy::double_parens)]
++    foo_rrrrmr((&&&&MoreRef).as_ref());
++
++    generic_not_ok(mrslice);
++    generic_ok(mrslice);
++}
++
++fn ok() {
++    let string = "hello".to_owned();
++    let mut arr = [1, 2, 3];
++    let mut vec = vec![1, 2, 3];
++
++    {
++        foo_rstr(string.as_ref());
++        foo_rslice(arr.as_ref());
++        foo_rslice(vec.as_ref());
++    }
++    {
++        foo_mrslice(arr.as_mut());
++        foo_mrslice(vec.as_mut());
++    }
++
++    {
++        let rrrrstring = &&&&string;
++        let rrrrarr = &&&&arr;
++        let rrrrvec = &&&&vec;
++        foo_rstr(rrrrstring.as_ref());
++        foo_rslice(rrrrarr.as_ref());
++        foo_rslice(rrrrvec.as_ref());
++    }
++    {
++        let mrrrrarr = &mut &mut &mut &mut arr;
++        let mrrrrvec = &mut &mut &mut &mut vec;
++        foo_mrslice(mrrrrarr.as_mut());
++        foo_mrslice(mrrrrvec.as_mut());
++    }
++    FakeAsRef.as_ref();
++    foo_rrrrmr(MoreRef.as_ref());
++
++    generic_not_ok(arr.as_mut());
++    generic_ok(&mut arr);
++}
++
++fn foo_mrt<T: Debug + ?Sized>(t: &mut T) {
++    println!("{:?}", t);
++}
++fn foo_rt<T: Debug + ?Sized>(t: &T) {
++    println!("{:?}", t);
++}
++
++fn generic_not_ok<T: AsMut<T> + AsRef<T> + Debug + ?Sized>(mrt: &mut T) {
++    foo_mrt(mrt.as_mut());
++    foo_mrt(mrt);
++    foo_rt(mrt.as_ref());
++    foo_rt(mrt);
++}
++
++fn generic_ok<U: AsMut<T> + AsRef<T> + ?Sized, T: Debug + ?Sized>(mru: &mut U) {
++    foo_mrt(mru.as_mut());
++    foo_rt(mru.as_ref());
++}
++
++fn main() {
++    not_ok();
++    ok();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5876b54aca8f0e8347995ae6c6ead836768b6a7c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,74 @@@
++error: this call to `as_ref` does nothing
++  --> $DIR/useless_asref.rs:43:18
++   |
++LL |         foo_rstr(rstr.as_ref());
++   |                  ^^^^^^^^^^^^^ help: try this: `rstr`
++   |
++note: the lint level is defined here
++  --> $DIR/useless_asref.rs:3:9
++   |
++LL | #![deny(clippy::useless_asref)]
++   |         ^^^^^^^^^^^^^^^^^^^^^
++
++error: this call to `as_ref` does nothing
++  --> $DIR/useless_asref.rs:45:20
++   |
++LL |         foo_rslice(rslice.as_ref());
++   |                    ^^^^^^^^^^^^^^^ help: try this: `rslice`
++
++error: this call to `as_mut` does nothing
++  --> $DIR/useless_asref.rs:49:21
++   |
++LL |         foo_mrslice(mrslice.as_mut());
++   |                     ^^^^^^^^^^^^^^^^ help: try this: `mrslice`
++
++error: this call to `as_ref` does nothing
++  --> $DIR/useless_asref.rs:51:20
++   |
++LL |         foo_rslice(mrslice.as_ref());
++   |                    ^^^^^^^^^^^^^^^^ help: try this: `mrslice`
++
++error: this call to `as_ref` does nothing
++  --> $DIR/useless_asref.rs:58:20
++   |
++LL |         foo_rslice(rrrrrslice.as_ref());
++   |                    ^^^^^^^^^^^^^^^^^^^ help: try this: `rrrrrslice`
++
++error: this call to `as_ref` does nothing
++  --> $DIR/useless_asref.rs:60:18
++   |
++LL |         foo_rstr(rrrrrstr.as_ref());
++   |                  ^^^^^^^^^^^^^^^^^ help: try this: `rrrrrstr`
++
++error: this call to `as_mut` does nothing
++  --> $DIR/useless_asref.rs:65:21
++   |
++LL |         foo_mrslice(mrrrrrslice.as_mut());
++   |                     ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice`
++
++error: this call to `as_ref` does nothing
++  --> $DIR/useless_asref.rs:67:20
++   |
++LL |         foo_rslice(mrrrrrslice.as_ref());
++   |                    ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice`
++
++error: this call to `as_ref` does nothing
++  --> $DIR/useless_asref.rs:71:16
++   |
++LL |     foo_rrrrmr((&&&&MoreRef).as_ref());
++   |                ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(&&&&MoreRef)`
++
++error: this call to `as_mut` does nothing
++  --> $DIR/useless_asref.rs:121:13
++   |
++LL |     foo_mrt(mrt.as_mut());
++   |             ^^^^^^^^^^^^ help: try this: `mrt`
++
++error: this call to `as_ref` does nothing
++  --> $DIR/useless_asref.rs:123:12
++   |
++LL |     foo_rt(mrt.as_ref());
++   |            ^^^^^^^^^^^^ help: try this: `mrt`
++
++error: aborting due to 11 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b222e2f7976d5a0b953fbeee44669608efff24ff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++// run-rustfix
++// aux-build:proc_macro_derive.rs
++
++#![warn(clippy::useless_attribute)]
++#![warn(unreachable_pub)]
++#![feature(rustc_private)]
++
++#![allow(dead_code)]
++#![cfg_attr(feature = "cargo-clippy", allow(dead_code))]
++#[rustfmt::skip]
++#[allow(unused_imports)]
++#[allow(unused_extern_crates)]
++#[macro_use]
++extern crate rustc_middle;
++
++#[macro_use]
++extern crate proc_macro_derive;
++
++// don't lint on unused_import for `use` items
++#[allow(unused_imports)]
++use std::collections;
++
++// don't lint on unused for `use` items
++#[allow(unused)]
++use std::option;
++
++// don't lint on deprecated for `use` items
++mod foo {
++    #[deprecated]
++    pub struct Bar;
++}
++#[allow(deprecated)]
++pub use foo::Bar;
++
++// This should not trigger the lint. There's lint level definitions inside the external derive
++// that would trigger the useless_attribute lint.
++#[derive(DeriveSomething)]
++struct Baz;
++
++// don't lint on unreachable_pub for `use` items
++mod a {
++    mod b {
++        #[allow(dead_code)]
++        #[allow(unreachable_pub)]
++        pub struct C {}
++    }
++
++    #[allow(unreachable_pub)]
++    pub use self::b::C;
++}
++
++fn test_indented_attr() {
++    #![allow(clippy::almost_swapped)]
++    use std::collections::HashSet;
++
++    let _ = HashSet::<u32>::default();
++}
++
++fn main() {
++    test_indented_attr();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3422eace4ab97d81613ab2d27a3082fb174cd2ea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++// run-rustfix
++// aux-build:proc_macro_derive.rs
++
++#![warn(clippy::useless_attribute)]
++#![warn(unreachable_pub)]
++#![feature(rustc_private)]
++
++#[allow(dead_code)]
++#[cfg_attr(feature = "cargo-clippy", allow(dead_code))]
++#[rustfmt::skip]
++#[allow(unused_imports)]
++#[allow(unused_extern_crates)]
++#[macro_use]
++extern crate rustc_middle;
++
++#[macro_use]
++extern crate proc_macro_derive;
++
++// don't lint on unused_import for `use` items
++#[allow(unused_imports)]
++use std::collections;
++
++// don't lint on unused for `use` items
++#[allow(unused)]
++use std::option;
++
++// don't lint on deprecated for `use` items
++mod foo {
++    #[deprecated]
++    pub struct Bar;
++}
++#[allow(deprecated)]
++pub use foo::Bar;
++
++// This should not trigger the lint. There's lint level definitions inside the external derive
++// that would trigger the useless_attribute lint.
++#[derive(DeriveSomething)]
++struct Baz;
++
++// don't lint on unreachable_pub for `use` items
++mod a {
++    mod b {
++        #[allow(dead_code)]
++        #[allow(unreachable_pub)]
++        pub struct C {}
++    }
++
++    #[allow(unreachable_pub)]
++    pub use self::b::C;
++}
++
++fn test_indented_attr() {
++    #[allow(clippy::almost_swapped)]
++    use std::collections::HashSet;
++
++    let _ = HashSet::<u32>::default();
++}
++
++fn main() {
++    test_indented_attr();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..57ba976730c172f2e2e06fb55decfb36ca8d9d01
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: useless lint attribute
++  --> $DIR/useless_attribute.rs:8:1
++   |
++LL | #[allow(dead_code)]
++   | ^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(dead_code)]`
++   |
++   = note: `-D clippy::useless-attribute` implied by `-D warnings`
++
++error: useless lint attribute
++  --> $DIR/useless_attribute.rs:9:1
++   |
++LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))]
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)`
++
++error: useless lint attribute
++  --> $DIR/useless_attribute.rs:53:5
++   |
++LL |     #[allow(clippy::almost_swapped)]
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e73a791891f890664cf3eddc2f60b63040c4e995
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++// run-rustfix
++
++#![warn(clippy::useless_vec)]
++
++#[derive(Debug)]
++struct NonCopy;
++
++fn on_slice(_: &[u8]) {}
++#[allow(clippy::ptr_arg)]
++fn on_vec(_: &Vec<u8>) {}
++
++struct Line {
++    length: usize,
++}
++
++impl Line {
++    fn length(&self) -> usize {
++        self.length
++    }
++}
++
++fn main() {
++    on_slice(&[]);
++    on_slice(&[]);
++
++    on_slice(&[1, 2]);
++    on_slice(&[1, 2]);
++
++    on_slice(&[1, 2]);
++    on_slice(&[1, 2]);
++    #[rustfmt::skip]
++    on_slice(&[1, 2]);
++    on_slice(&[1, 2]);
++
++    on_slice(&[1; 2]);
++    on_slice(&[1; 2]);
++
++    on_vec(&vec![]);
++    on_vec(&vec![1, 2]);
++    on_vec(&vec![1; 2]);
++
++    // Now with non-constant expressions
++    let line = Line { length: 2 };
++
++    on_slice(&vec![2; line.length]);
++    on_slice(&vec![2; line.length()]);
++
++    for a in &[1, 2, 3] {
++        println!("{:?}", a);
++    }
++
++    for a in vec![NonCopy, NonCopy] {
++        println!("{:?}", a);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3eb960f53d7af7dc33dc7baa14a51b36c7f65e05
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++// run-rustfix
++
++#![warn(clippy::useless_vec)]
++
++#[derive(Debug)]
++struct NonCopy;
++
++fn on_slice(_: &[u8]) {}
++#[allow(clippy::ptr_arg)]
++fn on_vec(_: &Vec<u8>) {}
++
++struct Line {
++    length: usize,
++}
++
++impl Line {
++    fn length(&self) -> usize {
++        self.length
++    }
++}
++
++fn main() {
++    on_slice(&vec![]);
++    on_slice(&[]);
++
++    on_slice(&vec![1, 2]);
++    on_slice(&[1, 2]);
++
++    on_slice(&vec![1, 2]);
++    on_slice(&[1, 2]);
++    #[rustfmt::skip]
++    on_slice(&vec!(1, 2));
++    on_slice(&[1, 2]);
++
++    on_slice(&vec![1; 2]);
++    on_slice(&[1; 2]);
++
++    on_vec(&vec![]);
++    on_vec(&vec![1, 2]);
++    on_vec(&vec![1; 2]);
++
++    // Now with non-constant expressions
++    let line = Line { length: 2 };
++
++    on_slice(&vec![2; line.length]);
++    on_slice(&vec![2; line.length()]);
++
++    for a in vec![1, 2, 3] {
++        println!("{:?}", a);
++    }
++
++    for a in vec![NonCopy, NonCopy] {
++        println!("{:?}", a);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..37e28ebddb55391cddcbbfccca830ffe30aff268
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: useless use of `vec!`
++  --> $DIR/vec.rs:23:14
++   |
++LL |     on_slice(&vec![]);
++   |              ^^^^^^^ help: you can use a slice directly: `&[]`
++   |
++   = note: `-D clippy::useless-vec` implied by `-D warnings`
++
++error: useless use of `vec!`
++  --> $DIR/vec.rs:26:14
++   |
++LL |     on_slice(&vec![1, 2]);
++   |              ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]`
++
++error: useless use of `vec!`
++  --> $DIR/vec.rs:29:14
++   |
++LL |     on_slice(&vec![1, 2]);
++   |              ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]`
++
++error: useless use of `vec!`
++  --> $DIR/vec.rs:32:14
++   |
++LL |     on_slice(&vec!(1, 2));
++   |              ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]`
++
++error: useless use of `vec!`
++  --> $DIR/vec.rs:35:14
++   |
++LL |     on_slice(&vec![1; 2]);
++   |              ^^^^^^^^^^^ help: you can use a slice directly: `&[1; 2]`
++
++error: useless use of `vec!`
++  --> $DIR/vec.rs:48:14
++   |
++LL |     for a in vec![1, 2, 3] {
++   |              ^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2, 3]`
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d0bee2460dd843993f9bd2d9035446f803638cb6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++// run-rustfix
++
++#![allow(dead_code)]
++
++struct SizedStruct(i32);
++struct UnsizedStruct([i32]);
++struct BigStruct([i32; 10000]);
++
++/// The following should trigger the lint
++mod should_trigger {
++    use super::SizedStruct;
++
++    struct StructWithVecBox {
++        sized_type: Vec<SizedStruct>,
++    }
++
++    struct A(Vec<SizedStruct>);
++    struct B(Vec<Vec<u32>>);
++}
++
++/// The following should not trigger the lint
++mod should_not_trigger {
++    use super::{BigStruct, UnsizedStruct};
++
++    struct C(Vec<Box<UnsizedStruct>>);
++    struct D(Vec<Box<BigStruct>>);
++
++    struct StructWithVecBoxButItsUnsized {
++        unsized_type: Vec<Box<UnsizedStruct>>,
++    }
++
++    struct TraitVec<T: ?Sized> {
++        // Regression test for #3720. This was causing an ICE.
++        inner: Vec<Box<T>>,
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..500a0ae263ea509a8be6ba1d205000908fe3f766
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++// run-rustfix
++
++#![allow(dead_code)]
++
++struct SizedStruct(i32);
++struct UnsizedStruct([i32]);
++struct BigStruct([i32; 10000]);
++
++/// The following should trigger the lint
++mod should_trigger {
++    use super::SizedStruct;
++
++    struct StructWithVecBox {
++        sized_type: Vec<Box<SizedStruct>>,
++    }
++
++    struct A(Vec<Box<SizedStruct>>);
++    struct B(Vec<Vec<Box<(u32)>>>);
++}
++
++/// The following should not trigger the lint
++mod should_not_trigger {
++    use super::{BigStruct, UnsizedStruct};
++
++    struct C(Vec<Box<UnsizedStruct>>);
++    struct D(Vec<Box<BigStruct>>);
++
++    struct StructWithVecBoxButItsUnsized {
++        unsized_type: Vec<Box<UnsizedStruct>>,
++    }
++
++    struct TraitVec<T: ?Sized> {
++        // Regression test for #3720. This was causing an ICE.
++        inner: Vec<Box<T>>,
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..29bf7069e8adb84c68f55eefe214a2bae8381a6e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: `Vec<T>` is already on the heap, the boxing is unnecessary.
++  --> $DIR/vec_box_sized.rs:14:21
++   |
++LL |         sized_type: Vec<Box<SizedStruct>>,
++   |                     ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<SizedStruct>`
++   |
++   = note: `-D clippy::vec-box` implied by `-D warnings`
++
++error: `Vec<T>` is already on the heap, the boxing is unnecessary.
++  --> $DIR/vec_box_sized.rs:17:14
++   |
++LL |     struct A(Vec<Box<SizedStruct>>);
++   |              ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<SizedStruct>`
++
++error: `Vec<T>` is already on the heap, the boxing is unnecessary.
++  --> $DIR/vec_box_sized.rs:18:18
++   |
++LL |     struct B(Vec<Vec<Box<(u32)>>>);
++   |                  ^^^^^^^^^^^^^^^ help: try: `Vec<u32>`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e0065e05ade62d31ba9c21f7fe8b75f2b368d973
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++#![warn(clippy::verbose_file_reads)]
++use std::env::temp_dir;
++use std::fs::File;
++use std::io::Read;
++
++struct Struct;
++// To make sure we only warn on File::{read_to_end, read_to_string} calls
++impl Struct {
++    pub fn read_to_end(&self) {}
++
++    pub fn read_to_string(&self) {}
++}
++
++fn main() -> std::io::Result<()> {
++    let path = "foo.txt";
++    // Lint shouldn't catch this
++    let s = Struct;
++    s.read_to_end();
++    s.read_to_string();
++    // Should catch this
++    let mut f = File::open(&path)?;
++    let mut buffer = Vec::new();
++    f.read_to_end(&mut buffer)?;
++    // ...and this
++    let mut string_buffer = String::new();
++    f.read_to_string(&mut string_buffer)?;
++    Ok(())
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..550b6ab679f19c7ec09a3aa505a6e20713304c4d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++error: use of `File::read_to_end`
++  --> $DIR/verbose_file_reads.rs:23:5
++   |
++LL |     f.read_to_end(&mut buffer)?;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::verbose-file-reads` implied by `-D warnings`
++   = help: consider using `fs::read` instead
++
++error: use of `File::read_to_string`
++  --> $DIR/verbose_file_reads.rs:26:5
++   |
++LL |     f.read_to_string(&mut string_buffer)?;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider using `fs::read_to_string` instead
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c91d96ee18a31972c1856196c149856634d8dbba
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++use std::fmt::Debug;
++use std::ptr;
++use std::rc::Rc;
++use std::sync::Arc;
++
++#[warn(clippy::vtable_address_comparisons)]
++fn main() {
++    let a: *const dyn Debug = &1 as &dyn Debug;
++    let b: *const dyn Debug = &1 as &dyn Debug;
++
++    // These should fail:
++    let _ = a == b;
++    let _ = a != b;
++    let _ = a < b;
++    let _ = a <= b;
++    let _ = a > b;
++    let _ = a >= b;
++    ptr::eq(a, b);
++
++    let a = &1 as &dyn Debug;
++    let b = &1 as &dyn Debug;
++    ptr::eq(a, b);
++
++    let a: Rc<dyn Debug> = Rc::new(1);
++    Rc::ptr_eq(&a, &a);
++
++    let a: Arc<dyn Debug> = Arc::new(1);
++    Arc::ptr_eq(&a, &a);
++
++    // These should be fine:
++    let a = &1;
++    ptr::eq(a, a);
++
++    let a = Rc::new(1);
++    Rc::ptr_eq(&a, &a);
++
++    let a = Arc::new(1);
++    Arc::ptr_eq(&a, &a);
++
++    let a: &[u8] = b"";
++    ptr::eq(a, a);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..76bd57217d784323c9a86f2b2881f033daad7e13
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,83 @@@
++error: comparing trait object pointers compares a non-unique vtable address
++  --> $DIR/vtable_address_comparisons.rs:12:13
++   |
++LL |     let _ = a == b;
++   |             ^^^^^^
++   |
++   = note: `-D clippy::vtable-address-comparisons` implied by `-D warnings`
++   = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++  --> $DIR/vtable_address_comparisons.rs:13:13
++   |
++LL |     let _ = a != b;
++   |             ^^^^^^
++   |
++   = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++  --> $DIR/vtable_address_comparisons.rs:14:13
++   |
++LL |     let _ = a < b;
++   |             ^^^^^
++   |
++   = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++  --> $DIR/vtable_address_comparisons.rs:15:13
++   |
++LL |     let _ = a <= b;
++   |             ^^^^^^
++   |
++   = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++  --> $DIR/vtable_address_comparisons.rs:16:13
++   |
++LL |     let _ = a > b;
++   |             ^^^^^
++   |
++   = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++  --> $DIR/vtable_address_comparisons.rs:17:13
++   |
++LL |     let _ = a >= b;
++   |             ^^^^^^
++   |
++   = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++  --> $DIR/vtable_address_comparisons.rs:18:5
++   |
++LL |     ptr::eq(a, b);
++   |     ^^^^^^^^^^^^^
++   |
++   = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++  --> $DIR/vtable_address_comparisons.rs:22:5
++   |
++LL |     ptr::eq(a, b);
++   |     ^^^^^^^^^^^^^
++   |
++   = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++  --> $DIR/vtable_address_comparisons.rs:25:5
++   |
++LL |     Rc::ptr_eq(&a, &a);
++   |     ^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider extracting and comparing data pointers only
++
++error: comparing trait object pointers compares a non-unique vtable address
++  --> $DIR/vtable_address_comparisons.rs:28:5
++   |
++LL |     Arc::ptr_eq(&a, &a);
++   |     ^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider extracting and comparing data pointers only
++
++error: aborting due to 10 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3ce699f551b209c122e9bc3eb3b59f3c9ce41698
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,119 @@@
++#![warn(clippy::while_let_loop)]
++
++fn main() {
++    let y = Some(true);
++    loop {
++        if let Some(_x) = y {
++            let _v = 1;
++        } else {
++            break;
++        }
++    }
++
++    #[allow(clippy::never_loop)]
++    loop {
++        // no error, break is not in else clause
++        if let Some(_x) = y {
++            let _v = 1;
++        }
++        break;
++    }
++
++    loop {
++        match y {
++            Some(_x) => true,
++            None => break,
++        };
++    }
++
++    loop {
++        let x = match y {
++            Some(x) => x,
++            None => break,
++        };
++        let _x = x;
++        let _str = "foo";
++    }
++
++    loop {
++        let x = match y {
++            Some(x) => x,
++            None => break,
++        };
++        {
++            let _a = "bar";
++        };
++        {
++            let _b = "foobar";
++        }
++    }
++
++    loop {
++        // no error, else branch does something other than break
++        match y {
++            Some(_x) => true,
++            _ => {
++                let _z = 1;
++                break;
++            },
++        };
++    }
++
++    while let Some(x) = y {
++        // no error, obviously
++        println!("{}", x);
++    }
++
++    // #675, this used to have a wrong suggestion
++    loop {
++        let (e, l) = match "".split_whitespace().next() {
++            Some(word) => (word.is_empty(), word.len()),
++            None => break,
++        };
++
++        let _ = (e, l);
++    }
++}
++
++fn issue771() {
++    let mut a = 100;
++    let b = Some(true);
++    loop {
++        if a > 10 {
++            break;
++        }
++
++        match b {
++            Some(_) => a = 0,
++            None => break,
++        }
++    }
++}
++
++fn issue1017() {
++    let r: Result<u32, u32> = Ok(42);
++    let mut len = 1337;
++
++    loop {
++        match r {
++            Err(_) => len = 0,
++            Ok(length) => {
++                len = length;
++                break;
++            },
++        }
++    }
++}
++
++#[allow(clippy::never_loop)]
++fn issue1948() {
++    // should not trigger clippy::while_let_loop lint because break passes an expression
++    let a = Some(10);
++    let b = loop {
++        if let Some(c) = a {
++            break Some(c);
++        } else {
++            break None;
++        }
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..13dd0ee224c10e74ca3f6a4254dcef94338c5c7e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++error: this loop could be written as a `while let` loop
++  --> $DIR/while_let_loop.rs:5:5
++   |
++LL | /     loop {
++LL | |         if let Some(_x) = y {
++LL | |             let _v = 1;
++LL | |         } else {
++LL | |             break;
++LL | |         }
++LL | |     }
++   | |_____^ help: try: `while let Some(_x) = y { .. }`
++   |
++   = note: `-D clippy::while-let-loop` implied by `-D warnings`
++
++error: this loop could be written as a `while let` loop
++  --> $DIR/while_let_loop.rs:22:5
++   |
++LL | /     loop {
++LL | |         match y {
++LL | |             Some(_x) => true,
++LL | |             None => break,
++LL | |         };
++LL | |     }
++   | |_____^ help: try: `while let Some(_x) = y { .. }`
++
++error: this loop could be written as a `while let` loop
++  --> $DIR/while_let_loop.rs:29:5
++   |
++LL | /     loop {
++LL | |         let x = match y {
++LL | |             Some(x) => x,
++LL | |             None => break,
++...  |
++LL | |         let _str = "foo";
++LL | |     }
++   | |_____^ help: try: `while let Some(x) = y { .. }`
++
++error: this loop could be written as a `while let` loop
++  --> $DIR/while_let_loop.rs:38:5
++   |
++LL | /     loop {
++LL | |         let x = match y {
++LL | |             Some(x) => x,
++LL | |             None => break,
++...  |
++LL | |         }
++LL | |     }
++   | |_____^ help: try: `while let Some(x) = y { .. }`
++
++error: this loop could be written as a `while let` loop
++  --> $DIR/while_let_loop.rs:68:5
++   |
++LL | /     loop {
++LL | |         let (e, l) = match "".split_whitespace().next() {
++LL | |             Some(word) => (word.is_empty(), word.len()),
++LL | |             None => break,
++...  |
++LL | |         let _ = (e, l);
++LL | |     }
++   | |_____^ help: try: `while let Some(word) = "".split_whitespace().next() { .. }`
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f5fcabf63fd392e6a5abee3fb5df203b86f2f312
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,160 @@@
++// run-rustfix
++
++#![warn(clippy::while_let_on_iterator)]
++#![allow(clippy::never_loop, unreachable_code, unused_mut)]
++
++fn base() {
++    let mut iter = 1..20;
++    for x in iter {
++        println!("{}", x);
++    }
++
++    let mut iter = 1..20;
++    for x in iter {
++        println!("{}", x);
++    }
++
++    let mut iter = 1..20;
++    for _ in iter {}
++
++    let mut iter = 1..20;
++    while let None = iter.next() {} // this is fine (if nonsensical)
++
++    let mut iter = 1..20;
++    if let Some(x) = iter.next() {
++        // also fine
++        println!("{}", x)
++    }
++
++    // the following shouldn't warn because it can't be written with a for loop
++    let mut iter = 1u32..20;
++    while let Some(_) = iter.next() {
++        println!("next: {:?}", iter.next())
++    }
++
++    // neither can this
++    let mut iter = 1u32..20;
++    while let Some(_) = iter.next() {
++        println!("next: {:?}", iter.next());
++    }
++
++    // or this
++    let mut iter = 1u32..20;
++    while let Some(_) = iter.next() {
++        break;
++    }
++    println!("Remaining iter {:?}", iter);
++
++    // or this
++    let mut iter = 1u32..20;
++    while let Some(_) = iter.next() {
++        iter = 1..20;
++    }
++}
++
++// Issue #1188
++fn refutable() {
++    let a = [42, 1337];
++    let mut b = a.iter();
++
++    // consume all the 42s
++    while let Some(&42) = b.next() {}
++
++    let a = [(1, 2, 3)];
++    let mut b = a.iter();
++
++    while let Some(&(1, 2, 3)) = b.next() {}
++
++    let a = [Some(42)];
++    let mut b = a.iter();
++
++    while let Some(&None) = b.next() {}
++
++    /* This gives “refutable pattern in `for` loop binding: `&_` not covered”
++    for &42 in b {}
++    for &(1, 2, 3) in b {}
++    for &Option::None in b.next() {}
++    // */
++}
++
++fn nested_loops() {
++    let a = [42, 1337];
++    let mut y = a.iter();
++    loop {
++        // x is reused, so don't lint here
++        while let Some(_) = y.next() {}
++    }
++
++    let mut y = a.iter();
++    for _ in 0..2 {
++        while let Some(_) = y.next() {
++            // y is reused, don't lint
++        }
++    }
++
++    loop {
++        let mut y = a.iter();
++        for _ in y {
++            // use a for loop here
++        }
++    }
++}
++
++fn issue1121() {
++    use std::collections::HashSet;
++    let mut values = HashSet::new();
++    values.insert(1);
++
++    while let Some(&value) = values.iter().next() {
++        values.remove(&value);
++    }
++}
++
++fn issue2965() {
++    // This should not cause an ICE and suggest:
++    //
++    // for _ in values.iter() {}
++    //
++    use std::collections::HashSet;
++    let mut values = HashSet::new();
++    values.insert(1);
++
++    while let Some(..) = values.iter().next() {}
++}
++
++fn issue3670() {
++    let array = [Some(0), None, Some(1)];
++    let mut iter = array.iter();
++
++    while let Some(elem) = iter.next() {
++        let _ = elem.or_else(|| *iter.next()?);
++    }
++}
++
++fn issue1654() {
++    // should not lint if the iterator is generated on every iteration
++    use std::collections::HashSet;
++    let mut values = HashSet::new();
++    values.insert(1);
++
++    while let Some(..) = values.iter().next() {
++        values.remove(&1);
++    }
++
++    while let Some(..) = values.iter().map(|x| x + 1).next() {}
++
++    let chars = "Hello, World!".char_indices();
++    while let Some((i, ch)) = chars.clone().next() {
++        println!("{}: {}", i, ch);
++    }
++}
++
++fn main() {
++    base();
++    refutable();
++    nested_loops();
++    issue1121();
++    issue2965();
++    issue3670();
++    issue1654();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..04dce8a0289845b0c86518bf4cda1b7fc7383e10
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,160 @@@
++// run-rustfix
++
++#![warn(clippy::while_let_on_iterator)]
++#![allow(clippy::never_loop, unreachable_code, unused_mut)]
++
++fn base() {
++    let mut iter = 1..20;
++    while let Option::Some(x) = iter.next() {
++        println!("{}", x);
++    }
++
++    let mut iter = 1..20;
++    while let Some(x) = iter.next() {
++        println!("{}", x);
++    }
++
++    let mut iter = 1..20;
++    while let Some(_) = iter.next() {}
++
++    let mut iter = 1..20;
++    while let None = iter.next() {} // this is fine (if nonsensical)
++
++    let mut iter = 1..20;
++    if let Some(x) = iter.next() {
++        // also fine
++        println!("{}", x)
++    }
++
++    // the following shouldn't warn because it can't be written with a for loop
++    let mut iter = 1u32..20;
++    while let Some(_) = iter.next() {
++        println!("next: {:?}", iter.next())
++    }
++
++    // neither can this
++    let mut iter = 1u32..20;
++    while let Some(_) = iter.next() {
++        println!("next: {:?}", iter.next());
++    }
++
++    // or this
++    let mut iter = 1u32..20;
++    while let Some(_) = iter.next() {
++        break;
++    }
++    println!("Remaining iter {:?}", iter);
++
++    // or this
++    let mut iter = 1u32..20;
++    while let Some(_) = iter.next() {
++        iter = 1..20;
++    }
++}
++
++// Issue #1188
++fn refutable() {
++    let a = [42, 1337];
++    let mut b = a.iter();
++
++    // consume all the 42s
++    while let Some(&42) = b.next() {}
++
++    let a = [(1, 2, 3)];
++    let mut b = a.iter();
++
++    while let Some(&(1, 2, 3)) = b.next() {}
++
++    let a = [Some(42)];
++    let mut b = a.iter();
++
++    while let Some(&None) = b.next() {}
++
++    /* This gives “refutable pattern in `for` loop binding: `&_` not covered”
++    for &42 in b {}
++    for &(1, 2, 3) in b {}
++    for &Option::None in b.next() {}
++    // */
++}
++
++fn nested_loops() {
++    let a = [42, 1337];
++    let mut y = a.iter();
++    loop {
++        // x is reused, so don't lint here
++        while let Some(_) = y.next() {}
++    }
++
++    let mut y = a.iter();
++    for _ in 0..2 {
++        while let Some(_) = y.next() {
++            // y is reused, don't lint
++        }
++    }
++
++    loop {
++        let mut y = a.iter();
++        while let Some(_) = y.next() {
++            // use a for loop here
++        }
++    }
++}
++
++fn issue1121() {
++    use std::collections::HashSet;
++    let mut values = HashSet::new();
++    values.insert(1);
++
++    while let Some(&value) = values.iter().next() {
++        values.remove(&value);
++    }
++}
++
++fn issue2965() {
++    // This should not cause an ICE and suggest:
++    //
++    // for _ in values.iter() {}
++    //
++    use std::collections::HashSet;
++    let mut values = HashSet::new();
++    values.insert(1);
++
++    while let Some(..) = values.iter().next() {}
++}
++
++fn issue3670() {
++    let array = [Some(0), None, Some(1)];
++    let mut iter = array.iter();
++
++    while let Some(elem) = iter.next() {
++        let _ = elem.or_else(|| *iter.next()?);
++    }
++}
++
++fn issue1654() {
++    // should not lint if the iterator is generated on every iteration
++    use std::collections::HashSet;
++    let mut values = HashSet::new();
++    values.insert(1);
++
++    while let Some(..) = values.iter().next() {
++        values.remove(&1);
++    }
++
++    while let Some(..) = values.iter().map(|x| x + 1).next() {}
++
++    let chars = "Hello, World!".char_indices();
++    while let Some((i, ch)) = chars.clone().next() {
++        println!("{}: {}", i, ch);
++    }
++}
++
++fn main() {
++    base();
++    refutable();
++    nested_loops();
++    issue1121();
++    issue2965();
++    issue3670();
++    issue1654();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6de138d7227b20b9f50a72f68dafe305b572a6d1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: this loop could be written as a `for` loop
++  --> $DIR/while_let_on_iterator.rs:8:5
++   |
++LL |     while let Option::Some(x) = iter.next() {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
++   |
++   = note: `-D clippy::while-let-on-iterator` implied by `-D warnings`
++
++error: this loop could be written as a `for` loop
++  --> $DIR/while_let_on_iterator.rs:13:5
++   |
++LL |     while let Some(x) = iter.next() {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
++
++error: this loop could be written as a `for` loop
++  --> $DIR/while_let_on_iterator.rs:18:5
++   |
++LL |     while let Some(_) = iter.next() {}
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter`
++
++error: this loop could be written as a `for` loop
++  --> $DIR/while_let_on_iterator.rs:97:9
++   |
++LL |         while let Some(_) = y.next() {
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y`
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ad600f125772f515964a78f96bafbe560c58997d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++#![warn(clippy::wildcard_in_or_patterns)]
++
++fn main() {
++    match "foo" {
++        "a" => {
++            dbg!("matched a");
++        },
++        "bar" | _ => {
++            dbg!("matched (bar or) wild");
++        },
++    };
++    match "foo" {
++        "a" => {
++            dbg!("matched a");
++        },
++        "bar" | "bar2" | _ => {
++            dbg!("matched (bar or bar2 or) wild");
++        },
++    };
++    match "foo" {
++        "a" => {
++            dbg!("matched a");
++        },
++        _ | "bar" | _ => {
++            dbg!("matched (bar or) wild");
++        },
++    };
++    match "foo" {
++        "a" => {
++            dbg!("matched a");
++        },
++        _ | "bar" => {
++            dbg!("matched (bar or) wild");
++        },
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..33c34cbbd4088bb23fb9b218fae9cb58b199d78c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++error: wildcard pattern covers any other pattern as it will match anyway.
++  --> $DIR/wild_in_or_pats.rs:8:9
++   |
++LL |         "bar" | _ => {
++   |         ^^^^^^^^^
++   |
++   = note: `-D clippy::wildcard-in-or-patterns` implied by `-D warnings`
++   = help: Consider handling `_` separately.
++
++error: wildcard pattern covers any other pattern as it will match anyway.
++  --> $DIR/wild_in_or_pats.rs:16:9
++   |
++LL |         "bar" | "bar2" | _ => {
++   |         ^^^^^^^^^^^^^^^^^^
++   |
++   = help: Consider handling `_` separately.
++
++error: wildcard pattern covers any other pattern as it will match anyway.
++  --> $DIR/wild_in_or_pats.rs:24:9
++   |
++LL |         _ | "bar" | _ => {
++   |         ^^^^^^^^^^^^^
++   |
++   = help: Consider handling `_` separately.
++
++error: wildcard pattern covers any other pattern as it will match anyway.
++  --> $DIR/wild_in_or_pats.rs:32:9
++   |
++LL |         _ | "bar" => {
++   |         ^^^^^^^^^
++   |
++   = help: Consider handling `_` separately.
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2aa24ea1156aaa5a0b70e1d1ef11c41992b12b0f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,101 @@@
++// run-rustfix
++
++#![deny(clippy::wildcard_enum_match_arm)]
++#![allow(
++    unreachable_code,
++    unused_variables,
++    dead_code,
++    clippy::single_match,
++    clippy::wildcard_in_or_patterns
++)]
++
++use std::io::ErrorKind;
++
++#[derive(Clone, Copy, Debug, Eq, PartialEq)]
++enum Color {
++    Red,
++    Green,
++    Blue,
++    Rgb(u8, u8, u8),
++    Cyan,
++}
++
++impl Color {
++    fn is_monochrome(self) -> bool {
++        match self {
++            Color::Red | Color::Green | Color::Blue => true,
++            Color::Rgb(r, g, b) => r | g == 0 || r | b == 0 || g | b == 0,
++            Color::Cyan => false,
++        }
++    }
++}
++
++fn main() {
++    let color = Color::Rgb(0, 0, 127);
++    match color {
++        Color::Red => println!("Red"),
++        Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan => eprintln!("Not red"),
++    };
++    match color {
++        Color::Red => println!("Red"),
++        _not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan => eprintln!("Not red"),
++    };
++    let _str = match color {
++        Color::Red => "Red".to_owned(),
++        not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan => format!("{:?}", not_red),
++    };
++    match color {
++        Color::Red => {},
++        Color::Green => {},
++        Color::Blue => {},
++        Color::Cyan => {},
++        c if c.is_monochrome() => {},
++        Color::Rgb(_, _, _) => {},
++    };
++    let _str = match color {
++        Color::Red => "Red",
++        c @ Color::Green | c @ Color::Blue | c @ Color::Rgb(_, _, _) | c @ Color::Cyan => "Not red",
++    };
++    match color {
++        Color::Rgb(r, _, _) if r > 0 => "Some red",
++        Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan => "No red",
++    };
++    match color {
++        Color::Red | Color::Green | Color::Blue | Color::Cyan => {},
++        Color::Rgb(..) => {},
++    };
++    let x: u8 = unimplemented!();
++    match x {
++        0 => {},
++        140 => {},
++        _ => {},
++    };
++    // We need to use an enum not defined in this test because non_exhaustive is ignored for the
++    // purposes of dead code analysis within a crate.
++    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 | _ => {},
++    }
++    match error_kind {
++        ErrorKind::NotFound => {},
++        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 => {},
++        _ => {},
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..07c93feaf284e7cc3c33135912d0d4f42e9eedc3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,101 @@@
++// run-rustfix
++
++#![deny(clippy::wildcard_enum_match_arm)]
++#![allow(
++    unreachable_code,
++    unused_variables,
++    dead_code,
++    clippy::single_match,
++    clippy::wildcard_in_or_patterns
++)]
++
++use std::io::ErrorKind;
++
++#[derive(Clone, Copy, Debug, Eq, PartialEq)]
++enum Color {
++    Red,
++    Green,
++    Blue,
++    Rgb(u8, u8, u8),
++    Cyan,
++}
++
++impl Color {
++    fn is_monochrome(self) -> bool {
++        match self {
++            Color::Red | Color::Green | Color::Blue => true,
++            Color::Rgb(r, g, b) => r | g == 0 || r | b == 0 || g | b == 0,
++            Color::Cyan => false,
++        }
++    }
++}
++
++fn main() {
++    let color = Color::Rgb(0, 0, 127);
++    match color {
++        Color::Red => println!("Red"),
++        _ => eprintln!("Not red"),
++    };
++    match color {
++        Color::Red => println!("Red"),
++        _not_red => eprintln!("Not red"),
++    };
++    let _str = match color {
++        Color::Red => "Red".to_owned(),
++        not_red => format!("{:?}", not_red),
++    };
++    match color {
++        Color::Red => {},
++        Color::Green => {},
++        Color::Blue => {},
++        Color::Cyan => {},
++        c if c.is_monochrome() => {},
++        Color::Rgb(_, _, _) => {},
++    };
++    let _str = match color {
++        Color::Red => "Red",
++        c @ Color::Green | c @ Color::Blue | c @ Color::Rgb(_, _, _) | c @ Color::Cyan => "Not red",
++    };
++    match color {
++        Color::Rgb(r, _, _) if r > 0 => "Some red",
++        _ => "No red",
++    };
++    match color {
++        Color::Red | Color::Green | Color::Blue | Color::Cyan => {},
++        Color::Rgb(..) => {},
++    };
++    let x: u8 = unimplemented!();
++    match x {
++        0 => {},
++        140 => {},
++        _ => {},
++    };
++    // We need to use an enum not defined in this test because non_exhaustive is ignored for the
++    // purposes of dead code analysis within a crate.
++    let error_kind = ErrorKind::NotFound;
++    match error_kind {
++        ErrorKind::NotFound => {},
++        _ => {},
++    }
++    match error_kind {
++        ErrorKind::NotFound => {},
++        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 => {},
++        _ => {},
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7151a5c5770bf3ad217c8eca4088df24cee6ecf6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++error: wildcard match will miss any future added variants
++  --> $DIR/wildcard_enum_match_arm.rs:37:9
++   |
++LL |         _ => eprintln!("Not red"),
++   |         ^ help: try this: `Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan`
++   |
++note: the lint level is defined here
++  --> $DIR/wildcard_enum_match_arm.rs:3:9
++   |
++LL | #![deny(clippy::wildcard_enum_match_arm)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: wildcard match will miss any future added variants
++  --> $DIR/wildcard_enum_match_arm.rs:41: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
++  --> $DIR/wildcard_enum_match_arm.rs:45: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
++  --> $DIR/wildcard_enum_match_arm.rs:61: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
++  --> $DIR/wildcard_enum_match_arm.rs:78: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 | _`
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ed6cc00ef0480a756a7f8282b971837cabf376ff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,157 @@@
++// run-rustfix
++// aux-build:wildcard_imports_helper.rs
++
++#![warn(clippy::wildcard_imports)]
++//#![allow(clippy::redundant_pub_crate)]
++#![allow(unused)]
++#![warn(unused_imports)]
++
++extern crate wildcard_imports_helper;
++
++use crate::fn_mod::foo;
++use crate::mod_mod::inner_mod;
++use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod};
++#[macro_use]
++use crate::struct_mod::{A, inner_struct_mod};
++
++#[allow(unused_imports)]
++use wildcard_imports_helper::inner::inner_for_self_import;
++use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar;
++use wildcard_imports_helper::{ExternA, extern_foo};
++
++use std::io::prelude::*;
++
++struct ReadFoo;
++
++impl Read for ReadFoo {
++    fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
++        Ok(0)
++    }
++}
++
++mod fn_mod {
++    pub fn foo() {}
++}
++
++mod mod_mod {
++    pub mod inner_mod {
++        pub fn foo() {}
++    }
++}
++
++mod multi_fn_mod {
++    pub fn multi_foo() {}
++    pub fn multi_bar() {}
++    pub fn multi_baz() {}
++    pub mod multi_inner_mod {
++        pub fn foo() {}
++    }
++}
++
++mod struct_mod {
++    pub struct A;
++    pub struct B;
++    pub mod inner_struct_mod {
++        pub struct C;
++    }
++
++    #[macro_export]
++    macro_rules! double_struct_import_test {
++        () => {
++            let _ = A;
++        };
++    }
++}
++
++fn main() {
++    foo();
++    multi_foo();
++    multi_bar();
++    multi_inner_mod::foo();
++    inner_mod::foo();
++    extern_foo();
++    inner_extern_bar();
++
++    let _ = A;
++    let _ = inner_struct_mod::C;
++    let _ = ExternA;
++
++    double_struct_import_test!();
++    double_struct_import_test!();
++}
++
++mod in_fn_test {
++    pub use self::inner_exported::*;
++    #[allow(unused_imports)]
++    pub(crate) use self::inner_exported2::*;
++
++    fn test_intern() {
++        use crate::fn_mod::foo;
++
++        foo();
++    }
++
++    fn test_extern() {
++        use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo};
++        use wildcard_imports_helper::{ExternA, extern_foo};
++
++        inner_for_self_import::inner_extern_foo();
++        inner_extern_foo();
++
++        extern_foo();
++
++        let _ = ExternA;
++    }
++
++    fn test_inner_nested() {
++        use self::{inner::inner_foo, inner2::inner_bar};
++
++        inner_foo();
++        inner_bar();
++    }
++
++    fn test_extern_reexported() {
++        use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported};
++
++        extern_exported();
++        let _ = ExternExportedStruct;
++        let _ = ExternExportedEnum::A;
++    }
++
++    mod inner_exported {
++        pub fn exported() {}
++        pub struct ExportedStruct;
++        pub enum ExportedEnum {
++            A,
++        }
++    }
++
++    mod inner_exported2 {
++        pub(crate) fn exported2() {}
++    }
++
++    mod inner {
++        pub fn inner_foo() {}
++    }
++
++    mod inner2 {
++        pub fn inner_bar() {}
++    }
++}
++
++fn test_reexported() {
++    use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported};
++
++    exported();
++    let _ = ExportedStruct;
++    let _ = ExportedEnum::A;
++}
++
++#[rustfmt::skip]
++fn test_weird_formatting() {
++    use crate:: in_fn_test::exported;
++    use crate:: fn_mod::foo;
++
++    exported();
++    foo();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c6d6efaece817fa53f80108ce725e4a498e24a5c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,158 @@@
++// run-rustfix
++// aux-build:wildcard_imports_helper.rs
++
++#![warn(clippy::wildcard_imports)]
++//#![allow(clippy::redundant_pub_crate)]
++#![allow(unused)]
++#![warn(unused_imports)]
++
++extern crate wildcard_imports_helper;
++
++use crate::fn_mod::*;
++use crate::mod_mod::*;
++use crate::multi_fn_mod::*;
++#[macro_use]
++use crate::struct_mod::*;
++
++#[allow(unused_imports)]
++use wildcard_imports_helper::inner::inner_for_self_import;
++use wildcard_imports_helper::inner::inner_for_self_import::*;
++use wildcard_imports_helper::*;
++
++use std::io::prelude::*;
++
++struct ReadFoo;
++
++impl Read for ReadFoo {
++    fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
++        Ok(0)
++    }
++}
++
++mod fn_mod {
++    pub fn foo() {}
++}
++
++mod mod_mod {
++    pub mod inner_mod {
++        pub fn foo() {}
++    }
++}
++
++mod multi_fn_mod {
++    pub fn multi_foo() {}
++    pub fn multi_bar() {}
++    pub fn multi_baz() {}
++    pub mod multi_inner_mod {
++        pub fn foo() {}
++    }
++}
++
++mod struct_mod {
++    pub struct A;
++    pub struct B;
++    pub mod inner_struct_mod {
++        pub struct C;
++    }
++
++    #[macro_export]
++    macro_rules! double_struct_import_test {
++        () => {
++            let _ = A;
++        };
++    }
++}
++
++fn main() {
++    foo();
++    multi_foo();
++    multi_bar();
++    multi_inner_mod::foo();
++    inner_mod::foo();
++    extern_foo();
++    inner_extern_bar();
++
++    let _ = A;
++    let _ = inner_struct_mod::C;
++    let _ = ExternA;
++
++    double_struct_import_test!();
++    double_struct_import_test!();
++}
++
++mod in_fn_test {
++    pub use self::inner_exported::*;
++    #[allow(unused_imports)]
++    pub(crate) use self::inner_exported2::*;
++
++    fn test_intern() {
++        use crate::fn_mod::*;
++
++        foo();
++    }
++
++    fn test_extern() {
++        use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
++        use wildcard_imports_helper::*;
++
++        inner_for_self_import::inner_extern_foo();
++        inner_extern_foo();
++
++        extern_foo();
++
++        let _ = ExternA;
++    }
++
++    fn test_inner_nested() {
++        use self::{inner::*, inner2::*};
++
++        inner_foo();
++        inner_bar();
++    }
++
++    fn test_extern_reexported() {
++        use wildcard_imports_helper::*;
++
++        extern_exported();
++        let _ = ExternExportedStruct;
++        let _ = ExternExportedEnum::A;
++    }
++
++    mod inner_exported {
++        pub fn exported() {}
++        pub struct ExportedStruct;
++        pub enum ExportedEnum {
++            A,
++        }
++    }
++
++    mod inner_exported2 {
++        pub(crate) fn exported2() {}
++    }
++
++    mod inner {
++        pub fn inner_foo() {}
++    }
++
++    mod inner2 {
++        pub fn inner_bar() {}
++    }
++}
++
++fn test_reexported() {
++    use crate::in_fn_test::*;
++
++    exported();
++    let _ = ExportedStruct;
++    let _ = ExportedEnum::A;
++}
++
++#[rustfmt::skip]
++fn test_weird_formatting() {
++    use crate:: in_fn_test::  * ;
++    use crate:: fn_mod::
++        *;
++
++    exported();
++    foo();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..050e4c6304f05b49640f208d94bf7b80c2f64e0f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,96 @@@
++error: usage of wildcard import
++  --> $DIR/wildcard_imports.rs:11:5
++   |
++LL | use crate::fn_mod::*;
++   |     ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
++   |
++   = note: `-D clippy::wildcard-imports` implied by `-D warnings`
++
++error: usage of wildcard import
++  --> $DIR/wildcard_imports.rs:12:5
++   |
++LL | use crate::mod_mod::*;
++   |     ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod`
++
++error: usage of wildcard import
++  --> $DIR/wildcard_imports.rs:13:5
++   |
++LL | use crate::multi_fn_mod::*;
++   |     ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}`
++
++error: usage of wildcard import
++  --> $DIR/wildcard_imports.rs:15:5
++   |
++LL | use crate::struct_mod::*;
++   |     ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}`
++
++error: usage of wildcard import
++  --> $DIR/wildcard_imports.rs:19:5
++   |
++LL | use wildcard_imports_helper::inner::inner_for_self_import::*;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar`
++
++error: usage of wildcard import
++  --> $DIR/wildcard_imports.rs:20:5
++   |
++LL | use wildcard_imports_helper::*;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
++
++error: usage of wildcard import
++  --> $DIR/wildcard_imports.rs:89:13
++   |
++LL |         use crate::fn_mod::*;
++   |             ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
++
++error: usage of wildcard import
++  --> $DIR/wildcard_imports.rs:95:75
++   |
++LL |         use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
++   |                                                                           ^ help: try: `inner_extern_foo`
++
++error: usage of wildcard import
++  --> $DIR/wildcard_imports.rs:96:13
++   |
++LL |         use wildcard_imports_helper::*;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
++
++error: usage of wildcard import
++  --> $DIR/wildcard_imports.rs:107:20
++   |
++LL |         use self::{inner::*, inner2::*};
++   |                    ^^^^^^^^ help: try: `inner::inner_foo`
++
++error: usage of wildcard import
++  --> $DIR/wildcard_imports.rs:107:30
++   |
++LL |         use self::{inner::*, inner2::*};
++   |                              ^^^^^^^^^ help: try: `inner2::inner_bar`
++
++error: usage of wildcard import
++  --> $DIR/wildcard_imports.rs:114:13
++   |
++LL |         use wildcard_imports_helper::*;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
++
++error: usage of wildcard import
++  --> $DIR/wildcard_imports.rs:143:9
++   |
++LL |     use crate::in_fn_test::*;
++   |         ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}`
++
++error: usage of wildcard import
++  --> $DIR/wildcard_imports.rs:152:9
++   |
++LL |     use crate:: in_fn_test::  * ;
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported`
++
++error: usage of wildcard import
++  --> $DIR/wildcard_imports.rs:153:9
++   |
++LL |       use crate:: fn_mod::
++   |  _________^
++LL | |         *;
++   | |_________^ help: try: `crate:: fn_mod::foo`
++
++error: aborting due to 15 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d8205c5eb670009189596bac1e39453d0277aa35
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++#![allow(unused_must_use)]
++#![warn(clippy::write_literal)]
++
++use std::io::Write;
++
++fn main() {
++    let mut v = Vec::new();
++
++    // these should be fine
++    write!(&mut v, "Hello");
++    writeln!(&mut v, "Hello");
++    let world = "world";
++    writeln!(&mut v, "Hello {}", world);
++    writeln!(&mut v, "Hello {world}", world = world);
++    writeln!(&mut v, "3 in hex is {:X}", 3);
++    writeln!(&mut v, "2 + 1 = {:.4}", 3);
++    writeln!(&mut v, "2 + 1 = {:5.4}", 3);
++    writeln!(&mut v, "Debug test {:?}", "hello, world");
++    writeln!(&mut v, "{0:8} {1:>8}", "hello", "world");
++    writeln!(&mut v, "{1:8} {0:>8}", "hello", "world");
++    writeln!(&mut v, "{foo:8} {bar:>8}", foo = "hello", bar = "world");
++    writeln!(&mut v, "{bar:8} {foo:>8}", foo = "hello", bar = "world");
++    writeln!(&mut v, "{number:>width$}", number = 1, width = 6);
++    writeln!(&mut v, "{number:>0width$}", number = 1, width = 6);
++
++    // these should throw warnings
++    writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2);
++    write!(&mut v, "Hello {}", "world");
++    writeln!(&mut v, "Hello {} {}", world, "world");
++    writeln!(&mut v, "Hello {}", "world");
++    writeln!(&mut v, "10 / 4 is {}", 2.5);
++    writeln!(&mut v, "2 + 1 = {}", 3);
++
++    // positional args don't change the fact
++    // that we're using a literal -- this should
++    // throw a warning
++    writeln!(&mut v, "{0} {1}", "hello", "world");
++    writeln!(&mut v, "{1} {0}", "hello", "world");
++
++    // named args shouldn't change anything either
++    writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world");
++    writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..54a787fe555af67960403da0c0317714bc731a43
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,88 @@@
++error: literal with an empty format string
++  --> $DIR/write_literal.rs:27:79
++   |
++LL |     writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2);
++   |                                                                               ^
++   |
++   = note: `-D clippy::write-literal` implied by `-D warnings`
++
++error: literal with an empty format string
++  --> $DIR/write_literal.rs:28:32
++   |
++LL |     write!(&mut v, "Hello {}", "world");
++   |                                ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/write_literal.rs:29:44
++   |
++LL |     writeln!(&mut v, "Hello {} {}", world, "world");
++   |                                            ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/write_literal.rs:30:34
++   |
++LL |     writeln!(&mut v, "Hello {}", "world");
++   |                                  ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/write_literal.rs:31:38
++   |
++LL |     writeln!(&mut v, "10 / 4 is {}", 2.5);
++   |                                      ^^^
++
++error: literal with an empty format string
++  --> $DIR/write_literal.rs:32:36
++   |
++LL |     writeln!(&mut v, "2 + 1 = {}", 3);
++   |                                    ^
++
++error: literal with an empty format string
++  --> $DIR/write_literal.rs:37:33
++   |
++LL |     writeln!(&mut v, "{0} {1}", "hello", "world");
++   |                                 ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/write_literal.rs:37:42
++   |
++LL |     writeln!(&mut v, "{0} {1}", "hello", "world");
++   |                                          ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/write_literal.rs:38:33
++   |
++LL |     writeln!(&mut v, "{1} {0}", "hello", "world");
++   |                                 ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/write_literal.rs:38:42
++   |
++LL |     writeln!(&mut v, "{1} {0}", "hello", "world");
++   |                                          ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/write_literal.rs:41:43
++   |
++LL |     writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world");
++   |                                           ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/write_literal.rs:41:58
++   |
++LL |     writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world");
++   |                                                          ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/write_literal.rs:42:43
++   |
++LL |     writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world");
++   |                                           ^^^^^^^
++
++error: literal with an empty format string
++  --> $DIR/write_literal.rs:42:58
++   |
++LL |     writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world");
++   |                                                          ^^^^^^^
++
++error: aborting due to 14 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..93afd73d1114d257998089f1c683bfa090c06458
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934
++// // run-rustfix
++
++#![allow(clippy::write_literal)]
++#![warn(clippy::write_with_newline)]
++
++use std::io::Write;
++
++fn main() {
++    let mut v = Vec::new();
++
++    // These should fail
++    write!(&mut v, "Hello\n");
++    write!(&mut v, "Hello {}\n", "world");
++    write!(&mut v, "Hello {} {}\n", "world", "#2");
++    write!(&mut v, "{}\n", 1265);
++
++    // These should be fine
++    write!(&mut v, "");
++    write!(&mut v, "Hello");
++    writeln!(&mut v, "Hello");
++    writeln!(&mut v, "Hello\n");
++    writeln!(&mut v, "Hello {}\n", "world");
++    write!(&mut v, "Issue\n{}", 1265);
++    write!(&mut v, "{}", 1265);
++    write!(&mut v, "\n{}", 1275);
++    write!(&mut v, "\n\n");
++    write!(&mut v, "like eof\n\n");
++    write!(&mut v, "Hello {} {}\n\n", "world", "#2");
++    writeln!(&mut v, "\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126
++    writeln!(&mut v, "\nbla\n\n"); // #3126
++
++    // Escaping
++    write!(&mut v, "\\n"); // #3514
++    write!(&mut v, "\\\n"); // should fail
++    write!(&mut v, "\\\\n");
++
++    // Raw strings
++    write!(&mut v, r"\n"); // #3778
++
++    // Literal newlines should also fail
++    write!(
++        &mut v,
++        "
++"
++    );
++    write!(
++        &mut v,
++        r"
++"
++    );
++
++    // Don't warn on CRLF (#4208)
++    write!(&mut v, "\r\n");
++    write!(&mut v, "foo\r\n");
++    write!(&mut v, "\\r\n"); //~ ERROR
++    write!(&mut v, "foo\rbar\n");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2473329ca7276e149eb6702d32ea76c87de995c6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,114 @@@
++error: using `write!()` with a format string that ends in a single newline
++  --> $DIR/write_with_newline.rs:13:5
++   |
++LL |     write!(&mut v, "Hello/n");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::write-with-newline` implied by `-D warnings`
++help: use `writeln!()` instead
++   |
++LL |     writeln!(&mut v, "Hello");
++   |     ^^^^^^^               --
++
++error: using `write!()` with a format string that ends in a single newline
++  --> $DIR/write_with_newline.rs:14:5
++   |
++LL |     write!(&mut v, "Hello {}/n", "world");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: use `writeln!()` instead
++   |
++LL |     writeln!(&mut v, "Hello {}", "world");
++   |     ^^^^^^^                  --
++
++error: using `write!()` with a format string that ends in a single newline
++  --> $DIR/write_with_newline.rs:15:5
++   |
++LL |     write!(&mut v, "Hello {} {}/n", "world", "#2");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: use `writeln!()` instead
++   |
++LL |     writeln!(&mut v, "Hello {} {}", "world", "#2");
++   |     ^^^^^^^                     --
++
++error: using `write!()` with a format string that ends in a single newline
++  --> $DIR/write_with_newline.rs:16:5
++   |
++LL |     write!(&mut v, "{}/n", 1265);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: use `writeln!()` instead
++   |
++LL |     writeln!(&mut v, "{}", 1265);
++   |     ^^^^^^^            --
++
++error: using `write!()` with a format string that ends in a single newline
++  --> $DIR/write_with_newline.rs:35:5
++   |
++LL |     write!(&mut v, "//n"); // should fail
++   |     ^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: use `writeln!()` instead
++   |
++LL |     writeln!(&mut v, "/"); // should fail
++   |     ^^^^^^^            --
++
++error: using `write!()` with a format string that ends in a single newline
++  --> $DIR/write_with_newline.rs:42:5
++   |
++LL | /     write!(
++LL | |         &mut v,
++LL | |         "
++LL | | "
++LL | |     );
++   | |_____^
++   |
++help: use `writeln!()` instead
++   |
++LL |     writeln!(
++LL |         &mut v,
++LL |         ""
++   |
++
++error: using `write!()` with a format string that ends in a single newline
++  --> $DIR/write_with_newline.rs:47:5
++   |
++LL | /     write!(
++LL | |         &mut v,
++LL | |         r"
++LL | | "
++LL | |     );
++   | |_____^
++   |
++help: use `writeln!()` instead
++   |
++LL |     writeln!(
++LL |         &mut v,
++LL |         r""
++   |
++
++error: using `write!()` with a format string that ends in a single newline
++  --> $DIR/write_with_newline.rs:56:5
++   |
++LL |     write!(&mut v, "/r/n"); //~ ERROR
++   |     ^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: use `writeln!()` instead
++   |
++LL |     writeln!(&mut v, "/r"); //~ ERROR
++   |     ^^^^^^^             --
++
++error: using `write!()` with a format string that ends in a single newline
++  --> $DIR/write_with_newline.rs:57:5
++   |
++LL |     write!(&mut v, "foo/rbar/n");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: use `writeln!()` instead
++   |
++LL |     writeln!(&mut v, "foo/rbar");
++   |     ^^^^^^^                  --
++
++error: aborting due to 9 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c3ac15b03751cb62852d7594e9440990b039b8d1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++// run-rustfix
++
++#![allow(unused_must_use)]
++#![warn(clippy::writeln_empty_string)]
++use std::io::Write;
++
++fn main() {
++    let mut v = Vec::new();
++
++    // These should fail
++    writeln!(&mut v);
++
++    let mut suggestion = Vec::new();
++    writeln!(&mut suggestion);
++
++    // These should be fine
++    writeln!(&mut v);
++    writeln!(&mut v, " ");
++    write!(&mut v, "");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9a8894b6c0d3285b2a3a07ed6b7ecdaec4094658
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++// run-rustfix
++
++#![allow(unused_must_use)]
++#![warn(clippy::writeln_empty_string)]
++use std::io::Write;
++
++fn main() {
++    let mut v = Vec::new();
++
++    // These should fail
++    writeln!(&mut v, "");
++
++    let mut suggestion = Vec::new();
++    writeln!(&mut suggestion, "");
++
++    // These should be fine
++    writeln!(&mut v);
++    writeln!(&mut v, " ");
++    write!(&mut v, "");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..99635229b3e13eafc1f4bf2db210de1e677215a0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++error: using `writeln!(&mut v, "")`
++  --> $DIR/writeln_empty_string.rs:11:5
++   |
++LL |     writeln!(&mut v, "");
++   |     ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(&mut v)`
++   |
++   = note: `-D clippy::writeln-empty-string` implied by `-D warnings`
++
++error: using `writeln!(&mut suggestion, "")`
++  --> $DIR/writeln_empty_string.rs:14:5
++   |
++LL |     writeln!(&mut suggestion, "");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(&mut suggestion)`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..99652ca4470c2a47530812a3b78f0e2381f68ec3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,77 @@@
++#![warn(clippy::wrong_self_convention)]
++#![warn(clippy::wrong_pub_self_convention)]
++#![allow(dead_code)]
++
++fn main() {}
++
++#[derive(Clone, Copy)]
++struct Foo;
++
++impl Foo {
++    fn as_i32(self) {}
++    fn as_u32(&self) {}
++    fn into_i32(self) {}
++    fn is_i32(self) {}
++    fn is_u32(&self) {}
++    fn to_i32(self) {}
++    fn from_i32(self) {}
++
++    pub fn as_i64(self) {}
++    pub fn into_i64(self) {}
++    pub fn is_i64(self) {}
++    pub fn to_i64(self) {}
++    pub fn from_i64(self) {}
++    // check whether the lint can be allowed at the function level
++    #[allow(clippy::wrong_self_convention)]
++    pub fn from_cake(self) {}
++
++    fn as_x<F: AsRef<Self>>(_: F) {}
++    fn as_y<F: AsRef<Foo>>(_: F) {}
++}
++
++struct Bar;
++
++impl Bar {
++    fn as_i32(self) {}
++    fn as_u32(&self) {}
++    fn into_i32(&self) {}
++    fn into_u32(self) {}
++    fn is_i32(self) {}
++    fn is_u32(&self) {}
++    fn to_i32(self) {}
++    fn to_u32(&self) {}
++    fn from_i32(self) {}
++
++    pub fn as_i64(self) {}
++    pub fn into_i64(&self) {}
++    pub fn is_i64(self) {}
++    pub fn to_i64(self) {}
++    pub fn from_i64(self) {}
++
++    // test for false positives
++    fn as_(self) {}
++    fn into_(&self) {}
++    fn is_(self) {}
++    fn to_(self) {}
++    fn from_(self) {}
++    fn to_mut(&mut self) {}
++}
++
++// Allow Box<Self>, Rc<Self>, Arc<Self> for methods that take conventionally take Self by value
++#[allow(clippy::boxed_local)]
++mod issue4293 {
++    use std::rc::Rc;
++    use std::sync::Arc;
++
++    struct T;
++
++    impl T {
++        fn into_s1(self: Box<Self>) {}
++        fn into_s2(self: Rc<Self>) {}
++        fn into_s3(self: Arc<Self>) {}
++
++        fn into_t1(self: Box<T>) {}
++        fn into_t2(self: Rc<T>) {}
++        fn into_t3(self: Arc<T>) {}
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0d0eb19cd072333012d91cc384e5b6a2031e199a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,76 @@@
++error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
++  --> $DIR/wrong_self_convention.rs:17:17
++   |
++LL |     fn from_i32(self) {}
++   |                 ^^^^
++   |
++   = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
++
++error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
++  --> $DIR/wrong_self_convention.rs:23:21
++   |
++LL |     pub fn from_i64(self) {}
++   |                     ^^^^
++
++error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
++  --> $DIR/wrong_self_convention.rs:35:15
++   |
++LL |     fn as_i32(self) {}
++   |               ^^^^
++
++error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
++  --> $DIR/wrong_self_convention.rs:37:17
++   |
++LL |     fn into_i32(&self) {}
++   |                 ^^^^^
++
++error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name
++  --> $DIR/wrong_self_convention.rs:39:15
++   |
++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:41:15
++   |
++LL |     fn to_i32(self) {}
++   |               ^^^^
++
++error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
++  --> $DIR/wrong_self_convention.rs:43:17
++   |
++LL |     fn from_i32(self) {}
++   |                 ^^^^
++
++error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
++  --> $DIR/wrong_self_convention.rs:45:19
++   |
++LL |     pub fn as_i64(self) {}
++   |                   ^^^^
++
++error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
++  --> $DIR/wrong_self_convention.rs:46:21
++   |
++LL |     pub fn into_i64(&self) {}
++   |                     ^^^^^
++
++error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name
++  --> $DIR/wrong_self_convention.rs:47:19
++   |
++LL |     pub fn is_i64(self) {}
++   |                   ^^^^
++
++error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name
++  --> $DIR/wrong_self_convention.rs:48:19
++   |
++LL |     pub fn to_i64(self) {}
++   |                   ^^^^
++
++error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
++  --> $DIR/wrong_self_convention.rs:49:21
++   |
++LL |     pub fn from_i64(self) {}
++   |                     ^^^^
++
++error: aborting due to 12 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..09db130a764315b5c2227c928d5c637cdbef6a71
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++#[allow(unused_variables)]
++#[warn(clippy::zero_divided_by_zero)]
++fn main() {
++    let nan = 0.0 / 0.0;
++    let f64_nan = 0.0 / 0.0f64;
++    let other_f64_nan = 0.0f64 / 0.0;
++    let one_more_f64_nan = 0.0f64 / 0.0f64;
++    let zero = 0.0;
++    let other_zero = 0.0;
++    let other_nan = zero / other_zero; // fine - this lint doesn't propegate constants.
++    let not_nan = 2.0 / 0.0; // not an error: 2/0 = inf
++    let also_not_nan = 0.0 / 2.0; // not an error: 0/2 = 0
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d0e88f3c5a546612a4cb5843fc41c0f2bce24410
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++error: equal expressions as operands to `/`
++  --> $DIR/zero_div_zero.rs:4:15
++   |
++LL |     let nan = 0.0 / 0.0;
++   |               ^^^^^^^^^
++   |
++   = note: `#[deny(clippy::eq_op)]` on by default
++
++error: constant division of `0.0` with `0.0` will always result in NaN
++  --> $DIR/zero_div_zero.rs:4:15
++   |
++LL |     let nan = 0.0 / 0.0;
++   |               ^^^^^^^^^
++   |
++   = note: `-D clippy::zero-divided-by-zero` implied by `-D warnings`
++   = help: Consider using `f64::NAN` if you would like a constant representing NaN
++
++error: equal expressions as operands to `/`
++  --> $DIR/zero_div_zero.rs:5:19
++   |
++LL |     let f64_nan = 0.0 / 0.0f64;
++   |                   ^^^^^^^^^^^^
++
++error: constant division of `0.0` with `0.0` will always result in NaN
++  --> $DIR/zero_div_zero.rs:5:19
++   |
++LL |     let f64_nan = 0.0 / 0.0f64;
++   |                   ^^^^^^^^^^^^
++   |
++   = help: Consider using `f64::NAN` if you would like a constant representing NaN
++
++error: equal expressions as operands to `/`
++  --> $DIR/zero_div_zero.rs:6:25
++   |
++LL |     let other_f64_nan = 0.0f64 / 0.0;
++   |                         ^^^^^^^^^^^^
++
++error: constant division of `0.0` with `0.0` will always result in NaN
++  --> $DIR/zero_div_zero.rs:6:25
++   |
++LL |     let other_f64_nan = 0.0f64 / 0.0;
++   |                         ^^^^^^^^^^^^
++   |
++   = help: Consider using `f64::NAN` if you would like a constant representing NaN
++
++error: equal expressions as operands to `/`
++  --> $DIR/zero_div_zero.rs:7:28
++   |
++LL |     let one_more_f64_nan = 0.0f64 / 0.0f64;
++   |                            ^^^^^^^^^^^^^^^
++
++error: constant division of `0.0` with `0.0` will always result in NaN
++  --> $DIR/zero_div_zero.rs:7:28
++   |
++LL |     let one_more_f64_nan = 0.0f64 / 0.0f64;
++   |                            ^^^^^^^^^^^^^^^
++   |
++   = help: Consider using `f64::NAN` if you would like a constant representing NaN
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2de904376ad45b438ef580b99f2791fe7d5ea9bd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++fn main() {
++    unsafe {
++        let x = &() as *const ();
++        x.offset(0);
++        x.wrapping_add(0);
++        x.sub(0);
++        x.wrapping_sub(0);
++
++        let y = &1 as *const u8;
++        y.offset(0);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cfcd7de2b3d2c6b4503cbb125ff03be2ee0896b8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++error[E0606]: casting `&i32` as `*const u8` is invalid
++  --> $DIR/zero_offset.rs:9:17
++   |
++LL |         let y = &1 as *const u8;
++   |                 ^^^^^^^^^^^^^^^
++
++error: aborting due to previous error
++
++For more information about this error, try `rustc --explain E0606`.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..489aa4121a3a965169b04316e4850f43aea6ad5c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++// run-rustfix
++pub fn foo(_const: *const f32, _mut: *mut i64) {}
++
++fn main() {
++    let _ = std::ptr::null::<usize>();
++    let _ = std::ptr::null_mut::<f64>();
++    let _: *const u8 = std::ptr::null();
++
++    foo(0 as _, 0 as _);
++    foo(std::ptr::null(), std::ptr::null_mut());
++
++    let z = 0;
++    let _ = z as *const usize; // this is currently not caught
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c3b55ef9ebd90c99ee0f4e90ccf22e735b49fdd2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++// run-rustfix
++pub fn foo(_const: *const f32, _mut: *mut i64) {}
++
++fn main() {
++    let _ = 0 as *const usize;
++    let _ = 0 as *mut f64;
++    let _: *const u8 = 0 as *const _;
++
++    foo(0 as _, 0 as _);
++    foo(0 as *const _, 0 as *mut _);
++
++    let z = 0;
++    let _ = z as *const usize; // this is currently not caught
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4ee5e9a261686bc6ee1fac45439b32fa88e0fac3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++error: `0 as *const _` detected
++  --> $DIR/zero_ptr.rs:5:13
++   |
++LL |     let _ = 0 as *const usize;
++   |             ^^^^^^^^^^^^^^^^^ help: try: `std::ptr::null::<usize>()`
++   |
++   = note: `-D clippy::zero-ptr` implied by `-D warnings`
++
++error: `0 as *mut _` detected
++  --> $DIR/zero_ptr.rs:6:13
++   |
++LL |     let _ = 0 as *mut f64;
++   |             ^^^^^^^^^^^^^ help: try: `std::ptr::null_mut::<f64>()`
++
++error: `0 as *const _` detected
++  --> $DIR/zero_ptr.rs:7:24
++   |
++LL |     let _: *const u8 = 0 as *const _;
++   |                        ^^^^^^^^^^^^^ help: try: `std::ptr::null()`
++
++error: `0 as *const _` detected
++  --> $DIR/zero_ptr.rs:10:9
++   |
++LL |     foo(0 as *const _, 0 as *mut _);
++   |         ^^^^^^^^^^^^^ help: try: `std::ptr::null()`
++
++error: `0 as *mut _` detected
++  --> $DIR/zero_ptr.rs:10:24
++   |
++LL |     foo(0 as *const _, 0 as *mut _);
++   |                        ^^^^^^^^^^^ help: try: `std::ptr::null_mut()`
++
++error: aborting due to 5 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f5d03c645df0435b7876033306ef910f0237afcc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++#[test]
++fn check_that_clippy_lints_has_the_same_version_as_clippy() {
++    let clippy_meta = cargo_metadata::MetadataCommand::new()
++        .no_deps()
++        .exec()
++        .expect("could not obtain cargo metadata");
++    std::env::set_current_dir(std::env::current_dir().unwrap().join("clippy_lints")).unwrap();
++    let clippy_lints_meta = cargo_metadata::MetadataCommand::new()
++        .no_deps()
++        .exec()
++        .expect("could not obtain cargo metadata");
++    assert_eq!(clippy_lints_meta.packages[0].version, clippy_meta.packages[0].version);
++    for package in &clippy_meta.packages[0].dependencies {
++        if package.name == "clippy_lints" {
++            assert!(package.req.matches(&clippy_lints_meta.packages[0].version));
++            return;
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..411229935c36a4729a69823a2594395b0e3bb4e8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,7 @@@
++[relabel]
++allow-unauthenticated = [
++    "C-*", "A-*", "E-*", "L-*", "M-*", "O-*",
++    "good first issue", "needs test"
++]
++
++[assign]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3f9a6b06f7255d4aaa201a129c895de2e588f326
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++#!/usr/bin/bash
++
++# This run `kcov` on Clippy. The coverage report will be at
++# `./target/cov/index.html`.
++# `compile-test` is special. `kcov` does not work directly on it so these files
++# are compiled manually.
++
++tests=$(find tests/ -maxdepth 1 -name '*.rs' ! -name compile-test.rs -exec basename {} .rs \;)
++tmpdir=$(mktemp -d)
++
++cargo test --no-run --verbose
++
++for t in $tests; do
++  kcov \
++    --verify \
++    --include-path="$(pwd)/src,$(pwd)/clippy_lints/src" \
++    "$tmpdir/$t" \
++    cargo test --test "$t"
++done
++
++for t in ./tests/compile-fail/*.rs; do
++  kcov \
++    --verify \
++    --include-path="$(pwd)/src,$(pwd)/clippy_lints/src" \
++    "$tmpdir/compile-fail-$(basename "$t")" \
++    cargo run -- -L target/debug -L target/debug/deps -Z no-trans "$t"
++done
++
++for t in ./tests/run-pass/*.rs; do
++  kcov \
++    --verify \
++    --include-path="$(pwd)/src,$(pwd)/clippy_lints/src" \
++    "$tmpdir/run-pass-$(basename "$t")" \
++    cargo run -- -L target/debug -L target/debug/deps -Z no-trans "$t"
++done
++
++kcov --verify --merge target/cov "$tmpdir"/*
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d1bd60acf3d66ff0a6eb8202e16ad052c767a6d
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,81 @@@
++#!/usr/bin/env python
++
++# Build the gh-pages
++
++from collections import OrderedDict
++import re
++import sys
++import json
++
++from lintlib import parse_all, log
++
++lint_subheadline = re.compile(r'''^\*\*([\w\s]+?)[:?.!]?\*\*(.*)''')
++rust_code_block = re.compile(r'''```rust.+?```''', flags=re.DOTALL)
++
++CONF_TEMPLATE = """\
++This lint has the following configuration variables:
++
++* `%s: %s`: %s (defaults to `%s`)."""
++
++
++def parse_code_block(match):
++    lines = []
++
++    for line in match.group(0).split('\n'):
++        if not line.startswith('# '):
++            lines.append(line)
++
++    return '\n'.join(lines)
++
++
++def parse_lint_def(lint):
++    lint_dict = {}
++    lint_dict['id'] = lint.name
++    lint_dict['group'] = lint.group
++    lint_dict['level'] = lint.level
++    lint_dict['docs'] = OrderedDict()
++
++    last_section = None
++
++    for line in lint.doc:
++        match = re.match(lint_subheadline, line)
++        if match:
++            last_section = match.groups()[0]
++            text = match.groups()[1]
++        else:
++            text = line
++
++        if not last_section:
++            log.warning("Skipping comment line as it was not preceded by a heading")
++            log.debug("in lint `%s`, line `%s`", lint.name, line)
++
++        if last_section not in lint_dict['docs']:
++            lint_dict['docs'][last_section] = ""
++
++        lint_dict['docs'][last_section] += text + "\n"
++
++    for section in lint_dict['docs']:
++        lint_dict['docs'][section] = re.sub(rust_code_block, parse_code_block, lint_dict['docs'][section].strip())
++
++    return lint_dict
++
++
++def main():
++    lintlist, configs = parse_all()
++    lints = {}
++    for lint in lintlist:
++        lints[lint.name] = parse_lint_def(lint)
++        if lint.name in configs:
++            lints[lint.name]['docs']['Configuration'] = \
++                CONF_TEMPLATE % configs[lint.name]
++
++    outfile = sys.argv[1] if len(sys.argv) > 1 else "util/gh-pages/lints.json"
++    with open(outfile, "w") as fp:
++        lints = list(lints.values())
++        lints.sort(key=lambda x: x['id'])
++        json.dump(lints, fp, indent=2)
++        log.info("wrote JSON for great justice")
++
++
++if __name__ == "__main__":
++    main()
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6865abf971b282891e1da574766c4b0d577c5cac
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++#!/bin/bash
++
++# Fetches the merge commits between two git commits and prints the PR URL
++# together with the full commit message
++#
++# If you want to use this to update the Clippy changelog, be sure to manually
++# exclude the non-user facing changes like 'rustup' PRs, typo fixes, etc.
++
++first=$1
++last=$2
++
++IFS='
++'
++for pr in $(git log --oneline --grep "Merge #" --grep "Merge pull request" --grep "Auto merge of" --grep "Rollup merge of" "$first...$last" | sort -rn | uniq); do
++  id=$(echo "$pr" | rg -o '#[0-9]{3,5}' | cut -c 2-)
++  commit=$(echo "$pr" | cut -d' ' -f 1)
++  message=$(git --no-pager show --pretty=medium "$commit")
++  if [[ -n $(echo "$message" | rg "^[\s]{4}changelog: [nN]one\.*$") ]]; then
++    continue
++  fi
++
++  echo "URL: https://github.com/rust-lang/rust-clippy/pull/$id"
++  echo "Markdown URL: [#$id](https://github.com/rust-lang/rust-clippy/pull/$id)"
++  echo "$message"
++  echo "---------------------------------------------------------"
++  echo
++done
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e11f2eeba3b32d11e5cd64b72a10b4c44498132c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,277 @@@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++    <meta charset="UTF-8"/>
++    <meta name="viewport" content="width=device-width, initial-scale=1"/>
++
++    <title>ALL the Clippy Lints</title>
++
++    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css"/>
++    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/styles/github.min.css"/>
++    <style>
++        blockquote { font-size: 1em; }
++        [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; }
++
++        .form-inline .checkbox { margin-right: 0.6em }
++
++        .panel-heading { cursor: pointer; }
++        .panel-heading:hover { background-color: #eee; }
++
++        .panel-title { display: flex; }
++        .panel-title .label { display: inline-block; }
++
++        .panel-title-name { flex: 1; }
++        .panel-title-name span { vertical-align: bottom; }
++
++        .panel .panel-title-name .anchor { display: none; }
++        .panel:hover .panel-title-name .anchor { display: inline; color: #fff; }
++    </style>
++</head>
++<body>
++    <div class="container" ng-app="clippy" ng-controller="lintList">
++        <div class="page-header">
++            <h1>ALL the Clippy Lints</h1>
++        </div>
++
++        <noscript>
++            <div class="alert alert-danger" role="alert">
++                Sorry, this site only works with JavaScript! :(
++            </div>
++        </noscript>
++
++        <div ng-cloak>
++
++            <div class="alert alert-info" role="alert" ng-if="loading">
++                Loading&#x2026;
++            </div>
++            <div class="alert alert-danger" role="alert" ng-if="error">
++                Error loading lints!
++            </div>
++
++            <div class="panel panel-default" ng-show="data">
++                <div class="panel-body row">
++                    <div class="col-md-6 form-inline">
++                        <div class="form-group form-group-lg">
++                            <p class="h4">Lint levels</p>
++                            <div class="checkbox" ng-repeat="(level, enabled) in levels">
++                                <label>
++                                    <input type="checkbox" ng-model="levels[level]" />
++                                    {{level}}
++                                </label>
++                            </div>
++                        </div>
++                    </div>
++                    <div class="col-md-6 form-inline">
++                        <div class="form-group form-group-lg">
++                            <p class="h4">Lint groups</p>
++                            <div class="checkbox" ng-repeat="(group, enabled) in groups">
++                                <label class="text-capitalize">
++                                    <input type="checkbox" ng-model="groups[group]" />
++                                    {{group}}
++                                </label>
++                            </div>
++                        </div>
++                    </div>
++                </div>
++                <div class="panel-body row">
++                    <div class="col-md-12 form-horizontal">
++                        <div class="input-group">
++                            <label class="input-group-addon" id="filter-label" for="filter-input">Filter:</label>
++                            <input type="text" class="form-control" placeholder="Keywords or search string" id="filter-input" ng-model="search" />
++                            <span class="input-group-btn">
++                                <button class="btn btn-default" type="button" ng-click="search = ''">
++                                    Clear
++                                </button>
++                            </span>
++                        </div>
++                    </div>
++                </div>
++            </div>
++
++            <article class="panel panel-default" id="{{lint.id}}"
++                ng-repeat="lint in data | filter:byLevels | filter:byGroups | filter:search | orderBy:'id' track by lint.id" on-finish-render="ngRepeatFinished">
++                <header class="panel-heading" ng-click="open[lint.id] = !open[lint.id]">
++                    <h2 class="panel-title">
++                        <div class="panel-title-name">
++                            <span>{{lint.id}}</span>
++                            <a href="#{{lint.id}}" class="anchor label label-default" ng-click="open[lint.id] = true; $event.stopPropagation()">&para;</a>
++                        </div>
++
++                        <div class="panel-title-addons">
++                            <span class="label label-default text-capitalize">{{lint.group}}</span>
++
++                            <span ng-if="lint.level == 'Allow'" class="label label-success">Allow</span>
++                            <span ng-if="lint.level == 'Warn'" class="label label-warning">Warn</span>
++                            <span ng-if="lint.level == 'Deny'" class="label label-danger">Deny</span>
++                            <span ng-if="lint.level == 'Deprecated'" class="label label-default">Deprecated</span>
++
++                            <button class="btn btn-default btn-xs">
++                                <span ng-show="open[lint.id]">&minus;</span>
++                                <span ng-hide="open[lint.id]">&plus;</span>
++                            </button>
++                        </div>
++                    </h2>
++                </header>
++
++                <ul class="list-group lint-docs" ng-if="lint.docs" ng-class="{collapse: true, in: open[lint.id]}">
++                    <li class="list-group-item" ng-repeat="(title, text) in lint.docs">
++                        <h4 class="list-group-item-heading">
++                            {{title}}
++                        </h4>
++                        <div class="list-group-item-text" ng-bind-html="text | markdown"></div>
++                    </li>
++                </ul>
++            </article>
++        </div>
++    </div>
++
++    <a href="https://github.com/rust-lang/rust-clippy">
++        <img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on Github"/>
++    </a>
++
++    <script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/7.0.0/markdown-it.min.js"></script>
++    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/highlight.min.js"></script>
++    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/languages/rust.min.js"></script>
++    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.12/angular.min.js"></script>
++    <script>
++    (function () {
++        var md = window.markdownit({
++            html: true,
++            linkify: true,
++            typographer: true,
++            highlight: function (str, lang) {
++                if (lang && hljs.getLanguage(lang)) {
++                    try {
++                        return '<pre class="hljs"><code>' +
++                            hljs.highlight(lang, str, true).value +
++                            '</code></pre>';
++                    } catch (__) {}
++                }
++
++                return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>';
++            }
++        });
++
++        function scrollToLint(lintId) {
++            var target = document.getElementById(lintId);
++            if (!target) {
++                return;
++            }
++            target.scrollIntoView();
++        }
++
++        function scrollToLintByURL($scope) {
++            var removeListener = $scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) {
++                scrollToLint(window.location.hash.slice(1));
++                removeListener();
++            });
++        }
++
++        function selectGroup($scope, selectedGroup) {
++            var groups = $scope.groups;
++            for (var group in groups) {
++                if (groups.hasOwnProperty(group)) {
++                    if (group === selectedGroup) {
++                        groups[group] = true;
++                    } else {
++                        groups[group] = false;
++                    }
++                }
++            }
++        }
++
++        angular.module("clippy", [])
++        .filter('markdown', function ($sce) {
++            return function (text) {
++                return $sce.trustAsHtml(
++                    md.render(text || '')
++                    // Oh deer, what a hack :O
++                    .replace('<table', '<table class="table"')
++                );
++            };
++        })
++        .directive('onFinishRender', function ($timeout) {
++            return {
++                restrict: 'A',
++                link: function (scope, element, attr) {
++                    if (scope.$last === true) {
++                        $timeout(function () {
++                            scope.$emit(attr.onFinishRender);
++                        });
++                    }
++                }
++            };
++        })
++        .controller("lintList", function ($scope, $http, $timeout) {
++            // Level filter
++            var LEVEL_FILTERS_DEFAULT = {Allow: true, Warn: true, Deny: true, Deprecated: true};
++            $scope.levels = LEVEL_FILTERS_DEFAULT;
++            $scope.byLevels = function (lint) {
++                return $scope.levels[lint.level];
++            };
++
++            $scope.groups = {};
++            $scope.byGroups = function (lint) {
++                return $scope.groups[lint.group];
++            };
++
++            // Get data
++            $scope.open = {};
++            $scope.loading = true;
++
++            if (window.location.hash.length > 1) {
++                $scope.search = window.location.hash.slice(1);
++                $scope.open[window.location.hash.slice(1)] = true;
++                scrollToLintByURL($scope);
++            }
++
++            $http.get('./lints.json')
++            .success(function (data) {
++                $scope.data = data;
++                $scope.loading = false;
++
++                // Initialize lint groups (the same structure is also used to enable filtering)
++                $scope.groups = data.reduce(function (result, val) {
++                    result[val.group] = true;
++                    return result;
++                }, {});
++
++                var selectedGroup = getQueryVariable("sel");
++                if (selectedGroup) {
++                    selectGroup($scope, selectedGroup.toLowerCase());
++                }
++
++                scrollToLintByURL($scope);
++            })
++            .error(function (data) {
++                $scope.error = data;
++                $scope.loading = false;
++            });
++
++            window.addEventListener('hashchange', function () {
++                // trigger re-render
++                $timeout(function () {
++                    $scope.levels = LEVEL_FILTERS_DEFAULT;
++                    $scope.search = window.location.hash.slice(1);
++                    $scope.open[window.location.hash.slice(1)] = true;
++
++                    scrollToLintByURL($scope);
++                });
++                return true;
++            }, false);
++        });
++    })();
++
++    function getQueryVariable(variable) {
++        var query = window.location.search.substring(1);
++        var vars = query.split('&');
++        for (var i = 0; i < vars.length; i++) {
++            var pair = vars[i].split('=');
++            if (decodeURIComponent(pair[0]) == variable) {
++                return decodeURIComponent(pair[1]);
++            }
++        }
++    }
++    </script>
++</body>
++</html>
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6e810a349bfccb640adbabfd9ce97d258bfd91d0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,91 @@@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++    <meta charset="UTF-8"/>
++    <meta name="viewport" content="width=device-width, initial-scale=1"/>
++
++    <title>Clippy lints documentation</title>
++
++    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css"/>
++    <style>
++        [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; }
++    </style>
++</head>
++<body>
++    <div class="container" ng-app="clippy" ng-controller="docVersions">
++        <div class="page-header">
++            <h1>Clippy lints documentation</h1>
++        </div>
++
++        <div ng-cloak>
++            <div class="alert alert-info" role="alert" ng-if="loading">
++                Loading&#x2026;
++            </div>
++            <div class="alert alert-danger" role="alert" ng-if="error">
++                Error loading versions!<br/>
++                You can always try to get <a href="master/index.html">the master branch docs</a>.
++            </div>
++
++            <article class="panel panel-default" ng-show="data">
++                <div class="panel-heading">
++                    <h3 class="panel-title">
++                        Available versions
++                    </h3>
++                </div>
++
++                <ul class="list-group">
++                    <a class="list-group-item" ng-repeat="version in data | orderBy:versionOrder:true"
++                       href="./{{version}}/index.html">
++                        {{normalizeVersionDisplay(version)}}
++                    </a>
++                </ul>
++            </article>
++        </div>
++    </div>
++
++    <a href="https://github.com/rust-lang/rust-clippy">
++        <img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png"/>
++    </a>
++
++
++    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.12/angular.min.js"></script>
++    <script>
++        angular.module('clippy', [])
++        .controller('docVersions', function ($scope, $http) {
++            $scope.loading = true;
++
++            $scope.normalizeVersionDisplay = function(v) {
++                return v.replace(/^v/, '');
++            };
++
++            $scope.normalizeVersion = function(v) {
++                return v.replace(/^v/, '').replace(/^rust-/, '');
++            };
++
++            $scope.versionOrder = function(v) {
++                if (v === 'master') { return Infinity; }
++                if (v === 'stable') { return Number.MAX_VALUE; }
++                if (v === 'beta') { return Number.MAX_VALUE - 1; }
++
++                return $scope.normalizeVersion(v)
++                    .split('.')
++                    .reverse()
++                    .reduce(function(acc, val, index) {
++                        return acc + (val * Math.pow(100, index));
++                    }, 0);
++            }
++
++            $http.get('./versions.json')
++            .success(function (data) {
++                $scope.data = data;
++                $scope.loading = false;
++            })
++            .error(function (data) {
++                $scope.error = data;
++                $scope.loading = false;
++            });
++        })
++        ;
++    </script>
++</body>
++</html>
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d0d9beb9b2d9fa686d907f650dfb55343d62feb0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,114 @@@
++# Common utils for the several housekeeping scripts.
++
++import os
++import re
++import collections
++
++import logging as log
++log.basicConfig(level=log.INFO, format='%(levelname)s: %(message)s')
++
++Lint = collections.namedtuple('Lint', 'name level doc sourcefile group')
++Config = collections.namedtuple('Config', 'name ty doc default')
++
++lintname_re = re.compile(r'''pub\s+([A-Z_][A-Z_0-9]*)''')
++group_re = re.compile(r'''\s*([a-z_][a-z_0-9]+)''')
++conf_re = re.compile(r'''define_Conf! {\n([^}]*)\n}''', re.MULTILINE)
++confvar_re = re.compile(
++    r'''/// Lint: ([\w,\s]+)\. (.*)\n\s*\([^,]+,\s+"([^"]+)":\s+([^,]+),\s+([^\.\)]+).*\),''', re.MULTILINE)
++comment_re = re.compile(r'''\s*/// ?(.*)''')
++
++lint_levels = {
++    "correctness": 'Deny',
++    "style": 'Warn',
++    "complexity": 'Warn',
++    "perf": 'Warn',
++    "restriction": 'Allow',
++    "pedantic": 'Allow',
++    "nursery": 'Allow',
++    "cargo": 'Allow',
++}
++
++
++def parse_lints(lints, filepath):
++    comment = []
++    clippy = False
++    deprecated = False
++    name = ""
++
++    with open(filepath) as fp:
++        for line in fp:
++            if clippy or deprecated:
++                m = lintname_re.search(line)
++                if m:
++                    name = m.group(1).lower()
++                    line = next(fp)
++
++                    if deprecated:
++                        level = "Deprecated"
++                        group = "deprecated"
++                    else:
++                        while True:
++                            g = group_re.search(line)
++                            if g:
++                                group = g.group(1).lower()
++                                level = lint_levels.get(group, None)
++                                break
++                            line = next(fp)
++
++                    if level is None:
++                        continue
++
++                    log.info("found %s with level %s in %s",
++                             name, level, filepath)
++                    lints.append(Lint(name, level, comment, filepath, group))
++                    comment = []
++
++                    clippy = False
++                    deprecated = False
++                    name = ""
++                else:
++                    m = comment_re.search(line)
++                    if m:
++                        comment.append(m.group(1))
++            elif line.startswith("declare_clippy_lint!"):
++                clippy = True
++                deprecated = False
++            elif line.startswith("declare_deprecated_lint!"):
++                clippy = False
++                deprecated = True
++            elif line.startswith("declare_lint!"):
++                import sys
++                print(
++                    "don't use `declare_lint!` in Clippy, "
++                    "use `declare_clippy_lint!` instead"
++                )
++                sys.exit(42)
++
++
++def parse_configs(path):
++    configs = {}
++    with open(os.path.join(path, 'utils/conf.rs')) as fp:
++        contents = fp.read()
++
++    match = re.search(conf_re, contents)
++    confvars = re.findall(confvar_re, match.group(1))
++
++    for (lints, doc, name, ty, default) in confvars:
++        for lint in lints.split(','):
++            configs[lint.strip().lower()] = Config(name.replace("_", "-"), ty, doc, default)
++    return configs
++
++
++def parse_all(path="clippy_lints/src"):
++    lints = []
++    for root, dirs, files in os.walk(path):
++        for fn in files:
++            if fn.endswith('.rs'):
++                parse_lints(lints, os.path.join(root, fn))
++
++    log.info("got %s lints", len(lints))
++
++    configs = parse_configs(path)
++    log.info("got %d configs", len(configs))
++
++    return lints, configs
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5cdc7313f543906479d47f95c6b5be5edd189cc5
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++#!/usr/bin/env python
++
++import json
++import os
++import sys
++
++from lintlib import log
++
++
++def key(v):
++    if v == 'master':
++        return float('inf')
++    if v == 'stable':
++        return sys.maxsize
++    if v == 'beta':
++        return sys.maxsize - 1
++
++    v = v.replace('v', '').replace('rust-', '')
++
++    s = 0
++    for i, val in enumerate(v.split('.')[::-1]):
++        s += int(val) * 100**i
++
++    return s
++
++
++def main():
++    if len(sys.argv) < 2:
++        print("Error: specify output directory")
++        return
++
++    outdir = sys.argv[1]
++    versions = [
++        dir for dir in os.listdir(outdir) if not dir.startswith(".") and os.path.isdir(os.path.join(outdir, dir))
++    ]
++    versions.sort(key=key)
++
++    with open(os.path.join(outdir, "versions.json"), "w") as fp:
++        json.dump(versions, fp, indent=2)
++        log.info("wrote JSON for great justice")
++
++
++if __name__ == "__main__":
++    main()