summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:20:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:20:29 +0000
commit631cd5845e8de329d0e227aaa707d7ea228b8f8f (patch)
treea1b87c8f8cad01cf18f7c5f57a08f102771ed303 /src/tools/clippy
parentAdding debian version 1.69.0+dfsg1-1. (diff)
downloadrustc-631cd5845e8de329d0e227aaa707d7ea228b8f8f.tar.xz
rustc-631cd5845e8de329d0e227aaa707d7ea228b8f8f.zip
Merging upstream version 1.70.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/clippy')
-rw-r--r--src/tools/clippy/.cargo/config.toml3
-rw-r--r--src/tools/clippy/.editorconfig1
-rw-r--r--src/tools/clippy/.github/workflows/clippy_bors.yml2
-rw-r--r--src/tools/clippy/.github/workflows/remark.yml2
-rw-r--r--src/tools/clippy/CHANGELOG.md169
-rw-r--r--src/tools/clippy/CONTRIBUTING.md2
-rw-r--r--src/tools/clippy/COPYRIGHT4
-rw-r--r--src/tools/clippy/Cargo.toml2
-rw-r--r--src/tools/clippy/README.md10
-rw-r--r--src/tools/clippy/book/src/README.md2
-rw-r--r--src/tools/clippy/book/src/SUMMARY.md1
-rw-r--r--src/tools/clippy/book/src/configuration.md29
-rw-r--r--src/tools/clippy/book/src/development/README.md2
-rw-r--r--src/tools/clippy/book/src/development/adding_lints.md32
-rw-r--r--src/tools/clippy/book/src/development/basics.md6
-rw-r--r--src/tools/clippy/book/src/development/common_tools_writing_lints.md14
-rw-r--r--src/tools/clippy/book/src/development/infrastructure/book.md4
-rw-r--r--src/tools/clippy/book/src/development/infrastructure/changelog_update.md2
-rw-r--r--src/tools/clippy/book/src/development/infrastructure/release.md2
-rw-r--r--src/tools/clippy/book/src/development/infrastructure/sync.md13
-rw-r--r--src/tools/clippy/book/src/development/proposals/README.md2
-rw-r--r--src/tools/clippy/book/src/development/proposals/roadmap-2021.md6
-rw-r--r--src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md42
-rw-r--r--src/tools/clippy/book/src/development/type_checking.md144
-rw-r--r--src/tools/clippy/book/src/installation.md4
-rw-r--r--src/tools/clippy/book/src/lint_configuration.md19
-rw-r--r--src/tools/clippy/book/src/lints.md10
-rw-r--r--src/tools/clippy/book/src/usage.md4
-rw-r--r--src/tools/clippy/clippy_dev/src/lib.rs2
-rw-r--r--src/tools/clippy/clippy_dev/src/new_lint.rs8
-rw-r--r--src/tools/clippy/clippy_dev/src/update_lints.rs14
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml3
-rw-r--r--src/tools/clippy/clippy_lints/src/allow_attributes.rs71
-rw-r--r--src/tools/clippy/clippy_lints/src/almost_complete_range.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/booleans.rs64
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs42
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/cognitive_complexity.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/collection_is_never_read.rs141
-rw-r--r--src/tools/clippy/clippy_lints/src/copies.rs70
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/default.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/derivable_impls.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/derive.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/exit.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/explicit_write.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs177
-rw-r--r--src/tools/clippy/clippy_lints/src/fn_null_check.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/format.rs91
-rw-r--r--src/tools/clippy/clippy_lints/src/format_args.rs224
-rw-r--r--src/tools/clippy/clippy_lints/src/format_impl.rs60
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/must_use.rs33
-rw-r--r--src/tools/clippy/clippy_lints/src/future_not_send.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/infinite_iter.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/instant_subtraction.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/items_after_statements.rs31
-rw-r--r--src/tools/clippy/clippy_lints/src/large_futures.rs87
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs125
-rw-r--r--src/tools/clippy/clippy_lints/src/let_underscore.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs45
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs53
-rw-r--r--src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs100
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/never_loop.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/same_item_push.rs32
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_async_fn.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_bits.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_clamp.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs74
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs93
-rw-r--r--src/tools/clippy/clippy_lints/src/match_result_ok.rs25
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_bool.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs52
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/single_match.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/mem_replace.rs71
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs53
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs27
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs45
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/misc.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_assert_message.rs82
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_doc.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs37
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_key.rs60
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_bool.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_question_mark.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/neg_multiply.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/option_if_let_else.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_async_block.rs108
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/ref_option_ref.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs32
-rw-r--r--src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/shadow.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/single_component_path_imports.rs59
-rw-r--r--src/tools/clippy/clippy_lints/src/size_of_ref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/std_instead_of_core.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs94
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/swap.rs116
-rw-r--r--src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs71
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/utils.rs57
-rw-r--r--src/tools/clippy/clippy_lints/src/types/borrowed_box.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/types/utils.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs120
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs88
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/useless_conversion.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/conf.rs38
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs98
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/mod.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/wildcard_imports.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs160
-rw-r--r--src/tools/clippy/clippy_utils/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils.rs43
-rw-r--r--src/tools/clippy/clippy_utils/src/attrs.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/check_proc_macro.rs1
-rw-r--r--src/tools/clippy/clippy_utils/src/eager_or_lazy.rs8
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs10
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs89
-rw-r--r--src/tools/clippy/clippy_utils/src/macros.rs832
-rw-r--r--src/tools/clippy/clippy_utils/src/msrvs.rs1
-rw-r--r--src/tools/clippy/clippy_utils/src/paths.rs3
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs17
-rw-r--r--src/tools/clippy/clippy_utils/src/source.rs42
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs12
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs69
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs5
-rw-r--r--src/tools/clippy/declare_clippy_lint/Cargo.toml2
-rw-r--r--src/tools/clippy/etc/relicense/RELICENSE_DOCUMENTATION.md4
-rw-r--r--src/tools/clippy/lintcheck/Cargo.toml14
-rw-r--r--src/tools/clippy/lintcheck/README.md11
-rw-r--r--src/tools/clippy/lintcheck/src/config.rs160
-rw-r--r--src/tools/clippy/lintcheck/src/popular-crates.rs65
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/rustc_tools_util/README.md4
-rw-r--r--src/tools/clippy/src/driver.rs6
-rw-r--r--src/tools/clippy/src/main.rs2
-rw-r--r--src/tools/clippy/tests/compile-test.rs2
-rw-r--r--src/tools/clippy/tests/dogfood.rs21
-rw-r--r--src/tools/clippy/tests/lint_message_convention.rs2
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr6
-rw-r--r--src/tools/clippy/tests/ui-internal/custom_ice_message.stderr1
-rw-r--r--src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr22
-rw-r--r--src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.rs10
-rw-r--r--src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.stderr29
-rw-r--r--src/tools/clippy/tests/ui-toml/array_size_threshold/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/extra_unused_type_parameters/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/extra_unused_type_parameters/extra_unused_type_parameters.rs9
-rw-r--r--src/tools/clippy/tests/ui-toml/ifs_same_cond/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs18
-rw-r--r--src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr15
-rw-r--r--src/tools/clippy/tests/ui-toml/large_futures/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/large_futures/large_futures.rs27
-rw-r--r--src/tools/clippy/tests/ui-toml/large_futures/large_futures.stderr10
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr1
-rw-r--r--src/tools/clippy/tests/ui/allow_attributes.fixed25
-rw-r--r--src/tools/clippy/tests/ui/allow_attributes.rs25
-rw-r--r--src/tools/clippy/tests/ui/allow_attributes.stderr16
-rw-r--r--src/tools/clippy/tests/ui/almost_complete_range.fixed49
-rw-r--r--src/tools/clippy/tests/ui/almost_complete_range.rs49
-rw-r--r--src/tools/clippy/tests/ui/almost_complete_range.stderr93
-rw-r--r--src/tools/clippy/tests/ui/arithmetic_side_effects.rs32
-rw-r--r--src/tools/clippy/tests/ui/arithmetic_side_effects.stderr294
-rw-r--r--src/tools/clippy/tests/ui/as_conversions.rs13
-rw-r--r--src/tools/clippy/tests/ui/as_conversions.stderr6
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.fixed1
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.rs1
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.stderr12
-rw-r--r--src/tools/clippy/tests/ui/author/blocks.stdout6
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/doc_unsafe_macros.rs16
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/implicit_hasher_macros.rs6
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/macro_rules.rs141
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/macro_use_helper.rs2
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/proc_macro_with_span.rs32
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/proc_macros.rs474
-rw-r--r--src/tools/clippy/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs2
-rw-r--r--src/tools/clippy/tests/ui/boxed_local.rs35
-rw-r--r--src/tools/clippy/tests/ui/boxed_local.stderr8
-rw-r--r--src/tools/clippy/tests/ui/cast.rs6
-rw-r--r--src/tools/clippy/tests/ui/cast.stderr112
-rw-r--r--src/tools/clippy/tests/ui/clear_with_drain.fixed358
-rw-r--r--src/tools/clippy/tests/ui/clear_with_drain.rs358
-rw-r--r--src/tools/clippy/tests/ui/clear_with_drain.stderr130
-rw-r--r--src/tools/clippy/tests/ui/collection_is_never_read.rs190
-rw-r--r--src/tools/clippy/tests/ui/collection_is_never_read.stderr76
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-10148.rs9
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-10148.stderr12
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6179.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6252.stderr6
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6254.stderr2
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed20
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs20
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr57
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed20
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs20
-rw-r--r--src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr61
-rw-r--r--src/tools/clippy/tests/ui/default_trait_access.fixed6
-rw-r--r--src/tools/clippy/tests/ui/default_trait_access.rs6
-rw-r--r--src/tools/clippy/tests/ui/deref_addrof.fixed24
-rw-r--r--src/tools/clippy/tests/ui/deref_addrof.rs24
-rw-r--r--src/tools/clippy/tests/ui/deref_addrof.stderr38
-rw-r--r--src/tools/clippy/tests/ui/deref_addrof_macro.rs15
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.fixed37
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.rs37
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.stderr24
-rw-r--r--src/tools/clippy/tests/ui/doc_unsafe.rs12
-rw-r--r--src/tools/clippy/tests/ui/double_must_use.rs11
-rw-r--r--src/tools/clippy/tests/ui/double_must_use.stderr10
-rw-r--r--src/tools/clippy/tests/ui/empty_loop.rs15
-rw-r--r--src/tools/clippy/tests/ui/equatable_if_let.fixed16
-rw-r--r--src/tools/clippy/tests/ui/equatable_if_let.rs16
-rw-r--r--src/tools/clippy/tests/ui/equatable_if_let.stderr32
-rw-r--r--src/tools/clippy/tests/ui/erasing_op.rs8
-rw-r--r--src/tools/clippy/tests/ui/erasing_op.stderr10
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_type_parameters.fixed105
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_type_parameters.rs11
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr79
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs24
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr27
-rw-r--r--src/tools/clippy/tests/ui/field_reassign_with_default.rs36
-rw-r--r--src/tools/clippy/tests/ui/field_reassign_with_default.stderr44
-rw-r--r--src/tools/clippy/tests/ui/format.fixed6
-rw-r--r--src/tools/clippy/tests/ui/format.rs6
-rw-r--r--src/tools/clippy/tests/ui/format.stderr30
-rw-r--r--src/tools/clippy/tests/ui/format_args_unfixable.rs44
-rw-r--r--src/tools/clippy/tests/ui/format_args_unfixable.stderr36
-rw-r--r--src/tools/clippy/tests/ui/ifs_same_cond.rs26
-rw-r--r--src/tools/clippy/tests/ui/ifs_same_cond.stderr14
-rw-r--r--src/tools/clippy/tests/ui/impl_trait_in_params.stderr4
-rw-r--r--src/tools/clippy/tests/ui/implicit_clone.fixed2
-rw-r--r--src/tools/clippy/tests/ui/implicit_clone.rs2
-rw-r--r--src/tools/clippy/tests/ui/implicit_hasher.rs25
-rw-r--r--src/tools/clippy/tests/ui/implicit_hasher.stderr55
-rw-r--r--src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed21
-rw-r--r--src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs21
-rw-r--r--src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr4
-rw-r--r--src/tools/clippy/tests/ui/integer_arithmetic.rs2
-rw-r--r--src/tools/clippy/tests/ui/items_after_statement.rs (renamed from src/tools/clippy/tests/ui/item_after_statement.rs)17
-rw-r--r--src/tools/clippy/tests/ui/items_after_statement.stderr (renamed from src/tools/clippy/tests/ui/item_after_statement.stderr)6
-rw-r--r--src/tools/clippy/tests/ui/large_enum_variant.rs13
-rw-r--r--src/tools/clippy/tests/ui/large_futures.rs61
-rw-r--r--src/tools/clippy/tests/ui/large_futures.stderr82
-rw-r--r--src/tools/clippy/tests/ui/len_without_is_empty.rs92
-rw-r--r--src/tools/clippy/tests/ui/len_without_is_empty.stderr20
-rw-r--r--src/tools/clippy/tests/ui/let_unit.fixed4
-rw-r--r--src/tools/clippy/tests/ui/let_unit.rs4
-rw-r--r--src/tools/clippy/tests/ui/let_with_type_underscore.rs19
-rw-r--r--src/tools/clippy/tests/ui/let_with_type_underscore.stderr39
-rw-r--r--src/tools/clippy/tests/ui/lines_filter_map_ok.fixed29
-rw-r--r--src/tools/clippy/tests/ui/lines_filter_map_ok.rs29
-rw-r--r--src/tools/clippy/tests/ui/lines_filter_map_ok.stderr51
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports.fixed6
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports.rs2
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports.stderr4
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports_expect.rs2
-rw-r--r--src/tools/clippy/tests/ui/manual_async_fn.fixed6
-rw-r--r--src/tools/clippy/tests/ui/manual_async_fn.rs12
-rw-r--r--src/tools/clippy/tests/ui/manual_async_fn.stderr47
-rw-r--r--src/tools/clippy/tests/ui/manual_clamp.rs19
-rw-r--r--src/tools/clippy/tests/ui/manual_main_separator_str.fixed39
-rw-r--r--src/tools/clippy/tests/ui/manual_main_separator_str.rs39
-rw-r--r--src/tools/clippy/tests/ui/manual_main_separator_str.stderr28
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.fixed25
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.rs25
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.stderr25
-rw-r--r--src/tools/clippy/tests/ui/manual_slice_size_calculation.rs36
-rw-r--r--src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr51
-rw-r--r--src/tools/clippy/tests/ui/match_result_ok.fixed2
-rw-r--r--src/tools/clippy/tests/ui/match_result_ok.stderr2
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.fixed41
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.rs55
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.stderr111
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding2.fixed4
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding2.stderr4
-rw-r--r--src/tools/clippy/tests/ui/mem_replace.fixed34
-rw-r--r--src/tools/clippy/tests/ui/mem_replace.rs34
-rw-r--r--src/tools/clippy/tests/ui/mem_replace.stderr26
-rw-r--r--src/tools/clippy/tests/ui/mem_replace_macro.rs23
-rw-r--r--src/tools/clippy/tests/ui/mem_replace_macro.stderr11
-rw-r--r--src/tools/clippy/tests/ui/missing_assert_message.rs84
-rw-r--r--src/tools/clippy/tests/ui/missing_assert_message.stderr131
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs6
-rw-r--r--src/tools/clippy/tests/ui/missing_doc.rs6
-rw-r--r--src/tools/clippy/tests/ui/missing_doc.stderr70
-rw-r--r--src/tools/clippy/tests/ui/missing_doc_impl.rs6
-rw-r--r--src/tools/clippy/tests/ui/missing_doc_impl.stderr56
-rw-r--r--src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed6
-rw-r--r--src/tools/clippy/tests/ui/mistyped_literal_suffix.rs6
-rw-r--r--src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs39
-rw-r--r--src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr62
-rw-r--r--src/tools/clippy/tests/ui/must_use_unit.fixed11
-rw-r--r--src/tools/clippy/tests/ui/must_use_unit.rs11
-rw-r--r--src/tools/clippy/tests/ui/mut_mut.rs11
-rw-r--r--src/tools/clippy/tests/ui/mut_mut.stderr25
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init.fixed36
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init.rs36
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init.stderr32
-rw-r--r--src/tools/clippy/tests/ui/needless_lifetimes.fixed37
-rw-r--r--src/tools/clippy/tests/ui/needless_lifetimes.rs37
-rw-r--r--src/tools/clippy/tests/ui/needless_lifetimes.stderr15
-rw-r--r--src/tools/clippy/tests/ui/needless_return.fixed9
-rw-r--r--src/tools/clippy/tests/ui/needless_return.rs9
-rw-r--r--src/tools/clippy/tests/ui/needless_update.rs2
-rw-r--r--src/tools/clippy/tests/ui/new_ret_no_self.rs2
-rw-r--r--src/tools/clippy/tests/ui/no_effect.rs10
-rw-r--r--src/tools/clippy/tests/ui/no_effect.stderr66
-rw-r--r--src/tools/clippy/tests/ui/no_mangle_with_rust_abi.fixed48
-rw-r--r--src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs2
-rw-r--r--src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr74
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool.rs47
-rw-r--r--src/tools/clippy/tests/ui/option_env_unwrap.rs24
-rw-r--r--src/tools/clippy/tests/ui/option_env_unwrap.stderr42
-rw-r--r--src/tools/clippy/tests/ui/overflow_check_conditional.rs9
-rw-r--r--src/tools/clippy/tests/ui/overflow_check_conditional.stderr16
-rw-r--r--src/tools/clippy/tests/ui/print_literal.rs4
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.fixed16
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.rs16
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.stderr25
-rw-r--r--src/tools/clippy/tests/ui/redundant_async_block.fixed193
-rw-r--r--src/tools/clippy/tests/ui/redundant_async_block.rs193
-rw-r--r--src/tools/clippy/tests/ui/redundant_async_block.stderr74
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed1
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs1
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr12
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed4
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr4
-rw-r--r--src/tools/clippy/tests/ui/single_component_path_imports.fixed6
-rw-r--r--src/tools/clippy/tests/ui/single_component_path_imports.rs6
-rw-r--r--src/tools/clippy/tests/ui/single_component_path_imports.stderr4
-rw-r--r--src/tools/clippy/tests/ui/single_match_else.rs6
-rw-r--r--src/tools/clippy/tests/ui/string_add.rs11
-rw-r--r--src/tools/clippy/tests/ui/suspicious_doc_comments.fixed81
-rw-r--r--src/tools/clippy/tests/ui/suspicious_doc_comments.rs81
-rw-r--r--src/tools/clippy/tests/ui/suspicious_doc_comments.stderr114
-rw-r--r--src/tools/clippy/tests/ui/suspicious_doc_comments_unfixable.rs16
-rw-r--r--src/tools/clippy/tests/ui/suspicious_doc_comments_unfixable.stderr37
-rw-r--r--src/tools/clippy/tests/ui/swap.fixed21
-rw-r--r--src/tools/clippy/tests/ui/swap.rs15
-rw-r--r--src/tools/clippy/tests/ui/swap.stderr58
-rw-r--r--src/tools/clippy/tests/ui/tests_outside_test_module.rs18
-rw-r--r--src/tools/clippy/tests/ui/tests_outside_test_module.stderr11
-rw-r--r--src/tools/clippy/tests/ui/toplevel_ref_arg.fixed24
-rw-r--r--src/tools/clippy/tests/ui/toplevel_ref_arg.rs24
-rw-r--r--src/tools/clippy/tests/ui/toplevel_ref_arg.stderr21
-rw-r--r--src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs22
-rw-r--r--src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr7
-rw-r--r--src/tools/clippy/tests/ui/trailing_empty_array.rs2
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed8
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs8
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr8
-rw-r--r--src/tools/clippy/tests/ui/try_err.fixed65
-rw-r--r--src/tools/clippy/tests/ui/try_err.rs65
-rw-r--r--src/tools/clippy/tests/ui/try_err.stderr32
-rw-r--r--src/tools/clippy/tests/ui/uninit.rs39
-rw-r--r--src/tools/clippy/tests/ui/uninit.stderr14
-rw-r--r--src/tools/clippy/tests/ui/uninit_vec.rs35
-rw-r--r--src/tools/clippy/tests/ui/uninit_vec.stderr33
-rw-r--r--src/tools/clippy/tests/ui/uninlined_format_args.fixed94
-rw-r--r--src/tools/clippy/tests/ui/uninlined_format_args.rs92
-rw-r--r--src/tools/clippy/tests/ui/uninlined_format_args.stderr14
-rw-r--r--src/tools/clippy/tests/ui/unit_arg.rs6
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_box_returns.rs60
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_box_returns.stderr35
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed6
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs6
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.fixed10
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.rs10
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.stderr46
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed73
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs77
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_struct_initialization.stderr46
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs12
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_unsafety_doc.stderr2
-rw-r--r--src/tools/clippy/tests/ui/unused_format_specs.fixed18
-rw-r--r--src/tools/clippy/tests/ui/unused_format_specs.rs18
-rw-r--r--src/tools/clippy/tests/ui/unused_format_specs.stderr54
-rw-r--r--src/tools/clippy/tests/ui/unused_format_specs_unfixable.stderr12
-rw-r--r--src/tools/clippy/tests/ui/use_self.fixed10
-rw-r--r--src/tools/clippy/tests/ui/use_self.rs10
-rw-r--r--src/tools/clippy/tests/workspace.rs2
-rw-r--r--src/tools/clippy/triagebot.toml1
420 files changed, 10115 insertions, 4034 deletions
diff --git a/src/tools/clippy/.cargo/config.toml b/src/tools/clippy/.cargo/config.toml
index f3dd9275a..4d80d3ce6 100644
--- a/src/tools/clippy/.cargo/config.toml
+++ b/src/tools/clippy/.cargo/config.toml
@@ -11,3 +11,6 @@ target-dir = "target"
[unstable]
binary-dep-depinfo = true
+
+[profile.dev]
+split-debuginfo = "unpacked"
diff --git a/src/tools/clippy/.editorconfig b/src/tools/clippy/.editorconfig
index ec6e107d5..bc7642bf8 100644
--- a/src/tools/clippy/.editorconfig
+++ b/src/tools/clippy/.editorconfig
@@ -11,6 +11,7 @@ trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 4
+max_line_length = 120
[*.md]
# double whitespace at end of line
diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml
index 24e677ce8..93198aabd 100644
--- a/src/tools/clippy/.github/workflows/clippy_bors.yml
+++ b/src/tools/clippy/.github/workflows/clippy_bors.yml
@@ -180,6 +180,8 @@ jobs:
# Run
- name: Build Integration Test
+ env:
+ CARGO_PROFILE_DEV_SPLIT_DEBUGINFO: off
run: cargo test --test integration --features integration --no-run
# Upload
diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml
index 81ef072bb..116058b7c 100644
--- a/src/tools/clippy/.github/workflows/remark.yml
+++ b/src/tools/clippy/.github/workflows/remark.yml
@@ -29,7 +29,7 @@ jobs:
- name: Install mdbook
run: |
mkdir mdbook
- curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.18/mdbook-v0.4.18-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
+ curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.28/mdbook-v0.4.28-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
echo `pwd`/mdbook >> $GITHUB_PATH
# Run
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 765826ed8..559b560dd 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -6,11 +6,158 @@ document.
## Unreleased / Beta / In Rust Nightly
-[d822110d...master](https://github.com/rust-lang/rust-clippy/compare/d822110d...master)
+[7f27e2e7...master](https://github.com/rust-lang/rust-clippy/compare/7f27e2e7...master)
+
+## Rust 1.68
+
+Current stable, released 2023-03-09
+
+[d822110d...7f27e2e7](https://github.com/rust-lang/rust-clippy/compare/d822110d...7f27e2e7)
+
+### New Lints
+
+* [`permissions_set_readonly_false`]
+ [#10063](https://github.com/rust-lang/rust-clippy/pull/10063)
+* [`almost_complete_range`]
+ [#10043](https://github.com/rust-lang/rust-clippy/pull/10043)
+* [`size_of_ref`]
+ [#10098](https://github.com/rust-lang/rust-clippy/pull/10098)
+* [`semicolon_outside_block`]
+ [#9826](https://github.com/rust-lang/rust-clippy/pull/9826)
+* [`semicolon_inside_block`]
+ [#9826](https://github.com/rust-lang/rust-clippy/pull/9826)
+* [`transmute_null_to_fn`]
+ [#10099](https://github.com/rust-lang/rust-clippy/pull/10099)
+* [`fn_null_check`]
+ [#10099](https://github.com/rust-lang/rust-clippy/pull/10099)
+
+### Moves and Deprecations
+
+* Moved [`manual_clamp`] to `nursery` (Now allow-by-default)
+ [#10101](https://github.com/rust-lang/rust-clippy/pull/10101)
+* Moved [`mutex_atomic`] to `restriction`
+ [#10115](https://github.com/rust-lang/rust-clippy/pull/10115)
+* Renamed `derive_hash_xor_eq` to [`derived_hash_with_manual_eq`]
+ [#10184](https://github.com/rust-lang/rust-clippy/pull/10184)
+
+### Enhancements
+
+* [`collapsible_str_replace`]: Now takes MSRV into consideration. The minimal version is 1.58
+ [#10047](https://github.com/rust-lang/rust-clippy/pull/10047)
+* [`unused_self`]: No longer lints, if the method body contains a `todo!()` call
+ [#10166](https://github.com/rust-lang/rust-clippy/pull/10166)
+* [`derivable_impls`]: Now suggests deriving `Default` for enums with default unit variants
+ [#10161](https://github.com/rust-lang/rust-clippy/pull/10161)
+* [`arithmetic_side_effects`]: Added two new config values
+ `arithmetic-side-effects-allowed-binary` and `arithmetic-side-effects-allowed-unary`
+ to allow operation on user types
+ [#9840](https://github.com/rust-lang/rust-clippy/pull/9840)
+* [`large_const_arrays`], [`large_stack_arrays`]: avoid integer overflow when calculating
+ total array size
+ [#10103](https://github.com/rust-lang/rust-clippy/pull/10103)
+* [`indexing_slicing`]: add new config `suppress-restriction-lint-in-const` to enable
+ restriction lints, even if the suggestion might not be applicable
+ [#9920](https://github.com/rust-lang/rust-clippy/pull/9920)
+* [`needless_borrow`], [`redundant_clone`]: Now track references better and detect more cases
+ [#9701](https://github.com/rust-lang/rust-clippy/pull/9701)
+* [`derived_hash_with_manual_eq`]: Now allows `#[derive(PartialEq)]` with custom `Hash`
+ implementations
+ [#10184](https://github.com/rust-lang/rust-clippy/pull/10184)
+* [`manual_is_ascii_check`]: Now detects ranges with `.contains()` calls
+ [#10053](https://github.com/rust-lang/rust-clippy/pull/10053)
+* [`transmuting_null`]: Now detects `const` pointers to all types
+ [#10099](https://github.com/rust-lang/rust-clippy/pull/10099)
+* [`needless_return`]: Now detects more cases for returns of owned values
+ [#10110](https://github.com/rust-lang/rust-clippy/pull/10110)
+
+### False Positive Fixes
+
+* [`field_reassign_with_default`]: No longer lints cases, where values are initializes from
+ closures capturing struct values
+ [#10143](https://github.com/rust-lang/rust-clippy/pull/10143)
+* [`seek_to_start_instead_of_rewind`]: No longer lints, if the return of `seek` is used.
+ [#10096](https://github.com/rust-lang/rust-clippy/pull/10096)
+* [`manual_filter`]: Now ignores if expressions where the else branch has side effects or
+ doesn't return `None`
+ [#10091](https://github.com/rust-lang/rust-clippy/pull/10091)
+* [`implicit_clone`]: No longer lints if the type doesn't implement clone
+ [#10022](https://github.com/rust-lang/rust-clippy/pull/10022)
+* [`match_wildcard_for_single_variants`]: No longer lints on wildcards with a guard
+ [#10056](https://github.com/rust-lang/rust-clippy/pull/10056)
+* [`drop_ref`]: No longer lints idiomatic expression in `match` arms
+ [#10142](https://github.com/rust-lang/rust-clippy/pull/10142)
+* [`arithmetic_side_effects`]: No longer lints on corner cases with negative number literals
+ [#9867](https://github.com/rust-lang/rust-clippy/pull/9867)
+* [`string_lit_as_bytes`]: No longer lints in scrutinies of `match` statements
+ [#10012](https://github.com/rust-lang/rust-clippy/pull/10012)
+* [`manual_assert`]: No longer lints in `else if` statements
+ [#10013](https://github.com/rust-lang/rust-clippy/pull/10013)
+* [`needless_return`]: don't lint when using `do yeet`
+ [#10109](https://github.com/rust-lang/rust-clippy/pull/10109)
+* All lints: No longer lint in enum discriminant values when the suggestion won't work in a
+ const context
+ [#10008](https://github.com/rust-lang/rust-clippy/pull/10008)
+* [`single_element_loop`]: No longer lints, if the loop contains a `break` or `continue`
+ [#10162](https://github.com/rust-lang/rust-clippy/pull/10162)
+* [`uninlined_format_args`]: No longer suggests inlining arguments in `assert!` and
+ `debug_assert!` macros before 2021 edition
+ [#10055](https://github.com/rust-lang/rust-clippy/pull/10055)
+* [`explicit_counter_loop`]: No longer ignores counter changes after `continue` expressions
+ [#10094](https://github.com/rust-lang/rust-clippy/pull/10094)
+* [`from_over_into`]: No longer lints on opaque types
+ [#9982](https://github.com/rust-lang/rust-clippy/pull/9982)
+* [`expl_impl_clone_on_copy`]: No longer lints on `#[repr(packed)]` structs with generic
+ parameters
+ [#10189](https://github.com/rust-lang/rust-clippy/pull/10189)
+
+### Suggestion Fixes/Improvements
+
+* [`zero_ptr`]: Now suggests `core::` paths for `no_std` crates
+ [#10023](https://github.com/rust-lang/rust-clippy/pull/10023)
+* [`useless_conversion`]: Now suggests removing calls to `into_iter()` on an expression
+ implementing `Iterator`
+ [#10020](https://github.com/rust-lang/rust-clippy/pull/10020)
+* [`box_default`]: The suggestion now uses short paths
+ [#10153](https://github.com/rust-lang/rust-clippy/pull/10153)
+* [`default_trait_access`], [`clone_on_copy`]: The suggestion now uses short paths
+ [#10160](https://github.com/rust-lang/rust-clippy/pull/10160)
+* [`comparison_to_empty`]: The suggestion now removes unused deref operations
+ [#9962](https://github.com/rust-lang/rust-clippy/pull/9962)
+* [`manual_let_else`]: Suggestions for or-patterns now include required brackets.
+ [#9966](https://github.com/rust-lang/rust-clippy/pull/9966)
+* [`match_single_binding`]: suggestion no longer introduces unneeded semicolons
+ [#10060](https://github.com/rust-lang/rust-clippy/pull/10060)
+* [`case_sensitive_file_extension_comparisons`]: Now displays a suggestion with `Path`
+ [#10107](https://github.com/rust-lang/rust-clippy/pull/10107)
+* [`empty_structs_with_brackets`]: The suggestion is no longer machine applicable, to avoid
+ errors when accessing struct fields
+ [#10141](https://github.com/rust-lang/rust-clippy/pull/10141)
+* [`identity_op`]: Removes borrows in the suggestion when needed
+ [#10004](https://github.com/rust-lang/rust-clippy/pull/10004)
+* [`suboptimal_flops`]: The suggestion now includes parentheses when required
+ [#10113](https://github.com/rust-lang/rust-clippy/pull/10113)
+* [`iter_kv_map`]: Now handles `mut` and reference annotations in the suggestion
+ [#10159](https://github.com/rust-lang/rust-clippy/pull/10159)
+* [`redundant_static_lifetimes`]: The suggestion no longer removes `mut` from references
+ [#10006](https://github.com/rust-lang/rust-clippy/pull/10006)
+
+### ICE Fixes
+
+* [`new_ret_no_self`]: Now avoids a stack overflow for `impl Trait` types
+ [#10086](https://github.com/rust-lang/rust-clippy/pull/10086)
+* [`unnecessary_to_owned`]: Now handles compiler generated notes better
+ [#10027](https://github.com/rust-lang/rust-clippy/pull/10027)
+
+### Others
+
+* `SYSROOT` and `--sysroot` can now be set at the same time
+ [#10149](https://github.com/rust-lang/rust-clippy/pull/10149)
+* Fix error when providing an `array-size-threshold` in `clippy.toml`
+ [#10423](https://github.com/rust-lang/rust-clippy/pull/10423)
## Rust 1.67
-Current stable, released 2023-01-26
+Released 2023-01-26
[4f142aa1...d822110d](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...d822110d)
@@ -41,8 +188,6 @@ Current stable, released 2023-01-26
### Moves and Deprecations
-* Moved [`uninlined_format_args`] to `style` (Now warn-by-default)
- [#9865](https://github.com/rust-lang/rust-clippy/pull/9865)
* Moved [`needless_collect`] to `nursery` (Now allow-by-default)
[#9705](https://github.com/rust-lang/rust-clippy/pull/9705)
* Moved [`or_fun_call`] to `nursery` (Now allow-by-default)
@@ -278,7 +423,7 @@ Released 2022-12-15
[#9490](https://github.com/rust-lang/rust-clippy/pull/9490)
* [`almost_complete_letter_range`]: No longer lints in external macros
[#9467](https://github.com/rust-lang/rust-clippy/pull/9467)
-* [`drop_copy`]: No longer lints on idiomatic cases in match arms
+* [`drop_copy`]: No longer lints on idiomatic cases in match arms
[#9491](https://github.com/rust-lang/rust-clippy/pull/9491)
* [`question_mark`]: No longer lints in const context
[#9487](https://github.com/rust-lang/rust-clippy/pull/9487)
@@ -4237,6 +4382,7 @@ Released 2018-09-13
<!-- begin autogenerated links to lint list -->
[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
[`alloc_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#alloc_instead_of_core
+[`allow_attributes`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes
[`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason
[`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range
[`almost_complete_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range
@@ -4295,6 +4441,7 @@ Released 2018-09-13
[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
[`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions
+[`clear_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#clear_with_drain
[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
[`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
@@ -4307,6 +4454,7 @@ Released 2018-09-13
[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
[`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace
+[`collection_is_never_read`]: https://rust-lang.github.io/rust-clippy/master/index.html#collection_is_never_read
[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
@@ -4485,6 +4633,7 @@ Released 2018-09-13
[`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_futures`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_futures
[`large_include_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file
[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
@@ -4497,6 +4646,8 @@ Released 2018-09-13
[`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
[`let_underscore_untyped`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped
[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
+[`let_with_type_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_with_type_underscore
+[`lines_filter_map_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok
[`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
@@ -4514,6 +4665,7 @@ Released 2018-09-13
[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
[`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else
+[`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
@@ -4522,6 +4674,7 @@ Released 2018-09-13
[`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid
[`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
+[`manual_slice_size_calculation`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_size_calculation
[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
[`manual_string_new`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new
@@ -4560,6 +4713,7 @@ Released 2018-09-13
[`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order
[`misnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#misnamed_getters
[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
+[`missing_assert_message`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_assert_message
[`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_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
@@ -4689,6 +4843,7 @@ Released 2018-09-13
[`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec
[`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
+[`redundant_async_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_async_block
[`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
@@ -4770,6 +4925,7 @@ Released 2018-09-13
[`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_command_arg_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_command_arg_space
+[`suspicious_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_doc_comments
[`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
@@ -4782,6 +4938,7 @@ Released 2018-09-13
[`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
+[`tests_outside_test_module`]: https://rust-lang.github.io/rust-clippy/master/index.html#tests_outside_test_module
[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
[`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
@@ -4823,6 +4980,7 @@ Released 2018-09-13
[`unit_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash
[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
+[`unnecessary_box_returns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns
[`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_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map
@@ -4836,6 +4994,7 @@ Released 2018-09-13
[`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc
[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
+[`unnecessary_struct_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_struct_initialization
[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned
[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md
index 3158080d2..3df132803 100644
--- a/src/tools/clippy/CONTRIBUTING.md
+++ b/src/tools/clippy/CONTRIBUTING.md
@@ -50,7 +50,7 @@ a [developer guide] and is a good place to start your journey.
All issues on Clippy are mentored, if you want help simply ask someone from the
Clippy team directly by mentioning them in the issue or over on [Zulip]. All
currently active team members can be found
-[here](https://github.com/rust-lang/highfive/blob/master/highfive/configs/rust-lang/rust-clippy.json#L3)
+[here](https://github.com/rust-lang/rust-clippy/blob/master/triagebot.toml#L18)
Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy
issues. You can use `@rustbot claim` to assign the issue to yourself.
diff --git a/src/tools/clippy/COPYRIGHT b/src/tools/clippy/COPYRIGHT
index a6be75b5e..82703b18f 100644
--- a/src/tools/clippy/COPYRIGHT
+++ b/src/tools/clippy/COPYRIGHT
@@ -1,3 +1,5 @@
+// REUSE-IgnoreStart
+
Copyright 2014-2022 The Rust Project Developers
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
@@ -5,3 +7,5 @@ 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.
+
+// REUSE-IgnoreEnd
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index 70d126809..c35dfcbd8 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clippy"
-version = "0.1.69"
+version = "0.1.70"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md
index 3e7379ace..85798e0e8 100644
--- a/src/tools/clippy/README.md
+++ b/src/tools/clippy/README.md
@@ -11,7 +11,7 @@ Lints are divided into categories, each with a default [lint level](https://doc.
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
| Category | Description | Default level |
-| --------------------- | ----------------------------------------------------------------------------------- | ------------- |
+|-----------------------|-------------------------------------------------------------------------------------|---------------|
| `clippy::all` | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** |
| `clippy::correctness` | code that is outright wrong or useless | **deny** |
| `clippy::suspicious` | code that is most likely wrong or useless | **warn** |
@@ -130,7 +130,7 @@ for example.
You can add Clippy to Travis CI in the same way you use it locally:
-```yml
+```yaml
language: rust
rust:
- stable
@@ -253,7 +253,7 @@ rust-version = "1.30"
The MSRV can also be specified as an attribute, like below.
-```rust
+```rust,ignore
#![feature(custom_inner_attributes)]
#![clippy::msrv = "1.30.0"]
@@ -275,6 +275,8 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT
## License
+<!-- REUSE-IgnoreStart -->
+
Copyright 2014-2022 The Rust Project Developers
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
@@ -282,3 +284,5 @@ Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
<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.
+
+<!-- REUSE-IgnoreEnd -->
diff --git a/src/tools/clippy/book/src/README.md b/src/tools/clippy/book/src/README.md
index df4a1f270..3b6270962 100644
--- a/src/tools/clippy/book/src/README.md
+++ b/src/tools/clippy/book/src/README.md
@@ -14,7 +14,7 @@ much Clippy is supposed to ~~annoy~~ help you by changing the lint level by
category.
| Category | Description | Default level |
-| --------------------- | ----------------------------------------------------------------------------------- | ------------- |
+|-----------------------|-------------------------------------------------------------------------------------|---------------|
| `clippy::all` | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** |
| `clippy::correctness` | code that is outright wrong or useless | **deny** |
| `clippy::suspicious` | code that is most likely wrong or useless | **warn** |
diff --git a/src/tools/clippy/book/src/SUMMARY.md b/src/tools/clippy/book/src/SUMMARY.md
index 0649f7a63..cbd73376d 100644
--- a/src/tools/clippy/book/src/SUMMARY.md
+++ b/src/tools/clippy/book/src/SUMMARY.md
@@ -13,6 +13,7 @@
- [Development](development/README.md)
- [Basics](development/basics.md)
- [Adding Lints](development/adding_lints.md)
+ - [Type Checking](development/type_checking.md)
- [Common Tools](development/common_tools_writing_lints.md)
- [Infrastructure](development/infrastructure/README.md)
- [Syncing changes between Clippy and rust-lang/rust](development/infrastructure/sync.md)
diff --git a/src/tools/clippy/book/src/configuration.md b/src/tools/clippy/book/src/configuration.md
index 87f4a697a..1304f6a8c 100644
--- a/src/tools/clippy/book/src/configuration.md
+++ b/src/tools/clippy/book/src/configuration.md
@@ -3,7 +3,7 @@
> **Note:** The configuration file is unstable and may be deprecated in the future.
Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a
-basic `variable = value` mapping eg.
+basic `variable = value` mapping e.g.
```toml
avoid-breaking-exported-api = false
@@ -60,7 +60,7 @@ And to warn on `lint_name`, run
cargo clippy -- -W clippy::lint_name
```
-This also works with lint groups. For example you can run Clippy with warnings for all lints enabled:
+This also works with lint groups. For example, you can run Clippy with warnings for all lints enabled:
```terminal
cargo clippy -- -W clippy::pedantic
@@ -84,7 +84,7 @@ msrv = "1.30.0"
The MSRV can also be specified as an attribute, like below.
-```rust
+```rust,ignore
#![feature(custom_inner_attributes)]
#![clippy::msrv = "1.30.0"]
@@ -96,7 +96,28 @@ fn main() {
You can also omit the patch version when specifying the MSRV, so `msrv = 1.30`
is equivalent to `msrv = 1.30.0`.
-Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly.
+Note: `custom_inner_attributes` is an unstable feature, so it has to be enabled explicitly.
Lints that recognize this configuration option can be
found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv)
+
+### Disabling evaluation of certain code
+
+> **Note:** This should only be used in cases where other solutions, like `#[allow(clippy::all)]`, are not sufficient.
+
+Very rarely, you may wish to prevent Clippy from evaluating certain sections of code entirely. You can do this with
+[conditional compilation](https://doc.rust-lang.org/reference/conditional-compilation.html) by checking that the
+`cargo-clippy` feature is not set. You may need to provide a stub so that the code compiles:
+
+```rust
+#[cfg(not(feature = "cargo-clippy"))]
+include!(concat!(env!("OUT_DIR"), "/my_big_function-generated.rs"));
+
+#[cfg(feature = "cargo-clippy")]
+fn my_big_function(_input: &str) -> Option<MyStruct> {
+ None
+}
+```
+
+This feature is not actually part of your crate, so specifying `--all-features` to other tools, e.g. `cargo test
+--all-features`, will not disable it.
diff --git a/src/tools/clippy/book/src/development/README.md b/src/tools/clippy/book/src/development/README.md
index 5cf7201cf..616e6d182 100644
--- a/src/tools/clippy/book/src/development/README.md
+++ b/src/tools/clippy/book/src/development/README.md
@@ -5,7 +5,7 @@ making Clippy better by contributing to it. In that case, welcome to the
project!
> _Note:_ If you're just interested in using Clippy, there's nothing to see from
-> this point onward and you should return to one of the earlier chapters.
+> this point onward, and you should return to one of the earlier chapters.
## Getting started
diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md
index f57dc627d..9dacaaaae 100644
--- a/src/tools/clippy/book/src/development/adding_lints.md
+++ b/src/tools/clippy/book/src/development/adding_lints.md
@@ -18,6 +18,7 @@ because that's clearly a non-descriptive name.
- [Cargo lints](#cargo-lints)
- [Rustfix tests](#rustfix-tests)
- [Testing manually](#testing-manually)
+ - [Running directly](#running-directly)
- [Lint declaration](#lint-declaration)
- [Lint registration](#lint-registration)
- [Lint passes](#lint-passes)
@@ -186,6 +187,15 @@ cargo dev lint input.rs
from the working copy root. With tests in place, let's have a look at
implementing our lint now.
+## Running directly
+
+While it's easier to just use `cargo dev lint`, it might be desirable to get
+`target/release/cargo-clippy` and `target/release/clippy-driver` to work as well in some cases.
+By default, they don't work because clippy dynamically links rustc. To help them find rustc,
+add the path printed by`rustc --print target-libdir` (ran inside this workspace so that the rustc version matches)
+to your library search path.
+On linux, this can be done by setting the `LD_LIBRARY_PATH` environment variable to that path.
+
## Lint declaration
Let's start by opening the new file created in the `clippy_lints` crate at
@@ -265,7 +275,7 @@ When declaring a new lint by hand and `cargo dev update_lints` is used, the lint
pass may have to be registered manually in the `register_plugins` function in
`clippy_lints/src/lib.rs`:
-```rust
+```rust,ignore
store.register_early_pass(|| Box::new(foo_functions::FooFunctions));
```
@@ -291,7 +301,7 @@ 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
+`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
@@ -308,7 +318,7 @@ implementation of the lint logic.
Let's start by implementing the `EarlyLintPass` for our `FooFunctions`:
-```rust
+```rust,ignore
impl EarlyLintPass for FooFunctions {
fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
// TODO: Emit lint here
@@ -327,10 +337,10 @@ variety of lint emission functions. They can all be found in
[`clippy_utils/src/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
+provide an extra help message, and we can't really suggest a better name
automatically. This is how it looks:
-```rust
+```rust,ignore
impl EarlyLintPass for FooFunctions {
fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
span_lint_and_help(
@@ -469,7 +479,7 @@ the value from `clippy.toml`. This can be accounted for using the
`extract_msrv_attr!(LintContext)` macro and passing
`LateContext`/`EarlyContext`.
-```rust
+```rust,ignore
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
...
@@ -483,7 +493,7 @@ the lint's test file, `tests/ui/manual_strip.rs` in this example. It should
have a case for the version below the MSRV and one with the same contents but
for the MSRV version itself.
-```rust
+```rust,ignore
...
#[clippy::msrv = "1.44"]
@@ -514,7 +524,7 @@ define_Conf! {
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.
+work for all 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
@@ -607,7 +617,7 @@ output in the `stdout` part.
## PR Checklist
-Before submitting your PR make sure you followed all of the basic requirements:
+Before submitting your PR make sure you followed all the basic requirements:
<!-- Sync this with `.github/PULL_REQUEST_TEMPLATE` -->
@@ -627,7 +637,7 @@ for some users. Adding a configuration is done in the following steps:
1. Adding a new configuration entry to [`clippy_lints::utils::conf`] like this:
- ```rust
+ ```rust,ignore
/// Lint: LINT_NAME.
///
/// <The configuration field doc comment>
@@ -680,7 +690,7 @@ for some users. Adding a configuration is done in the following steps:
configuration value is now cloned or copied into a local value that is then
passed to the impl struct like this:
- ```rust
+ ```rust,ignore
// Default generated registration:
store.register_*_pass(|| box module::StructName);
diff --git a/src/tools/clippy/book/src/development/basics.md b/src/tools/clippy/book/src/development/basics.md
index 6fb53236e..7615dc12f 100644
--- a/src/tools/clippy/book/src/development/basics.md
+++ b/src/tools/clippy/book/src/development/basics.md
@@ -4,8 +4,8 @@ This document explains the basics for hacking on Clippy. Besides others, this
includes how to build and test Clippy. For a more in depth description on the
codebase take a look at [Adding Lints] or [Common Tools].
-[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/adding_lints.md
-[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md
+[Adding Lints]: adding_lints.md
+[Common Tools]: common_tools_writing_lints.md
- [Basics for hacking on Clippy](#basics-for-hacking-on-clippy)
- [Get the Code](#get-the-code)
@@ -125,7 +125,7 @@ We follow a rustc no merge-commit policy. See
## Common Abbreviations
| Abbreviation | Meaning |
-| ------------ | -------------------------------------- |
+|--------------|----------------------------------------|
| UB | Undefined Behavior |
| FP | False Positive |
| FN | False Negative |
diff --git a/src/tools/clippy/book/src/development/common_tools_writing_lints.md b/src/tools/clippy/book/src/development/common_tools_writing_lints.md
index f5aa06e4b..09171d86a 100644
--- a/src/tools/clippy/book/src/development/common_tools_writing_lints.md
+++ b/src/tools/clippy/book/src/development/common_tools_writing_lints.md
@@ -3,7 +3,7 @@
You may need following tooltips to catch up with common operations.
- [Common tools for writing lints](#common-tools-for-writing-lints)
- - [Retrieving the type of an expression](#retrieving-the-type-of-an-expression)
+ - [Retrieving the type of expression](#retrieving-the-type-of-expression)
- [Checking if an expr is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method)
- [Checking for a specific type](#checking-for-a-specific-type)
- [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait)
@@ -16,7 +16,7 @@ Useful Rustc dev guide links:
- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html)
- [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html)
-## Retrieving the type of an expression
+## Retrieving the type of expression
Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for
example to answer following questions:
@@ -45,7 +45,7 @@ impl LateLintPass<'_> for MyStructLint {
}
```
-Similarly in [`TypeckResults`][TypeckResults] methods, you have the
+Similarly, in [`TypeckResults`][TypeckResults] methods, you have the
[`pat_ty()`][pat_ty] method to retrieve a type from a pattern.
Two noticeable items here:
@@ -192,7 +192,7 @@ functions to deal with macros:
- `span.from_expansion()`: detects if a span is from macro expansion or
desugaring. Checking this is a common first step in a lint.
- ```rust
+ ```rust,ignore
if expr.span.from_expansion() {
// just forget it
return;
@@ -203,11 +203,11 @@ functions to deal with macros:
if so, which macro call expanded it. It is sometimes useful to check if the
context of two spans are equal.
- ```rust
+ ```rust,ignore
// expands to `1 + 0`, but don't lint
1 + mac!()
```
- ```rust
+ ```rust,ignore
if left.span.ctxt() != right.span.ctxt() {
// the coder most likely cannot modify this expression
return;
@@ -246,7 +246,7 @@ functions to deal with macros:
`macro_rules!` with `a == $b`, `$b` is expanded to some expression with a
different context from `a`.
- ```rust
+ ```rust,ignore
macro_rules! m {
($a:expr, $b:expr) => {
if $a.is_some() {
diff --git a/src/tools/clippy/book/src/development/infrastructure/book.md b/src/tools/clippy/book/src/development/infrastructure/book.md
index dbd624ecd..de5de4beb 100644
--- a/src/tools/clippy/book/src/development/infrastructure/book.md
+++ b/src/tools/clippy/book/src/development/infrastructure/book.md
@@ -13,7 +13,7 @@ guide to Clippy that you're reading right now. The Clippy book is formatted with
While not strictly necessary since the book source is simply Markdown text
files, having mdBook locally will allow you to build, test and serve the book
locally to view changes before you commit them to the repository. You likely
-already have `cargo` installed, so the easiest option is to simply:
+already have `cargo` installed, so the easiest option is to:
```shell
cargo install mdbook
@@ -26,7 +26,7 @@ instructions for other options.
The book's
[src](https://github.com/rust-lang/rust-clippy/tree/master/book/src)
-directory contains all of the markdown files used to generate the book. If you
+directory contains all the markdown files used to generate the book. If you
want to see your changes in real time, you can use the mdBook `serve` command to
run a web server locally that will automatically update changes as they are
made. From the top level of your `rust-clippy` directory:
diff --git a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md
index d1ac7237b..df9b1bbe1 100644
--- a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md
+++ b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md
@@ -101,7 +101,7 @@ Look for the [`beta-accepted`] label and make sure to also include the PRs with
that label in the changelog. If you can, remove the `beta-accepted` labels
**after** the changelog PR was merged.
-> _Note:_ Some of those PRs might even got backported to the previous `beta`.
+> _Note:_ Some of those PRs might even get backported to the previous `beta`.
> Those have to be included in the changelog of the _previous_ release.
### 4. Update `clippy::version` attributes
diff --git a/src/tools/clippy/book/src/development/infrastructure/release.md b/src/tools/clippy/book/src/development/infrastructure/release.md
index 057228180..98fabf8e8 100644
--- a/src/tools/clippy/book/src/development/infrastructure/release.md
+++ b/src/tools/clippy/book/src/development/infrastructure/release.md
@@ -44,7 +44,7 @@ $ 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
+`HEAD` of the `beta` branch must exist. In addition to that, no files should be
changed by this PR.
## Update the `beta` branch
diff --git a/src/tools/clippy/book/src/development/infrastructure/sync.md b/src/tools/clippy/book/src/development/infrastructure/sync.md
index 02cfc11b5..e1fe92f95 100644
--- a/src/tools/clippy/book/src/development/infrastructure/sync.md
+++ b/src/tools/clippy/book/src/development/infrastructure/sync.md
@@ -19,8 +19,7 @@ to beta. For reference, the first sync following this cadence was performed the
2020-08-27.
This process is described in detail in the following sections. For general
-information about `subtree`s in the Rust repository see [Rust's
-`CONTRIBUTING.md`][subtree].
+information about `subtree`s in the Rust repository see [the rustc-dev-guide][subtree].
## Patching git-subtree to work with big repos
@@ -47,7 +46,7 @@ sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subt
> _Note:_ If you are a Debian user, `dash` is the shell used by default for
> scripts instead of `sh`. This shell has a hardcoded recursion limit set to
-> 1000. In order to make this process work, you need to force the script to run
+> 1,000. In order to make this process work, you need to force the script to run
> `bash` instead. You can do this by editing the first line of the `git-subtree`
> script and changing `sh` to `bash`.
@@ -71,10 +70,10 @@ $ git remote add clippy-local /path/to/rust-clippy
## Performing the sync from [`rust-lang/rust`] to Clippy
-Here is a TL;DR version of the sync process (all of the following commands have
+Here is a TL;DR version of the sync process (all the following commands have
to be run inside the `rust` directory):
-1. Clone the [`rust-lang/rust`] repository or make sure it is up to date.
+1. Clone the [`rust-lang/rust`] repository or make sure it is up-to-date.
2. Checkout the commit from the latest available nightly. You can get it using
`rustup check`.
3. Sync the changes to the rust-copy of Clippy to your Clippy fork:
@@ -107,7 +106,7 @@ to be run inside the `rust` directory):
## Performing the sync from Clippy to [`rust-lang/rust`]
-All of the following commands have to be run inside the `rust` directory.
+All the following commands have to be run inside the `rust` directory.
1. Make sure you have checked out the latest `master` of `rust-lang/rust`.
2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy:
@@ -118,5 +117,5 @@ All of the following commands have to be run inside the `rust` directory.
3. Open a PR to [`rust-lang/rust`]
[gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493
-[subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree
+[subtree]: https://rustc-dev-guide.rust-lang.org/external-repos.html#external-dependencies-subtree
[`rust-lang/rust`]: https://github.com/rust-lang/rust
diff --git a/src/tools/clippy/book/src/development/proposals/README.md b/src/tools/clippy/book/src/development/proposals/README.md
index 78fe34ebf..059c22ce1 100644
--- a/src/tools/clippy/book/src/development/proposals/README.md
+++ b/src/tools/clippy/book/src/development/proposals/README.md
@@ -6,6 +6,6 @@ or around Clippy in the long run.
Besides adding more and more lints and improve the lints that Clippy already
has, Clippy is also interested in making the experience of its users, developers
and maintainers better over time. Projects that address bigger picture things
-like this usually take more time and it is useful to have a proposal for those
+like this usually take more time, and it is useful to have a proposal for those
first. This is the place where such proposals are collected, so that we can
refer to them when working on them.
diff --git a/src/tools/clippy/book/src/development/proposals/roadmap-2021.md b/src/tools/clippy/book/src/development/proposals/roadmap-2021.md
index fe8b080f5..4406616bb 100644
--- a/src/tools/clippy/book/src/development/proposals/roadmap-2021.md
+++ b/src/tools/clippy/book/src/development/proposals/roadmap-2021.md
@@ -52,8 +52,8 @@ In the following, plans to improve the usability are covered.
#### No Output After `cargo check`
-Currently when `cargo clippy` is run after `cargo check`, it does not produce
-any output. This is especially problematic since `rust-analyzer` is on the rise
+Currently, when `cargo clippy` is run after `cargo check`, it does not produce
+any output. This is especially problematic since `rust-analyzer` is on the rise,
and it uses `cargo check` for checking code. A fix is already implemented, but
it still has to be pushed over the finish line. This also includes the
stabilization of the `cargo clippy --fix` command or the support of multi-span
@@ -221,7 +221,7 @@ regarding the user facing issues.
Rust's roadmap process was established by [RFC 1728] in 2016. Since then every
year a roadmap was published, that defined the bigger plans for the coming
-years. This years roadmap can be found [here][Rust Roadmap 2021].
+years. This year roadmap can be found [here][Rust Roadmap 2021].
[RFC 1728]: https://rust-lang.github.io/rfcs/1728-north-star.html
diff --git a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
index c5587c4bf..36d722609 100644
--- a/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
+++ b/src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
@@ -16,7 +16,7 @@ lints. For non-trivial lints, it often requires nested pattern matching of AST /
HIR nodes. For example, testing that an expression is a boolean literal requires
the following checks:
-```rust
+```rust,ignore
if let ast::ExprKind::Lit(lit) = &expr.node {
if let ast::LitKind::Bool(_) = &lit.node {
...
@@ -28,7 +28,7 @@ Writing this kind of matching code quickly becomes a complex task and the
resulting code is often hard to comprehend. The code below shows a simplified
version of the pattern matching required by the `collapsible_if` lint:
-```rust
+```rust,ignore
// simplified version of the collapsible_if lint
if let ast::ExprKind::If(check, then, None) = &expr.node {
if then.stmts.len() == 1 {
@@ -68,13 +68,13 @@ The second part of the motivation is clippy's dependence on unstable
compiler-internal data structures. Clippy lints are currently written against
the compiler's AST / HIR which means that even small changes in these data
structures might break a lot of lints. The second goal of this RFC is to **make
-lints independant of the compiler's AST / HIR data structures**.
+lints independent of the compiler's AST / HIR data structures**.
# Approach
A lot of complexity in writing lints currently seems to come from having to
manually implement the matching logic (see code samples above). It's an
-imparative style that describes *how* to match a syntax tree node instead of
+imperative style that describes *how* to match a syntax tree node instead of
specifying *what* should be matched against declaratively. In other areas, it's
common to use declarative patterns to describe desired information and let the
implementation do the actual matching. A well-known example of this approach are
@@ -111,7 +111,7 @@ expressions that are boolean literals with value `false`.
The pattern can then be used to implement lints in the following way:
-```rust
+```rust,ignore
...
impl EarlyLintPass for MyAwesomeLint {
@@ -270,7 +270,7 @@ pattern!{
// matches if expressions that **may or may not** have an else block
// Attn: `If(_, _, _)` matches only ifs that **have** an else block
//
- // | if with else block | if witout else block
+ // | if with else block | if without else block
// If(_, _, _) | match | no match
// If(_, _, _?) | match | match
// If(_, _, ()) | no match | match
@@ -346,7 +346,7 @@ pattern!{
one could get references to the nodes that matched the subpatterns in the
following way:
-```rust
+```rust,ignore
...
fn check_expr(expr: &syntax::ast::Expr) {
if let Some(result) = my_pattern(expr) {
@@ -372,7 +372,7 @@ matches arrays that consist of any number of literal expressions. Because those
expressions are named `foo`, the result struct contains a `foo` attribute which
is a vector of expressions:
-```rust
+```rust,ignore
...
if let Some(result) = my_pattern_seq(expr) {
result.foo // type: Vec<&syntax::ast::Expr>
@@ -394,7 +394,7 @@ In the pattern above, the `bar` name is only defined if the pattern matches a
boolean literal. If it matches an integer literal, the name isn't set. To
account for this, the result struct's `bar` attribute is an option type:
-```rust
+```rust,ignore
...
if let Some(result) = my_pattern_alt(expr) {
result.bar // type: Option<&bool>
@@ -404,7 +404,7 @@ if let Some(result) = my_pattern_alt(expr) {
It's also possible to use a name in multiple alternation branches if they have
compatible types:
-```rust
+```rust,ignore
pattern!{
// matches if expression is a boolean or integer literal
my_pattern_mult: Expr =
@@ -519,7 +519,7 @@ The `Alt`, `Seq` and `Opt` structs look like these:
> Note: The current implementation can be found
> [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/matchers.rs#L35-L60).
-```rust
+```rust,ignore
pub enum Alt<T> {
Any,
Elmt(Box<T>),
@@ -568,7 +568,7 @@ another example, `Array( Lit(_)* )` is a valid pattern because the parameter of
## The IsMatch Trait
-The pattern syntax and the *PatternTree* are independant of specific syntax tree
+The pattern syntax and the *PatternTree* are independent of specific syntax tree
implementations (rust ast / hir, syn, ...). When looking at the different
pattern examples in the previous sections, it can be seen that the patterns
don't contain any information specific to a certain syntax tree implementation.
@@ -580,7 +580,7 @@ implementations is the `IsMatch` trait. It defines how to match *PatternTree*
nodes against specific syntax tree nodes. A simplified implementation of the
`IsMatch` trait is shown below:
-```rust
+```rust,ignore
pub trait IsMatch<O> {
fn is_match(&self, other: &'o O) -> bool;
}
@@ -619,7 +619,7 @@ approach (matching against the coarse pattern first and checking for additional
properties later) might be slower than the current practice of checking for
structure and additional properties in one pass. For example, the following lint
-```rust
+```rust,ignore
pattern!{
pat_if_without_else: Expr =
If(
@@ -644,7 +644,7 @@ first matches against the pattern and then checks that the `then` block doesn't
start with a comment. Using clippy's current approach, it's possible to check
for these conditions earlier:
-```rust
+```rust,ignore
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
if_chain! {
if let ast::ExprKind::If(ref check, ref then, None) = expr.node;
@@ -708,7 +708,7 @@ is similar to actual Rust syntax (probably like the `quote!` macro). For
example, a pattern that matches `if` expressions that have `false` in their
condition could look like this:
-```rust
+```rust,ignore
if false {
#[*]
}
@@ -717,7 +717,7 @@ if false {
#### Problems
Extending Rust syntax (which is quite complex by itself) with additional syntax
-needed for specifying patterns (alternations, sequences, repetisions, named
+needed for specifying patterns (alternations, sequences, repetitions, named
submatches, ...) might become difficult to read and really hard to parse
properly.
@@ -742,7 +742,7 @@ affects the structure of the resulting AST. `1 + 0 + 0` is parsed as `(1 + 0) +
Another example of a problem would be named submatches. Take a look at this
pattern:
-```rust
+```rust,ignore
fn test() {
1 #foo
}
@@ -858,11 +858,11 @@ would be evaluated as soon as the `Block(_)#then` was matched.
Another idea in this area would be to introduce a syntax for backreferences.
They could be used to require that multiple parts of a pattern should match the
same value. For example, the `assign_op_pattern` lint that searches for `a = a
-op b` and recommends changing it to `a op= b` requires that both occurrances of
+op b` and recommends changing it to `a op= b` requires that both occurrences of
`a` are the same. Using `=#...` as syntax for backreferences, the lint could be
implemented like this:
-```rust
+```rust,ignore
pattern!{
assign_op_pattern: Expr =
Assign(_#target, Binary(_, =#target, _)
@@ -882,7 +882,7 @@ least two return statements" could be a practical addition.
For patterns like "a literal that is not a boolean literal" one currently needs
to list all alternatives except the boolean case. Introducing a negation
operator that allows to write `Lit(!Bool(_))` might be a good idea. This pattern
-would be eqivalent to `Lit( Char(_) | Int(_) )` (given that currently only three
+would be equivalent to `Lit( Char(_) | Int(_) )` (given that currently only three
literal types are implemented).
#### Functional composition
diff --git a/src/tools/clippy/book/src/development/type_checking.md b/src/tools/clippy/book/src/development/type_checking.md
new file mode 100644
index 000000000..5ce434b99
--- /dev/null
+++ b/src/tools/clippy/book/src/development/type_checking.md
@@ -0,0 +1,144 @@
+# Type Checking
+
+When we work on a new lint or improve an existing lint, we might want
+to retrieve the type `Ty` of an expression `Expr` for a variety of
+reasons. This can be achieved by utilizing the [`LateContext`][LateContext]
+that is available for [`LateLintPass`][LateLintPass].
+
+## `LateContext` and `TypeckResults`
+
+The lint context [`LateContext`][LateContext] and [`TypeckResults`][TypeckResults]
+(returned by `LateContext::typeck_results`) are the two most useful data structures
+in `LateLintPass`. They allow us to jump to type definitions and other compilation
+stages such as HIR.
+
+> Note: `LateContext.typeck_results`'s return value is [`TypeckResults`][TypeckResults]
+> and is created in the type checking step, it includes useful information such as types of
+> expressions, ways to resolve methods and so on.
+
+`TypeckResults` contains useful methods such as [`expr_ty`][expr_ty],
+which gives us access to the underlying structure [`Ty`][Ty] of a given expression.
+
+```rust
+pub fn expr_ty(&self, expr: &Expr<'_>) -> Ty<'tcx>
+```
+
+As a side note, besides `expr_ty`, [`TypeckResults`][TypeckResults] contains a
+[`pat_ty()`][pat_ty] method that is useful for retrieving a type from a pattern.
+
+## `Ty`
+
+`Ty` struct contains the type information of an expression.
+Let's take a look at `rustc_middle`'s [`Ty`][Ty] struct to examine this struct:
+
+```rust
+pub struct Ty<'tcx>(Interned<'tcx, WithStableHash<TyS<'tcx>>>);
+```
+
+At a first glance, this struct looks quite esoteric. But at a closer look,
+we will see that this struct contains many useful methods for type checking.
+
+For instance, [`is_char`][is_char] checks if the given `Ty` struct corresponds
+to the primitive character type.
+
+### `is_*` Usage
+
+In some scenarios, all we need to do is check if the `Ty` of an expression
+is a specific type, such as `char` type, so we could write the following:
+
+```rust
+impl LateLintPass<'_> for MyStructLint {
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+ // Get type of `expr`
+ let ty = cx.typeck_results().expr_ty(expr);
+
+ // Check if the `Ty` of this expression is of character type
+ if ty.is_char() {
+ println!("Our expression is a char!");
+ }
+ }
+}
+```
+
+Furthermore, if we examine the [source code][is_char_source] for `is_char`,
+we find something very interesting:
+
+```rust
+#[inline]
+pub fn is_char(self) -> bool {
+ matches!(self.kind(), Char)
+}
+```
+
+Indeed, we just discovered `Ty`'s [`kind` method][kind], which provides us
+with [`TyKind`][TyKind] of a `Ty`.
+
+## `TyKind`
+
+`TyKind` defines the kinds of types in Rust's type system.
+Peeking into [`TyKind` documentation][TyKind], we will see that it is an
+enum of 27 variants, including items such as `Bool`, `Int`, `Ref`, etc.
+
+### `kind` Usage
+
+The `TyKind` of `Ty` can be returned by calling [`Ty.kind` method][kind].
+We often use this method to perform pattern matching in Clippy.
+
+For instance, if we want to check for a `struct`, we could examine if the
+`ty.kind` corresponds to an [`Adt`][Adt] (algebraic data type) and if its
+[`AdtDef`][AdtDef] is a struct:
+
+```rust
+impl LateLintPass<'_> for MyStructLint {
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+ // Get type of `expr`
+ let ty = cx.typeck_results().expr_ty(expr);
+ // Match its kind to enter the type
+ match ty.kind {
+ ty::Adt(adt_def, _) if adt_def.is_struct() => println!("Our `expr` is a struct!"),
+ _ => ()
+ }
+ }
+}
+```
+
+## `hir::Ty` and `ty::Ty`
+
+We've been talking about [`ty::Ty`][middle_ty] this whole time without addressing [`hir::Ty`][hir_ty], but the latter
+is also important to understand.
+
+`hir::Ty` would represent *what* an user wrote, while `ty::Ty` would understand the meaning of it (because it has more
+information).
+
+**Example: `fn foo(x: u32) -> u32 { x }`**
+
+Here the HIR sees the types without "thinking" about them, it knows that the function takes an `u32` and returns
+an `u32`. But at the `ty::Ty` level the compiler understands that they're the same type, in-depth lifetimes, etc...
+
+you can use the [`hir_ty_to_ty`][hir_ty_to_ty] function to convert from a `hir::Ty` to a `ty::Ty`
+
+## Useful Links
+
+Below are some useful links to further explore the concepts covered
+in this chapter:
+
+- [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation)
+- [Diagnostic items](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html)
+- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html)
+- [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html)
+
+[Adt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variant.Adt
+[AdtDef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/adt/struct.AdtDef.html
+[expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty
+[is_char]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.is_char
+[is_char_source]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_middle/ty/sty.rs.html#1831-1834
+[kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.kind
+[LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html
+[LateLintPass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
+[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty
+[Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html
+[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
+[TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html
+[middle_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/ty/struct.Ty.html
+[hir_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/struct.Ty.html
+[hir_ty_to_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir_analysis/fn.hir_ty_to_ty.html
diff --git a/src/tools/clippy/book/src/installation.md b/src/tools/clippy/book/src/installation.md
index cce888b17..d54fff9de 100644
--- a/src/tools/clippy/book/src/installation.md
+++ b/src/tools/clippy/book/src/installation.md
@@ -17,8 +17,8 @@ $ rustup component add clippy [--toolchain=<name>]
## From Source
-Take a look at the [Basics] chapter in the Clippy developer guide to find step
-by step instructions on how to build and install Clippy from source.
+Take a look at the [Basics] chapter in the Clippy developer guide to find step-by-step
+instructions on how to build and install Clippy from source.
[Basics]: development/basics.md#install-from-source
[Usage]: usage.md
diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md
index 995dd2f04..78e1a55cf 100644
--- a/src/tools/clippy/book/src/lint_configuration.md
+++ b/src/tools/clippy/book/src/lint_configuration.md
@@ -54,6 +54,7 @@ Please use that command to update the file and do not edit it by hand.
| [allow-mixed-uninlined-format-args](#allow-mixed-uninlined-format-args) | `true` |
| [suppress-restriction-lint-in-const](#suppress-restriction-lint-in-const) | `false` |
| [missing-docs-in-crate-items](#missing-docs-in-crate-items) | `false` |
+| [future-size-threshold](#future-size-threshold) | `16384` |
### arithmetic-side-effects-allowed
Suppress checking of the passed type names in all types of operations.
@@ -130,6 +131,7 @@ Suppress lints whenever the suggested change would cause breakage for other crat
* [option_option](https://rust-lang.github.io/rust-clippy/master/index.html#option_option)
* [linkedlist](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist)
* [rc_mutex](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex)
+* [unnecessary_box_returns](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns)
### msrv
@@ -193,7 +195,7 @@ The maximum cognitive complexity a function can have
### disallowed-names
The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value
`".."` can be used as part of the list to indicate, that the configured values should be appended to the
-default configuration of Clippy. By default any configuration will replace the default value.
+default configuration of Clippy. By default, any configuration will replace the default value.
**Default Value:** `["foo", "baz", "quux"]` (`Vec<String>`)
@@ -203,7 +205,7 @@ default configuration of Clippy. By default any configuration will replace the d
### doc-valid-idents
The list of words this lint should not consider as identifiers needing ticks. The value
`".."` can be used as part of the list to indicate, that the configured values should be appended to the
-default configuration of Clippy. By default any configuraction will replace the default value. For example:
+default configuration of Clippy. By default, any configuration will replace the default value. For example:
* `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
* `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
@@ -413,7 +415,7 @@ For internal testing only, ignores the current `publish` settings in the Cargo m
Enforce the named macros always use the braces specified.
A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
-is could be used with a full path two `MacroMatcher`s have to be added one with the full path
+could be used with a full path two `MacroMatcher`s have to be added one with the full path
`crate_name::macro_name` and one with just the macro name.
**Default Value:** `[]` (`Vec<crate::nonstandard_macro_braces::MacroMatcher>`)
@@ -447,7 +449,7 @@ Whether to apply the raw pointer heuristic to determine if a type is `Send`.
### max-suggested-slice-pattern-length
When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in
-the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed.
+the slice pattern that is suggested. If more elements are necessary, the lint is suppressed.
For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
**Default Value:** `3` (`u64`)
@@ -519,6 +521,7 @@ for the generic parameters for determining interior mutability
**Default Value:** `["bytes::Bytes"]` (`Vec<String>`)
* [mutable_key_type](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type)
+* [ifs_same_cond](https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond)
### allow-mixed-uninlined-format-args
@@ -550,4 +553,12 @@ crate. For example, `pub(crate)` items.
* [missing_docs_in_private_items](https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items)
+### future-size-threshold
+The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint
+
+**Default Value:** `16384` (`u64`)
+
+* [large_futures](https://rust-lang.github.io/rust-clippy/master/index.html#large_futures)
+
+
diff --git a/src/tools/clippy/book/src/lints.md b/src/tools/clippy/book/src/lints.md
index 35e30960b..442dc6391 100644
--- a/src/tools/clippy/book/src/lints.md
+++ b/src/tools/clippy/book/src/lints.md
@@ -17,7 +17,7 @@ The different lint groups were defined in the [Clippy 1.0 RFC].
The `clippy::correctness` group is the only lint group in Clippy which lints are
deny-by-default and abort the compilation when triggered. This is for good
reason: If you see a `correctness` lint, it means that your code is outright
-wrong or useless and you should try to fix it.
+wrong or useless, and you should try to fix it.
Lints in this category are carefully picked and should be free of false
positives. So just `#[allow]`ing those lints is not recommended.
@@ -41,7 +41,7 @@ simplify your code. It mostly focuses on code that can be written in a shorter
and more readable way, while preserving the semantics.
If you should see a complexity lint, it usually means that you can remove or
-replace some code and it is recommended to do so. However, if you need the more
+replace some code, and it is recommended to do so. However, if you need the more
complex code for some expressiveness reason, it is recommended to allow
complexity lints on a case-by-case basis.
@@ -50,9 +50,9 @@ complexity lints on a case-by-case basis.
The `clippy::perf` group gives you suggestions on how you can increase the
performance of your code. Those lints are mostly about code that the compiler
can't trivially optimize, but has to be written in a slightly different way to
-make the optimizer's job easier.
+make the optimizer job easier.
-Perf lints are usually easy to apply and it is recommended to do so.
+Perf lints are usually easy to apply, and it is recommended to do so.
## Style
@@ -91,7 +91,7 @@ and your use case.
Lints from this group will restrict you in some way. If you enable a restriction
lint for your crate it is recommended to also fix code that this lint triggers
-on. However, those lints are really strict by design and you might want to
+on. However, those lints are really strict by design, and you might want to
`#[allow]` them in some special cases, with a comment justifying that.
## Cargo
diff --git a/src/tools/clippy/book/src/usage.md b/src/tools/clippy/book/src/usage.md
index 61a90445d..32084a919 100644
--- a/src/tools/clippy/book/src/usage.md
+++ b/src/tools/clippy/book/src/usage.md
@@ -19,7 +19,7 @@ cargo clippy
### Lint configuration
The above command will run the default set of lints, which are included in the
-lint group `clippy::all`. You might want to use even more lints or you might not
+lint group `clippy::all`. You might want to use even more lints, or you may not
agree with every Clippy lint, and for that there are ways to configure lint
levels.
@@ -98,7 +98,7 @@ other of Clippy's lint groups.
You can configure lint levels in source code the same way you can configure
`rustc` lints:
-```rust
+```rust,ignore
#![allow(clippy::style)]
#[warn(clippy::double_neg)]
diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs
index e70488165..3a8b070d7 100644
--- a/src/tools/clippy/clippy_dev/src/lib.rs
+++ b/src/tools/clippy/clippy_dev/src/lib.rs
@@ -1,5 +1,5 @@
+#![feature(lazy_cell)]
#![feature(let_chains)]
-#![feature(once_cell)]
#![feature(rustc_private)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
// warn on lints, that are included in `rust-lang/rust`s bootstrap
diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs
index 420214d92..13a277034 100644
--- a/src/tools/clippy/clippy_dev/src/new_lint.rs
+++ b/src/tools/clippy/clippy_dev/src/new_lint.rs
@@ -369,9 +369,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
}}
todo!();
}}
- "#,
- context_import = context_import,
- name_upper = name_upper,
+ "#
);
} else {
let _: fmt::Result = writedoc!(
@@ -385,9 +383,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
pub(super) fn check(cx: &{context_import}) {{
todo!();
}}
- "#,
- context_import = context_import,
- name_upper = name_upper,
+ "#
);
}
diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs
index 779e4d0e1..95222a9ac 100644
--- a/src/tools/clippy/clippy_dev/src/update_lints.rs
+++ b/src/tools/clippy/clippy_dev/src/update_lints.rs
@@ -537,17 +537,13 @@ fn declare_deprecated(name: &str, path: &Path, reason: &str) -> io::Result<()> {
/// Nothing. This lint has been deprecated.
///
/// ### Deprecation reason
- /// {}
- #[clippy::version = \"{}\"]
- pub {},
- \"{}\"
+ /// {deprecation_reason}
+ #[clippy::version = \"{version}\"]
+ pub {name},
+ \"{reason}\"
}}
- ",
- deprecation_reason,
- version,
- name,
- reason,
+ "
)
}
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index 796f1ff16..18e8bf772 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clippy_lints"
-version = "0.1.69"
+version = "0.1.70"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
@@ -9,6 +9,7 @@ keywords = ["clippy", "lint", "plugin"]
edition = "2021"
[dependencies]
+arrayvec = { version = "0.7", default-features = false }
cargo_metadata = "0.15.3"
clippy_utils = { path = "../clippy_utils" }
declare_clippy_lint = { path = "../declare_clippy_lint" }
diff --git a/src/tools/clippy/clippy_lints/src/allow_attributes.rs b/src/tools/clippy/clippy_lints/src/allow_attributes.rs
new file mode 100644
index 000000000..15d46e954
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/allow_attributes.rs
@@ -0,0 +1,71 @@
+use ast::AttrStyle;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use rustc_ast as ast;
+use rustc_errors::Applicability;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// Detects uses of the `#[allow]` attribute and suggests replacing it with
+ /// the `#[expect]` (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html))
+ ///
+ /// The expect attribute is still unstable and requires the `lint_reasons`
+ /// on nightly. It can be enabled by adding `#![feature(lint_reasons)]` to
+ /// the crate root.
+ ///
+ /// This lint only warns outer attributes (`#[allow]`), as inner attributes
+ /// (`#![allow]`) are usually used to enable or disable lints on a global scale.
+ ///
+ /// ### Why is this bad?
+ ///
+ /// `#[expect]` attributes suppress the lint emission, but emit a warning, if
+ /// the expectation is unfulfilled. This can be useful to be notified when the
+ /// lint is no longer triggered.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// #[allow(unused_mut)]
+ /// fn foo() -> usize {
+ /// let mut a = Vec::new();
+ /// a.len()
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// #![feature(lint_reasons)]
+ /// #[expect(unused_mut)]
+ /// fn foo() -> usize {
+ /// let mut a = Vec::new();
+ /// a.len()
+ /// }
+ /// ```
+ #[clippy::version = "1.69.0"]
+ pub ALLOW_ATTRIBUTES,
+ restriction,
+ "`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings."
+}
+
+declare_lint_pass!(AllowAttribute => [ALLOW_ATTRIBUTES]);
+
+impl LateLintPass<'_> for AllowAttribute {
+ // Separate each crate's features.
+ fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
+ if_chain! {
+ if cx.tcx.features().lint_reasons;
+ if let AttrStyle::Outer = attr.style;
+ if let Some(ident) = attr.ident();
+ if ident.name == rustc_span::symbol::sym::allow;
+ then {
+ span_lint_and_sugg(
+ cx,
+ ALLOW_ATTRIBUTES,
+ ident.span,
+ "#[allow] attribute found",
+ "replace it with",
+ "expect".into(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
index 42e14b5cd..32d80f42e 100644
--- a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
+++ b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
@@ -24,7 +24,7 @@ declare_clippy_lint! {
/// ```rust
/// let _ = 'a'..='z';
/// ```
- #[clippy::version = "1.63.0"]
+ #[clippy::version = "1.68.0"]
pub ALMOST_COMPLETE_RANGE,
suspicious,
"almost complete range"
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index e8106beec..455f0df7c 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -7,7 +7,7 @@ use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, Level};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
@@ -430,23 +430,25 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
}
}
let nonminimal_bool_lint = |suggestions: Vec<_>| {
- span_lint_hir_and_then(
- self.cx,
- NONMINIMAL_BOOL,
- e.hir_id,
- 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 self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, e.hir_id).0 != Level::Allow {
+ span_lint_hir_and_then(
+ self.cx,
+ NONMINIMAL_BOOL,
+ e.hir_id,
+ 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 };
@@ -495,18 +497,20 @@ struct NotSimplificationVisitor<'a, 'tcx> {
impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
- if let ExprKind::Unary(UnOp::Not, 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,
- );
- }
+ if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind &&
+ !inner.span.from_expansion() &&
+ let Some(suggestion) = simplify_not(self.cx, inner)
+ && self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
+ {
+ span_lint_and_sugg(
+ self.cx,
+ NONMINIMAL_BOOL,
+ expr.span,
+ "this boolean expression can be simplified",
+ "try",
+ suggestion,
+ Applicability::MachineApplicable,
+ );
}
walk_expr(self, expr);
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
index 823970e35..95c2ecbf7 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -2,8 +2,9 @@ use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::expr_or_init;
use clippy_utils::source::snippet;
+use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize};
-use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_errors::{Applicability, Diagnostic, SuggestionStyle};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::LateContext;
@@ -163,19 +164,34 @@ pub(super) fn check(
_ => return,
};
- let name_of_cast_from = snippet(cx, cast_expr.span, "..");
- let cast_to_snip = snippet(cx, cast_to_span, "..");
- let suggestion = format!("{cast_to_snip}::try_from({name_of_cast_from})");
-
span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg, |diag| {
diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...");
- diag.span_suggestion_with_style(
- expr.span,
- "... or use `try_from` and handle the error accordingly",
- suggestion,
- Applicability::Unspecified,
- // always show the suggestion in a separate line
- SuggestionStyle::ShowAlways,
- );
+ if !cast_from.is_floating_point() {
+ offer_suggestion(cx, expr, cast_expr, cast_to_span, diag);
+ }
});
}
+
+fn offer_suggestion(
+ cx: &LateContext<'_>,
+ expr: &Expr<'_>,
+ cast_expr: &Expr<'_>,
+ cast_to_span: Span,
+ diag: &mut Diagnostic,
+) {
+ let cast_to_snip = snippet(cx, cast_to_span, "..");
+ let suggestion = if cast_to_snip == "_" {
+ format!("{}.try_into()", Sugg::hir(cx, cast_expr, "..").maybe_par())
+ } else {
+ format!("{cast_to_snip}::try_from({})", Sugg::hir(cx, cast_expr, ".."))
+ };
+
+ diag.span_suggestion_with_style(
+ expr.span,
+ "... or use `try_from` and handle the error accordingly",
+ suggestion,
+ Applicability::Unspecified,
+ // always show the suggestion in a separate line
+ SuggestionStyle::ShowAlways,
+ );
+}
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
index 627b795d6..1233c632a 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
@@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
@@ -34,6 +34,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
if let ExprKind::Path(ref qpath) = fun.kind;
if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
if let Some(rpk) = raw_parts_kind(cx, fun_def_id);
+ let ctxt = expr.span.ctxt();
+ if cast_expr.span.ctxt() == ctxt;
then {
let func = match rpk {
RawPartsKind::Immutable => "from_raw_parts",
@@ -41,8 +43,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
};
let span = expr.span;
let mut applicability = Applicability::MachineApplicable;
- let ptr = snippet_with_applicability(cx, ptr_arg.span, "ptr", &mut applicability);
- let len = snippet_with_applicability(cx, len_arg.span, "len", &mut applicability);
+ let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0;
+ let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0;
span_lint_and_sugg(
cx,
CAST_SLICE_FROM_RAW_PARTS,
diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
index e8531157e..a8926b29a 100644
--- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
@@ -143,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity {
span: Span,
def_id: LocalDefId,
) {
- if !cx.tcx.has_attr(def_id.to_def_id(), sym::test) {
+ if !cx.tcx.has_attr(def_id, sym::test) {
let expr = if is_async_fn(kind) {
match get_async_fn_body(cx.tcx, body) {
Some(b) => b,
diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
new file mode 100644
index 000000000..5e2eb5789
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs
@@ -0,0 +1,141 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
+use clippy_utils::visitors::for_each_expr_with_closures;
+use clippy_utils::{get_enclosing_block, get_parent_node, path_to_local_id};
+use core::ops::ControlFlow;
+use rustc_hir::{Block, ExprKind, HirId, LangItem, Local, Node, PatKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::sym;
+use rustc_span::Symbol;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for collections that are never queried.
+ ///
+ /// ### Why is this bad?
+ /// Putting effort into constructing a collection but then never querying it might indicate that
+ /// the author forgot to do whatever they intended to do with the collection. Example: Clone
+ /// a vector, sort it for iteration, but then mistakenly iterate the original vector
+ /// instead.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let samples = vec![3, 1, 2];
+ /// let mut sorted_samples = samples.clone();
+ /// sorted_samples.sort();
+ /// for sample in &samples { // Oops, meant to use `sorted_samples`.
+ /// println!("{sample}");
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # let samples = vec![3, 1, 2];
+ /// let mut sorted_samples = samples.clone();
+ /// sorted_samples.sort();
+ /// for sample in &sorted_samples {
+ /// println!("{sample}");
+ /// }
+ /// ```
+ #[clippy::version = "1.69.0"]
+ pub COLLECTION_IS_NEVER_READ,
+ nursery,
+ "a collection is never queried"
+}
+declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]);
+
+// Add `String` here when it is added to diagnostic items
+static COLLECTIONS: [Symbol; 9] = [
+ sym::BTreeMap,
+ sym::BTreeSet,
+ sym::BinaryHeap,
+ sym::HashMap,
+ sym::HashSet,
+ sym::LinkedList,
+ sym::Option,
+ sym::Vec,
+ sym::VecDeque,
+];
+
+impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
+ fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
+ // Look for local variables whose type is a container. Search surrounding bock for read access.
+ if match_acceptable_type(cx, local, &COLLECTIONS)
+ && let PatKind::Binding(_, local_id, _, _) = local.pat.kind
+ && let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id)
+ && has_no_read_access(cx, local_id, enclosing_block)
+ {
+ span_lint(cx, COLLECTION_IS_NEVER_READ, local.span, "collection is never read");
+ }
+ }
+}
+
+fn match_acceptable_type(cx: &LateContext<'_>, local: &Local<'_>, collections: &[rustc_span::Symbol]) -> bool {
+ let ty = cx.typeck_results().pat_ty(local.pat);
+ collections.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym))
+ // String type is a lang item but not a diagnostic item for now so we need a separate check
+ || is_type_lang_item(cx, ty, LangItem::String)
+}
+
+fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Block<'tcx>) -> bool {
+ let mut has_access = false;
+ let mut has_read_access = false;
+
+ // Inspect all expressions and sub-expressions in the block.
+ for_each_expr_with_closures(cx, block, |expr| {
+ // Ignore expressions that are not simply `id`.
+ if !path_to_local_id(expr, id) {
+ return ControlFlow::Continue(());
+ }
+
+ // `id` is being accessed. Investigate if it's a read access.
+ has_access = true;
+
+ // `id` appearing in the left-hand side of an assignment is not a read access:
+ //
+ // id = ...; // Not reading `id`.
+ if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id)
+ && let ExprKind::Assign(lhs, ..) = parent.kind
+ && path_to_local_id(lhs, id)
+ {
+ return ControlFlow::Continue(());
+ }
+
+ // Look for method call with receiver `id`. It might be a non-read access:
+ //
+ // id.foo(args)
+ //
+ // Only assuming this for "official" methods defined on the type. For methods defined in extension
+ // traits (identified as local, based on the orphan rule), pessimistically assume that they might
+ // have side effects, so consider them a read.
+ if let Some(Node::Expr(parent)) = get_parent_node(cx.tcx, expr.hir_id)
+ && let ExprKind::MethodCall(_, receiver, _, _) = parent.kind
+ && path_to_local_id(receiver, id)
+ && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
+ && !method_def_id.is_local()
+ {
+ // The method call is a statement, so the return value is not used. That's not a read access:
+ //
+ // id.foo(args);
+ if let Some(Node::Stmt(..)) = get_parent_node(cx.tcx, parent.hir_id) {
+ return ControlFlow::Continue(());
+ }
+
+ // The method call is not a statement, so its return value is used somehow but its type is the
+ // unit type, so this is not a real read access. Examples:
+ //
+ // let y = x.clear();
+ // println!("{:?}", x.clear());
+ if cx.typeck_results().expr_ty(parent).is_unit() {
+ return ControlFlow::Continue(());
+ }
+ }
+
+ // Any other access to `id` is a read access. Stop searching.
+ has_read_access = true;
+ ControlFlow::Break(())
+ });
+
+ // Ignore collections that have no access at all. Other lints should catch them.
+ has_access && !has_read_access
+}
diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs
index f10c35cde..970f50049 100644
--- a/src/tools/clippy/clippy_lints/src/copies.rs
+++ b/src/tools/clippy/clippy_lints/src/copies.rs
@@ -1,18 +1,20 @@
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
-use clippy_utils::ty::needs_ordered_drop;
+use clippy_utils::ty::{is_interior_mut_ty, needs_ordered_drop};
use clippy_utils::visitors::for_each_expr;
use clippy_utils::{
- capture_local_usage, eq_expr_value, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause,
- is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
+ capture_local_usage, def_path_def_ids, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt,
+ if_sequence, is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
};
use core::iter;
use core::ops::ControlFlow;
use rustc_errors::Applicability;
+use rustc_hir::def_id::DefIdSet;
use rustc_hir::intravisit;
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_middle::query::Key;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::hygiene::walk_chain;
use rustc_span::source_map::SourceMap;
use rustc_span::{BytePos, Span, Symbol};
@@ -159,7 +161,21 @@ declare_clippy_lint! {
"`if` statement with shared code in all blocks"
}
-declare_lint_pass!(CopyAndPaste => [
+pub struct CopyAndPaste {
+ ignore_interior_mutability: Vec<String>,
+ ignored_ty_ids: DefIdSet,
+}
+
+impl CopyAndPaste {
+ pub fn new(ignore_interior_mutability: Vec<String>) -> Self {
+ Self {
+ ignore_interior_mutability,
+ ignored_ty_ids: DefIdSet::new(),
+ }
+ }
+}
+
+impl_lint_pass!(CopyAndPaste => [
IFS_SAME_COND,
SAME_FUNCTIONS_IN_IF_CONDITION,
IF_SAME_THEN_ELSE,
@@ -167,10 +183,18 @@ declare_lint_pass!(CopyAndPaste => [
]);
impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
+ fn check_crate(&mut self, cx: &LateContext<'tcx>) {
+ for ignored_ty in &self.ignore_interior_mutability {
+ let path: Vec<&str> = ignored_ty.split("::").collect();
+ for id in def_path_def_ids(cx, path.as_slice()) {
+ self.ignored_ty_ids.insert(id);
+ }
+ }
+ }
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) {
let (conds, blocks) = if_sequence(expr);
- lint_same_cond(cx, &conds);
+ lint_same_cond(cx, &conds, &self.ignored_ty_ids);
lint_same_fns_in_if_cond(cx, &conds);
let all_same =
!is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks);
@@ -547,9 +571,39 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo
})
}
+fn method_caller_is_mutable(cx: &LateContext<'_>, caller_expr: &Expr<'_>, ignored_ty_ids: &DefIdSet) -> bool {
+ let caller_ty = cx.typeck_results().expr_ty(caller_expr);
+ // Check if given type has inner mutability and was not set to ignored by the configuration
+ let is_inner_mut_ty = is_interior_mut_ty(cx, caller_ty)
+ && !matches!(caller_ty.ty_adt_id(), Some(adt_id) if ignored_ty_ids.contains(&adt_id));
+
+ is_inner_mut_ty
+ || caller_ty.is_mutable_ptr()
+ // `find_binding_init` will return the binding iff its not mutable
+ || path_to_local(caller_expr)
+ .and_then(|hid| find_binding_init(cx, hid))
+ .is_none()
+}
+
/// Implementation of `IFS_SAME_COND`.
-fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
- for (i, j) in search_same(conds, |e| hash_expr(cx, e), |lhs, rhs| eq_expr_value(cx, lhs, rhs)) {
+fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>], ignored_ty_ids: &DefIdSet) {
+ for (i, j) in search_same(
+ conds,
+ |e| hash_expr(cx, e),
+ |lhs, rhs| {
+ // Ignore eq_expr side effects iff one of the expressin kind is a method call
+ // and the caller is not a mutable, including inner mutable type.
+ if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind {
+ if method_caller_is_mutable(cx, caller, ignored_ty_ids) {
+ false
+ } else {
+ SpanlessEq::new(cx).eq_expr(lhs, rhs)
+ }
+ } else {
+ eq_expr_value(cx, lhs, rhs)
+ }
+ },
+ ) {
span_lint_and_note(
cx,
IFS_SAME_COND,
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index cd5dd7a57..f24dab627 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -35,6 +35,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO,
+ crate::allow_attributes::ALLOW_ATTRIBUTES_INFO,
crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO,
crate::approx_const::APPROX_CONSTANT_INFO,
crate::as_conversions::AS_CONVERSIONS_INFO,
@@ -92,6 +93,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO,
crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO,
crate::collapsible_if::COLLAPSIBLE_IF_INFO,
+ crate::collection_is_never_read::COLLECTION_IS_NEVER_READ_INFO,
crate::comparison_chain::COMPARISON_CHAIN_INFO,
crate::copies::BRANCHES_SHARING_CODE_INFO,
crate::copies::IFS_SAME_COND_INFO,
@@ -216,6 +218,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO,
crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO,
crate::large_enum_variant::LARGE_ENUM_VARIANT_INFO,
+ crate::large_futures::LARGE_FUTURES_INFO,
crate::large_include_file::LARGE_INCLUDE_FILE_INFO,
crate::large_stack_arrays::LARGE_STACK_ARRAYS_INFO,
crate::len_zero::COMPARISON_TO_EMPTY_INFO,
@@ -226,8 +229,10 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::let_underscore::LET_UNDERSCORE_LOCK_INFO,
crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO,
crate::let_underscore::LET_UNDERSCORE_UNTYPED_INFO,
+ crate::let_with_type_underscore::LET_WITH_TYPE_UNDERSCORE_INFO,
crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO,
crate::lifetimes::NEEDLESS_LIFETIMES_INFO,
+ crate::lines_filter_map_ok::LINES_FILTER_MAP_OK_INFO,
crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO,
crate::literal_representation::INCONSISTENT_DIGIT_GROUPING_INFO,
crate::literal_representation::LARGE_DIGIT_GROUPS_INFO,
@@ -260,9 +265,11 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::manual_clamp::MANUAL_CLAMP_INFO,
crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
crate::manual_let_else::MANUAL_LET_ELSE_INFO,
+ crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO,
crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO,
crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO,
crate::manual_retain::MANUAL_RETAIN_INFO,
+ crate::manual_slice_size_calculation::MANUAL_SLICE_SIZE_CALCULATION_INFO,
crate::manual_string_new::MANUAL_STRING_NEW_INFO,
crate::manual_strip::MANUAL_STRIP_INFO,
crate::map_unit_fn::OPTION_MAP_UNIT_FN_INFO,
@@ -303,6 +310,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS_INFO,
crate::methods::CHARS_LAST_CMP_INFO,
crate::methods::CHARS_NEXT_CMP_INFO,
+ crate::methods::CLEAR_WITH_DRAIN_INFO,
crate::methods::CLONED_INSTEAD_OF_COPIED_INFO,
crate::methods::CLONE_DOUBLE_REF_INFO,
crate::methods::CLONE_ON_COPY_INFO,
@@ -416,6 +424,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::misc_early::UNSEPARATED_LITERAL_SUFFIX_INFO,
crate::misc_early::ZERO_PREFIXED_LITERAL_INFO,
crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_INFO,
+ crate::missing_assert_message::MISSING_ASSERT_MESSAGE_INFO,
crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO,
crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO,
crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO,
@@ -517,6 +526,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::ranges::REVERSED_EMPTY_RANGES_INFO,
crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO,
crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO,
+ crate::redundant_async_block::REDUNDANT_ASYNC_BLOCK_INFO,
crate::redundant_clone::REDUNDANT_CLONE_INFO,
crate::redundant_closure_call::REDUNDANT_CLOSURE_CALL_INFO,
crate::redundant_else::REDUNDANT_ELSE_INFO,
@@ -559,6 +569,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::strings::STR_TO_STRING_INFO,
crate::strings::TRIM_SPLIT_WHITESPACE_INFO,
crate::strlen_on_c_strings::STRLEN_ON_C_STRINGS_INFO,
+ crate::suspicious_doc_comments::SUSPICIOUS_DOC_COMMENTS_INFO,
crate::suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS_INFO,
crate::suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL_INFO,
crate::suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL_INFO,
@@ -568,6 +579,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::swap_ptr_to_ref::SWAP_PTR_TO_REF_INFO,
crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO,
crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO,
+ crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO,
crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO,
crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO,
crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO,
@@ -610,8 +622,10 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::unit_types::UNIT_CMP_INFO,
crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO,
crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO,
+ crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO,
crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO,
crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
+ crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO,
crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO,
crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO,
crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs
index 080d44e63..80c22742b 100644
--- a/src/tools/clippy/clippy_lints/src/default.rs
+++ b/src/tools/clippy/clippy_lints/src/default.rs
@@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
-use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::{has_drop, is_copy};
use clippy_utils::{
any_parent_is_automatically_derived, contains_name, get_parent_expr, is_from_proc_macro, match_def_path, paths,
@@ -160,6 +160,8 @@ impl<'tcx> LateLintPass<'tcx> for Default {
}
};
+ let init_ctxt = local.span.ctxt();
+
// find all "later statement"'s where the fields of the binding set as
// Default::default() get reassigned, unless the reassignment refers to the original binding
let mut first_assign = None;
@@ -169,7 +171,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
// find out if and which field was set by this `consecutive_statement`
if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) {
// interrupt and cancel lint if assign_rhs references the original binding
- if contains_name(binding_name, assign_rhs, cx) {
+ if contains_name(binding_name, assign_rhs, cx) || init_ctxt != consecutive_statement.span.ctxt() {
cancel_lint = true;
break;
}
@@ -204,11 +206,12 @@ impl<'tcx> LateLintPass<'tcx> for Default {
.iter()
.all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name));
+ let mut app = Applicability::Unspecified;
let field_list = assigned_fields
.into_iter()
.map(|(field, rhs)| {
// extract and store the assigned value for help message
- let value_snippet = snippet_with_macro_callsite(cx, rhs.span, "..");
+ let value_snippet = snippet_with_context(cx, rhs.span, init_ctxt, "..", &mut app).0;
format!("{field}: {value_snippet}")
})
.collect::<Vec<String>>()
diff --git a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
index 1ad929864..f296b80d2 100644
--- a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
+++ b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
@@ -1,11 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::last_path_segment;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
use clippy_utils::{match_def_path, paths};
use rustc_errors::Applicability;
use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::SyntaxContext;
declare_clippy_lint! {
/// ### What it does
@@ -38,9 +39,11 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
&& let QPath::Resolved(None, path) = ty_path
&& let def::Res::Def(_, def_id) = &path.res
&& match_def_path(cx, *def_id, &paths::ITER_EMPTY)
+ && let ctxt = expr.span.ctxt()
+ && ty.span.ctxt() == ctxt
{
let mut applicability = Applicability::MachineApplicable;
- let sugg = make_sugg(cx, ty_path, &mut applicability);
+ let sugg = make_sugg(cx, ty_path, ctxt, &mut applicability);
span_lint_and_sugg(
cx,
DEFAULT_INSTEAD_OF_ITER_EMPTY,
@@ -54,14 +57,19 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
}
}
-fn make_sugg(cx: &LateContext<'_>, ty_path: &rustc_hir::QPath<'_>, applicability: &mut Applicability) -> String {
+fn make_sugg(
+ cx: &LateContext<'_>,
+ ty_path: &rustc_hir::QPath<'_>,
+ ctxt: SyntaxContext,
+ applicability: &mut Applicability,
+) -> String {
if let Some(last) = last_path_segment(ty_path).args
&& let Some(iter_ty) = last.args.iter().find_map(|arg| match arg {
GenericArg::Type(ty) => Some(ty),
_ => None,
})
{
- format!("std::iter::empty::<{}>()", snippet_with_applicability(cx, iter_ty.span, "..", applicability))
+ format!("std::iter::empty::<{}>()", snippet_with_context(cx, iter_ty.span, ctxt, "..", applicability).0)
} else {
"std::iter::empty()".to_owned()
}
diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
index f95b8ccf0..8f68f90a2 100644
--- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
@@ -8,7 +8,7 @@ use rustc_hir::{
Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, Ty, TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{AdtDef, DefIdTree};
+use rustc_middle::ty::{Adt, AdtDef, SubstsRef};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
@@ -81,13 +81,18 @@ fn check_struct<'tcx>(
self_ty: &Ty<'_>,
func_expr: &Expr<'_>,
adt_def: AdtDef<'_>,
+ substs: SubstsRef<'_>,
) {
if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
- if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() {
- for arg in a.args {
- if !matches!(arg, GenericArg::Lifetime(_)) {
- return;
- }
+ if let Some(PathSegment { args, .. }) = p.segments.last() {
+ let args = args.map(|a| a.args).unwrap_or(&[]);
+
+ // substs contains the generic parameters of the type declaration, while args contains the arguments
+ // used at instantiation time. If both len are not equal, it means that some parameters were not
+ // provided (which means that the default values were used); in this case we will not risk
+ // suggesting too broad a rewrite. We won't either if any argument is a type or a const.
+ if substs.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) {
+ return;
}
}
}
@@ -176,7 +181,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
self_ty,
..
}) = item.kind;
- if !cx.tcx.has_attr(item.owner_id.to_def_id(), sym::automatically_derived);
+ if !cx.tcx.has_attr(item.owner_id, sym::automatically_derived);
if !item.span.from_expansion();
if let Some(def_id) = trait_ref.trait_def_id();
if cx.tcx.is_diagnostic_item(sym::Default, def_id);
@@ -184,7 +189,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir);
if let ImplItemKind::Fn(_, b) = &impl_item.kind;
if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
- if let Some(adt_def) = cx.tcx.type_of(item.owner_id).subst_identity().ty_adt_def();
+ if let &Adt(adt_def, substs) = cx.tcx.type_of(item.owner_id).subst_identity().kind();
if let attrs = cx.tcx.hir().attrs(item.hir_id());
if !attrs.iter().any(|attr| attr.doc_str().is_some());
if let child_attrs = cx.tcx.hir().attrs(impl_item_hir);
@@ -192,7 +197,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
then {
if adt_def.is_struct() {
- check_struct(cx, item, self_ty, func_expr, adt_def);
+ check_struct(cx, item, self_ty, func_expr, adt_def, substs);
} else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) {
check_enum(cx, item, func_expr, adt_def);
}
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index b8428d66a..f425dd5fb 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -24,8 +24,8 @@ use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
- /// Checks for deriving `Hash` but implementing `PartialEq`
- /// explicitly or vice versa.
+ /// Lints against manual `PartialEq` implementations for types with a derived `Hash`
+ /// implementation.
///
/// ### Why is this bad?
/// The implementation of these traits must agree (for
@@ -54,8 +54,8 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
- /// Checks for deriving `Ord` but implementing `PartialOrd`
- /// explicitly or vice versa.
+ /// Lints against manual `PartialOrd` and `Ord` implementations for types with a derived `Ord`
+ /// or `PartialOrd` implementation.
///
/// ### Why is this bad?
/// The implementation of these traits must agree (for
@@ -212,7 +212,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive {
}) = item.kind
{
let ty = cx.tcx.type_of(item.owner_id).subst_identity();
- let is_automatically_derived = cx.tcx.has_attr(item.owner_id.to_def_id(), sym::automatically_derived);
+ let is_automatically_derived = cx.tcx.has_attr(item.owner_id, sym::automatically_derived);
check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
index 084190f00..c9fad98e4 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
@@ -32,7 +32,7 @@ declare_clippy_lint! {
/// ### Example
/// ```rust
/// // Assuming that `clippy.toml` contains the following line:
- /// // allowed-locales = ["Latin", "Cyrillic"]
+ /// // allowed-scripts = ["Latin", "Cyrillic"]
/// let counter = 10; // OK, latin is allowed.
/// let счётчик = 10; // OK, cyrillic is allowed.
/// let zähler = 10; // OK, it's still latin.
diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs
index 9c8b0d076..8ba6a9e48 100644
--- a/src/tools/clippy/clippy_lints/src/exit.rs
+++ b/src/tools/clippy/clippy_lints/src/exit.rs
@@ -11,7 +11,7 @@ declare_clippy_lint! {
///
/// ### Why is this bad?
/// Exit terminates the program at the location it is called. For unrecoverable
- /// errors `panics` should be used to provide a stacktrace and potentualy other
+ /// errors `panics` should be used to provide a stacktrace and potentially other
/// information. A normal termination or one with an error code should happen in
/// the main function.
///
diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs
index c0ea6f338..315df6c71 100644
--- a/src/tools/clippy/clippy_lints/src/explicit_write.rs
+++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs
@@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::macros::FormatArgsExpn;
+use clippy_utils::macros::{find_format_args, format_args_inputs_span};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{is_expn_of, match_function_call, paths};
use if_chain::if_chain;
@@ -8,7 +8,7 @@ use rustc_hir::def::Res;
use rustc_hir::{BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
+use rustc_span::{sym, ExpnId};
declare_clippy_lint! {
/// ### What it does
@@ -43,23 +43,22 @@ declare_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]);
impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if_chain! {
- // match call to unwrap
- if let ExprKind::MethodCall(unwrap_fun, write_call, [], _) = expr.kind;
- if unwrap_fun.ident.name == sym::unwrap;
+ // match call to unwrap
+ if let ExprKind::MethodCall(unwrap_fun, write_call, [], _) = expr.kind
+ && unwrap_fun.ident.name == sym::unwrap
// match call to write_fmt
- if let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = look_in_block(cx, &write_call.kind);
- if write_fun.ident.name == sym!(write_fmt);
+ && let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = look_in_block(cx, &write_call.kind)
+ && write_fun.ident.name == sym!(write_fmt)
// match calls to std::io::stdout() / std::io::stderr ()
- if let Some(dest_name) = if match_function_call(cx, write_recv, &paths::STDOUT).is_some() {
+ && let Some(dest_name) = if match_function_call(cx, write_recv, &paths::STDOUT).is_some() {
Some("stdout")
} else if match_function_call(cx, write_recv, &paths::STDERR).is_some() {
Some("stderr")
} else {
None
- };
- if let Some(format_args) = FormatArgsExpn::parse(cx, write_arg);
- then {
+ }
+ {
+ find_format_args(cx, write_arg, ExpnId::root(), |format_args| {
let calling_macro =
// ordering is important here, since `writeln!` uses `write!` internally
if is_expn_of(write_call.span, "writeln").is_some() {
@@ -92,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
let mut applicability = Applicability::MachineApplicable;
let inputs_snippet = snippet_with_applicability(
cx,
- format_args.inputs_span(),
+ format_args_inputs_span(format_args),
"..",
&mut applicability,
);
@@ -104,8 +103,8 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
"try this",
format!("{prefix}{sugg_mac}!({inputs_snippet})"),
applicability,
- )
- }
+ );
+ });
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
index 20565e1d2..eeb4de8b5 100644
--- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
+++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
@@ -1,10 +1,10 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
use clippy_utils::trait_ref_of_method;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::MultiSpan;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor};
use rustc_hir::{
- BodyId, ExprKind, GenericBound, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind,
+ BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind,
PredicateOrigin, Ty, TyKind, WherePredicate,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -53,13 +53,19 @@ impl ExtraUnusedTypeParameters {
}
}
- /// Don't lint external macros or functions with empty bodies. Also, don't lint public items if
- /// the `avoid_breaking_exported_api` config option is set.
- fn check_false_positive(&self, cx: &LateContext<'_>, span: Span, def_id: LocalDefId, body_id: BodyId) -> bool {
+ /// Don't lint external macros or functions with empty bodies. Also, don't lint exported items
+ /// if the `avoid_breaking_exported_api` config option is set.
+ fn is_empty_exported_or_macro(
+ &self,
+ cx: &LateContext<'_>,
+ span: Span,
+ def_id: LocalDefId,
+ body_id: BodyId,
+ ) -> bool {
let body = cx.tcx.hir().body(body_id).value;
let fn_empty = matches!(&body.kind, ExprKind::Block(blk, None) if blk.stmts.is_empty() && blk.expr.is_none());
let is_exported = cx.effective_visibilities.is_exported(def_id);
- in_external_macro(cx.sess(), span) || (self.avoid_breaking_exported_api && is_exported) || fn_empty
+ in_external_macro(cx.sess(), span) || fn_empty || (is_exported && self.avoid_breaking_exported_api)
}
}
@@ -69,85 +75,129 @@ impl_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]);
/// trait bounds those parameters have.
struct TypeWalker<'cx, 'tcx> {
cx: &'cx LateContext<'tcx>,
- /// Collection of all the function's type parameters.
+ /// Collection of the function's type parameters. Once the function has been walked, this will
+ /// contain only unused type parameters.
ty_params: FxHashMap<DefId, Span>,
- /// Collection of any (inline) trait bounds corresponding to each type parameter.
- bounds: FxHashMap<DefId, Span>,
+ /// Collection of any inline trait bounds corresponding to each type parameter.
+ inline_bounds: FxHashMap<DefId, Span>,
+ /// Collection of any type parameters with trait bounds that appear in a where clause.
+ where_bounds: FxHashSet<DefId>,
/// The entire `Generics` object of the function, useful for querying purposes.
generics: &'tcx Generics<'tcx>,
- /// The value of this will remain `true` if *every* parameter:
- /// 1. Is a type parameter, and
- /// 2. Goes unused in the function.
- /// Otherwise, if any type parameters end up being used, or if any lifetime or const-generic
- /// parameters are present, this will be set to `false`.
- all_params_unused: bool,
}
impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
fn new(cx: &'cx LateContext<'tcx>, generics: &'tcx Generics<'tcx>) -> Self {
- let mut all_params_unused = true;
let ty_params = generics
.params
.iter()
- .filter_map(|param| {
- if let GenericParamKind::Type { synthetic, .. } = param.kind {
- (!synthetic).then_some((param.def_id.into(), param.span))
- } else {
- if !param.is_elided_lifetime() {
- all_params_unused = false;
- }
- None
- }
+ .filter_map(|param| match param.kind {
+ GenericParamKind::Type { synthetic, .. } if !synthetic => Some((param.def_id.into(), param.span)),
+ _ => None,
})
.collect();
Self {
cx,
ty_params,
- bounds: FxHashMap::default(),
+ inline_bounds: FxHashMap::default(),
+ where_bounds: FxHashSet::default(),
generics,
- all_params_unused,
}
}
- fn mark_param_used(&mut self, def_id: DefId) {
- if self.ty_params.remove(&def_id).is_some() {
- self.all_params_unused = false;
- }
+ fn get_bound_span(&self, param: &'tcx GenericParam<'tcx>) -> Span {
+ self.inline_bounds
+ .get(&param.def_id.to_def_id())
+ .map_or(param.span, |bound_span| param.span.with_hi(bound_span.hi()))
+ }
+
+ fn emit_help(&self, spans: Vec<Span>, msg: &str, help: &'static str) {
+ span_lint_and_help(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, spans, msg, None, help);
+ }
+
+ fn emit_sugg(&self, spans: Vec<Span>, msg: &str, help: &'static str) {
+ let suggestions: Vec<(Span, String)> = spans.iter().copied().zip(std::iter::repeat(String::new())).collect();
+ span_lint_and_then(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, spans, msg, |diag| {
+ diag.multipart_suggestion(help, suggestions, Applicability::MachineApplicable);
+ });
}
fn emit_lint(&self) {
- let (msg, help) = match self.ty_params.len() {
+ let explicit_params = self
+ .generics
+ .params
+ .iter()
+ .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait())
+ .collect::<Vec<_>>();
+
+ let extra_params = explicit_params
+ .iter()
+ .enumerate()
+ .filter(|(_, param)| self.ty_params.contains_key(&param.def_id.to_def_id()))
+ .collect::<Vec<_>>();
+
+ let (msg, help) = match extra_params.len() {
0 => return,
1 => (
- "type parameter goes unused in function definition",
+ format!(
+ "type parameter `{}` goes unused in function definition",
+ extra_params[0].1.name.ident()
+ ),
"consider removing the parameter",
),
_ => (
- "type parameters go unused in function definition",
+ format!(
+ "type parameters go unused in function definition: {}",
+ extra_params
+ .iter()
+ .map(|(_, param)| param.name.ident().to_string())
+ .collect::<Vec<_>>()
+ .join(", ")
+ ),
"consider removing the parameters",
),
};
- let source_map = self.cx.sess().source_map();
- let span = if self.all_params_unused {
- self.generics.span.into() // Remove the entire list of generics
+ // If any parameters are bounded in where clauses, don't try to form a suggestion.
+ // Otherwise, the leftover where bound would produce code that wouldn't compile.
+ if extra_params
+ .iter()
+ .any(|(_, param)| self.where_bounds.contains(&param.def_id.to_def_id()))
+ {
+ let spans = extra_params
+ .iter()
+ .map(|(_, param)| self.get_bound_span(param))
+ .collect::<Vec<_>>();
+ self.emit_help(spans, &msg, help);
} else {
- MultiSpan::from_spans(
- self.ty_params
+ let spans = if explicit_params.len() == extra_params.len() {
+ vec![self.generics.span] // Remove the entire list of generics
+ } else {
+ let mut end: Option<LocalDefId> = None;
+ extra_params
.iter()
- .map(|(def_id, &span)| {
- // Extend the span past any trait bounds, and include the comma at the end.
- let span_to_extend = self.bounds.get(def_id).copied().map_or(span, Span::shrink_to_hi);
- let comma_range = source_map.span_extend_to_next_char(span_to_extend, '>', false);
- let comma_span = source_map.span_through_char(comma_range, ',');
- span.with_hi(comma_span.hi())
+ .rev()
+ .map(|(idx, param)| {
+ if let Some(next) = explicit_params.get(idx + 1) && end != Some(next.def_id) {
+ // Extend the current span forward, up until the next param in the list.
+ param.span.until(next.span)
+ } else {
+ // Extend the current span back to include the comma following the previous
+ // param. If the span of the next param in the list has already been
+ // extended, we continue the chain. This is why we're iterating in reverse.
+ end = Some(param.def_id);
+
+ // idx will never be 0, else we'd be removing the entire list of generics
+ let prev = explicit_params[idx - 1];
+ let prev_span = self.get_bound_span(prev);
+ self.get_bound_span(param).with_lo(prev_span.hi())
+ }
})
- .collect(),
- )
+ .collect()
+ };
+ self.emit_sugg(spans, &msg, help);
};
-
- span_lint_and_help(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, span, msg, None, help);
}
}
@@ -162,7 +212,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) {
if let Some((def_id, _)) = t.peel_refs().as_generic_param() {
- self.mark_param_used(def_id);
+ self.ty_params.remove(&def_id);
} else if let TyKind::OpaqueDef(id, _, _) = t.kind {
// Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls
// `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're
@@ -176,9 +226,18 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) {
if let WherePredicate::BoundPredicate(predicate) = predicate {
- // Collect spans for any bounds on type parameters. We only keep bounds that appear in
- // the list of generics (not in a where-clause).
+ // Collect spans for any bounds on type parameters.
if let Some((def_id, _)) = predicate.bounded_ty.peel_refs().as_generic_param() {
+ match predicate.origin {
+ PredicateOrigin::GenericParam => {
+ self.inline_bounds.insert(def_id, predicate.span);
+ },
+ PredicateOrigin::WhereClause => {
+ self.where_bounds.insert(def_id);
+ },
+ PredicateOrigin::ImplTrait => (),
+ }
+
// If the bound contains non-public traits, err on the safe side and don't lint the
// corresponding parameter.
if !predicate
@@ -187,12 +246,10 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
.filter_map(bound_to_trait_def_id)
.all(|id| self.cx.effective_visibilities.is_exported(id))
{
- self.mark_param_used(def_id);
- } else if let PredicateOrigin::GenericParam = predicate.origin {
- self.bounds.insert(def_id, predicate.span);
+ self.ty_params.remove(&def_id);
}
}
- // Only walk the right-hand side of where-bounds
+ // Only walk the right-hand side of where bounds
for bound in predicate.bounds {
walk_param_bound(self, bound);
}
@@ -207,7 +264,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let ItemKind::Fn(_, generics, body_id) = item.kind
- && !self.check_false_positive(cx, item.span, item.owner_id.def_id, body_id)
+ && !self.is_empty_exported_or_macro(cx, item.span, item.owner_id.def_id, body_id)
{
let mut walker = TypeWalker::new(cx, generics);
walk_item(&mut walker, item);
@@ -219,7 +276,7 @@ impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters {
// Only lint on inherent methods, not trait methods.
if let ImplItemKind::Fn(.., body_id) = item.kind
&& trait_ref_of_method(cx, item.owner_id.def_id).is_none()
- && !self.check_false_positive(cx, item.span, item.owner_id.def_id, body_id)
+ && !self.is_empty_exported_or_macro(cx, item.span, item.owner_id.def_id, body_id)
{
let mut walker = TypeWalker::new(cx, item.generics);
walk_impl_item(&mut walker, item);
diff --git a/src/tools/clippy/clippy_lints/src/fn_null_check.rs b/src/tools/clippy/clippy_lints/src/fn_null_check.rs
index 91c8c340c..d8f4a5fe2 100644
--- a/src/tools/clippy/clippy_lints/src/fn_null_check.rs
+++ b/src/tools/clippy/clippy_lints/src/fn_null_check.rs
@@ -25,7 +25,7 @@ declare_clippy_lint! {
///
/// if fn_ptr.is_none() { ... }
/// ```
- #[clippy::version = "1.67.0"]
+ #[clippy::version = "1.68.0"]
pub FN_NULL_CHECK,
correctness,
"`fn()` type assumed to be nullable"
diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs
index d0fab6949..d34d6e927 100644
--- a/src/tools/clippy/clippy_lints/src/format.rs
+++ b/src/tools/clippy/clippy_lints/src/format.rs
@@ -1,14 +1,13 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::macros::{find_format_arg_expr, find_format_args, root_macro_call_first_node};
+use clippy_utils::source::{snippet_opt, snippet_with_context};
use clippy_utils::sugg::Sugg;
-use if_chain::if_chain;
+use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::kw;
use rustc_span::{sym, Span};
declare_clippy_lint! {
@@ -44,55 +43,53 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
impl<'tcx> LateLintPass<'tcx> for UselessFormat {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- let (format_args, call_site) = if_chain! {
- if let Some(macro_call) = root_macro_call_first_node(cx, expr);
- if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id);
- if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn);
- then {
- (format_args, macro_call.span)
- } else {
- return
- }
- };
+ let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+ if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) {
+ return;
+ }
+
+ find_format_args(cx, expr, macro_call.expn, |format_args| {
+ let mut applicability = Applicability::MachineApplicable;
+ let call_site = macro_call.span;
- let mut applicability = Applicability::MachineApplicable;
- if format_args.args.is_empty() {
- match *format_args.format_string.parts {
- [] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
- [_] => {
+ match (format_args.arguments.all_args(), &format_args.template[..]) {
+ ([], []) => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
+ ([], [_]) => {
// Simulate macro expansion, converting {{ and }} to { and }.
- let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}");
+ let Some(snippet) = snippet_opt(cx, format_args.span) else { return };
+ let s_expand = snippet.replace("{{", "{").replace("}}", "}");
let sugg = format!("{s_expand}.to_string()");
span_useless_format(cx, call_site, sugg, applicability);
},
- [..] => {},
- }
- } else if let [arg] = &*format_args.args {
- let value = arg.param.value;
- if_chain! {
- if format_args.format_string.parts == [kw::Empty];
- if arg.format.is_default();
- if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
- ty::Adt(adt, _) => Some(adt.did()) == cx.tcx.lang_items().string(),
- ty::Str => true,
- _ => false,
- };
- then {
- let is_new_string = match value.kind {
- ExprKind::Binary(..) => true,
- ExprKind::MethodCall(path, ..) => path.ident.name == sym::to_string,
- _ => false,
- };
- let sugg = if is_new_string {
- snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned()
- } else {
- let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability);
- format!("{}.to_string()", sugg.maybe_par())
- };
- span_useless_format(cx, call_site, sugg, applicability);
- }
+ ([arg], [piece]) => {
+ if let Ok(value) = find_format_arg_expr(expr, arg)
+ && let FormatArgsPiece::Placeholder(placeholder) = piece
+ && placeholder.format_trait == FormatTrait::Display
+ && placeholder.format_options == FormatOptions::default()
+ && match cx.typeck_results().expr_ty(value).peel_refs().kind() {
+ ty::Adt(adt, _) => Some(adt.did()) == cx.tcx.lang_items().string(),
+ ty::Str => true,
+ _ => false,
+ }
+ {
+ let is_new_string = match value.kind {
+ ExprKind::Binary(..) => true,
+ ExprKind::MethodCall(path, ..) => path.ident.name == sym::to_string,
+ _ => false,
+ };
+ let sugg = if is_new_string {
+ snippet_with_context(cx, value.span, call_site.ctxt(), "..", &mut applicability).0.into_owned()
+ } else {
+ let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "<arg>", &mut applicability);
+ format!("{}.to_string()", sugg.maybe_par())
+ };
+ span_useless_format(cx, call_site, sugg, applicability);
+
+ }
+ },
+ _ => {},
}
- };
+ });
}
}
diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs
index c511d85e9..08e45ed7d 100644
--- a/src/tools/clippy/clippy_lints/src/format_args.rs
+++ b/src/tools/clippy/clippy_lints/src/format_args.rs
@@ -1,27 +1,31 @@
+use arrayvec::ArrayVec;
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::is_diag_trait_item;
-use clippy_utils::macros::FormatParamKind::{Implicit, Named, NamedInline, Numbered, Starred};
use clippy_utils::macros::{
- is_assert_macro, is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam,
- FormatParamUsage,
+ find_format_arg_expr, find_format_args, format_arg_removal_span, format_placeholder_format_span, is_assert_macro,
+ is_format_macro, is_panic, root_macro_call, root_macro_call_first_node, FormatParamUsage,
};
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{implements_trait, is_type_lang_item};
use if_chain::if_chain;
use itertools::Itertools;
+use rustc_ast::{
+ FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions,
+ FormatPlaceholder, FormatTrait,
+};
use rustc_errors::{
Applicability,
SuggestionStyle::{CompletelyHidden, ShowCode},
};
-use rustc_hir::{Expr, ExprKind, HirId, LangItem, QPath};
+use rustc_hir::{Expr, ExprKind, LangItem};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_middle::ty::Ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::def_id::DefId;
use rustc_span::edition::Edition::Edition2021;
-use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol};
+use rustc_span::{sym, Span, Symbol};
declare_clippy_lint! {
/// ### What it does
@@ -184,72 +188,79 @@ impl FormatArgs {
impl<'tcx> LateLintPass<'tcx> for FormatArgs {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- if let Some(format_args) = FormatArgsExpn::parse(cx, expr)
- && let expr_expn_data = expr.span.ctxt().outer_expn_data()
- && let outermost_expn_data = outermost_expn_data(expr_expn_data)
- && let Some(macro_def_id) = outermost_expn_data.macro_def_id
- && is_format_macro(cx, macro_def_id)
- && let ExpnKind::Macro(_, name) = outermost_expn_data.kind
- {
- for arg in &format_args.args {
- check_unused_format_specifier(cx, arg);
- if !arg.format.is_default() {
- continue;
- }
- if is_aliased(&format_args, arg.param.value.hir_id) {
- continue;
+ let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+ if !is_format_macro(cx, macro_call.def_id) {
+ return;
+ }
+ let name = cx.tcx.item_name(macro_call.def_id);
+
+ find_format_args(cx, expr, macro_call.expn, |format_args| {
+ for piece in &format_args.template {
+ if let FormatArgsPiece::Placeholder(placeholder) = piece
+ && let Ok(index) = placeholder.argument.index
+ && let Some(arg) = format_args.arguments.all_args().get(index)
+ {
+ let arg_expr = find_format_arg_expr(expr, arg);
+
+ check_unused_format_specifier(cx, placeholder, arg_expr);
+
+ if placeholder.format_trait != FormatTrait::Display
+ || placeholder.format_options != FormatOptions::default()
+ || is_aliased(format_args, index)
+ {
+ continue;
+ }
+
+ if let Ok(arg_hir_expr) = arg_expr {
+ check_format_in_format_args(cx, macro_call.span, name, arg_hir_expr);
+ check_to_string_in_format_args(cx, name, arg_hir_expr);
+ }
}
- check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
- check_to_string_in_format_args(cx, name, arg.param.value);
}
+
if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) {
- check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id, self.ignore_mixed);
+ check_uninlined_args(cx, format_args, macro_call.span, macro_call.def_id, self.ignore_mixed);
}
- }
+ });
}
extract_msrv_attr!(LateContext);
}
-fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) {
- let param_ty = cx.typeck_results().expr_ty(arg.param.value).peel_refs();
+fn check_unused_format_specifier(
+ cx: &LateContext<'_>,
+ placeholder: &FormatPlaceholder,
+ arg_expr: Result<&Expr<'_>, &rustc_ast::Expr>,
+) {
+ let ty_or_ast_expr = arg_expr.map(|expr| cx.typeck_results().expr_ty(expr).peel_refs());
- if let Count::Implied(Some(mut span)) = arg.format.precision
- && !span.is_empty()
- {
- span_lint_and_then(
- cx,
- UNUSED_FORMAT_SPECS,
- span,
- "empty precision specifier has no effect",
- |diag| {
- if param_ty.is_floating_point() {
- diag.note("a precision specifier is not required to format floats");
- }
+ let is_format_args = match ty_or_ast_expr {
+ Ok(ty) => is_type_lang_item(cx, ty, LangItem::FormatArguments),
+ Err(expr) => matches!(expr.peel_parens_and_refs().kind, rustc_ast::ExprKind::FormatArgs(_)),
+ };
- if arg.format.is_default() {
- // If there's no other specifiers remove the `:` too
- span = arg.format_span();
- }
+ let options = &placeholder.format_options;
- diag.span_suggestion_verbose(span, "remove the `.`", "", Applicability::MachineApplicable);
- },
- );
- }
+ let arg_span = match arg_expr {
+ Ok(expr) => expr.span,
+ Err(expr) => expr.span,
+ };
- if is_type_lang_item(cx, param_ty, LangItem::FormatArguments) && !arg.format.is_default_for_trait() {
+ if let Some(placeholder_span) = placeholder.span
+ && is_format_args
+ && *options != FormatOptions::default()
+ {
span_lint_and_then(
cx,
UNUSED_FORMAT_SPECS,
- arg.span,
+ placeholder_span,
"format specifiers have no effect on `format_args!()`",
|diag| {
- let mut suggest_format = |spec, span| {
+ let mut suggest_format = |spec| {
let message = format!("for the {spec} to apply consider using `format!()`");
- if let Some(mac_call) = root_macro_call(arg.param.value.span)
+ if let Some(mac_call) = root_macro_call(arg_span)
&& cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id)
- && arg.span.eq_ctxt(mac_call.span)
{
diag.span_suggestion(
cx.sess().source_map().span_until_char(mac_call.span, '!'),
@@ -257,25 +268,27 @@ fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) {
"format",
Applicability::MaybeIncorrect,
);
- } else if let Some(span) = span {
- diag.span_help(span, message);
+ } else {
+ diag.help(message);
}
};
- if !arg.format.width.is_implied() {
- suggest_format("width", arg.format.width.span());
+ if options.width.is_some() {
+ suggest_format("width");
}
- if !arg.format.precision.is_implied() {
- suggest_format("precision", arg.format.precision.span());
+ if options.precision.is_some() {
+ suggest_format("precision");
}
- diag.span_suggestion_verbose(
- arg.format_span(),
- "if the current behavior is intentional, remove the format specifiers",
- "",
- Applicability::MaybeIncorrect,
- );
+ if let Some(format_span) = format_placeholder_format_span(placeholder) {
+ diag.span_suggestion_verbose(
+ format_span,
+ "if the current behavior is intentional, remove the format specifiers",
+ "",
+ Applicability::MaybeIncorrect,
+ );
+ }
},
);
}
@@ -283,12 +296,12 @@ fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) {
fn check_uninlined_args(
cx: &LateContext<'_>,
- args: &FormatArgsExpn<'_>,
+ args: &rustc_ast::FormatArgs,
call_site: Span,
def_id: DefId,
ignore_mixed: bool,
) {
- if args.format_string.span.from_expansion() {
+ if args.span.from_expansion() {
return;
}
if call_site.edition() < Edition2021 && (is_panic(cx, def_id) || is_assert_macro(cx, def_id)) {
@@ -303,7 +316,13 @@ fn check_uninlined_args(
// we cannot remove any other arguments in the format string,
// because the index numbers might be wrong after inlining.
// Example of an un-inlinable format: print!("{}{1}", foo, 2)
- if !args.params().all(|p| check_one_arg(args, &p, &mut fixes, ignore_mixed)) || fixes.is_empty() {
+ for (pos, usage) in format_arg_positions(args) {
+ if !check_one_arg(args, pos, usage, &mut fixes, ignore_mixed) {
+ return;
+ }
+ }
+
+ if fixes.is_empty() {
return;
}
@@ -332,47 +351,40 @@ fn check_uninlined_args(
}
fn check_one_arg(
- args: &FormatArgsExpn<'_>,
- param: &FormatParam<'_>,
+ args: &rustc_ast::FormatArgs,
+ pos: &FormatArgPosition,
+ usage: FormatParamUsage,
fixes: &mut Vec<(Span, String)>,
ignore_mixed: bool,
) -> bool {
- if matches!(param.kind, Implicit | Starred | Named(_) | Numbered)
- && let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind
- && let [segment] = path.segments
+ let index = pos.index.unwrap();
+ let arg = &args.arguments.all_args()[index];
+
+ if !matches!(arg.kind, FormatArgumentKind::Captured(_))
+ && let rustc_ast::ExprKind::Path(None, path) = &arg.expr.kind
+ && let [segment] = path.segments.as_slice()
&& segment.args.is_none()
- && let Some(arg_span) = args.value_with_prev_comma_span(param.value.hir_id)
+ && let Some(arg_span) = format_arg_removal_span(args, index)
+ && let Some(pos_span) = pos.span
{
- let replacement = match param.usage {
+ let replacement = match usage {
FormatParamUsage::Argument => segment.ident.name.to_string(),
FormatParamUsage::Width => format!("{}$", segment.ident.name),
FormatParamUsage::Precision => format!(".{}$", segment.ident.name),
};
- fixes.push((param.span, replacement));
+ fixes.push((pos_span, replacement));
fixes.push((arg_span, String::new()));
true // successful inlining, continue checking
} else {
// Do not continue inlining (return false) in case
// * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)`
// * if allow_mixed_uninlined_format_args is false and this arg hasn't been inlined already
- param.kind != Numbered && (!ignore_mixed || matches!(param.kind, NamedInline(_)))
- }
-}
-
-fn outermost_expn_data(expn_data: ExpnData) -> ExpnData {
- if expn_data.call_site.from_expansion() {
- outermost_expn_data(expn_data.call_site.ctxt().outer_expn_data())
- } else {
- expn_data
+ pos.kind != FormatArgPositionKind::Number
+ && (!ignore_mixed || matches!(arg.kind, FormatArgumentKind::Captured(_)))
}
}
-fn check_format_in_format_args(
- cx: &LateContext<'_>,
- call_site: Span,
- name: Symbol,
- arg: &Expr<'_>,
-) {
+fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) {
let expn_data = arg.span.ctxt().outer_expn_data();
if expn_data.call_site.from_expansion() {
return;
@@ -443,9 +455,33 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
}
}
-/// Returns true if `hir_id` is referred to by multiple format params
-fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
- args.params().filter(|param| param.value.hir_id == hir_id).at_most_one().is_err()
+fn format_arg_positions(
+ format_args: &rustc_ast::FormatArgs,
+) -> impl Iterator<Item = (&FormatArgPosition, FormatParamUsage)> {
+ format_args.template.iter().flat_map(|piece| match piece {
+ FormatArgsPiece::Placeholder(placeholder) => {
+ let mut positions = ArrayVec::<_, 3>::new();
+
+ positions.push((&placeholder.argument, FormatParamUsage::Argument));
+ if let Some(FormatCount::Argument(position)) = &placeholder.format_options.width {
+ positions.push((position, FormatParamUsage::Width));
+ }
+ if let Some(FormatCount::Argument(position)) = &placeholder.format_options.precision {
+ positions.push((position, FormatParamUsage::Precision));
+ }
+
+ positions
+ },
+ FormatArgsPiece::Literal(_) => ArrayVec::new(),
+ })
+}
+
+/// Returns true if the format argument at `index` is referred to by multiple format params
+fn is_aliased(format_args: &rustc_ast::FormatArgs, index: usize) -> bool {
+ format_arg_positions(format_args)
+ .filter(|(position, _)| position.index == Ok(index))
+ .at_most_one()
+ .is_err()
}
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
@@ -455,7 +491,11 @@ where
let mut n_total = 0;
let mut n_needed = 0;
loop {
- if let Some(Adjustment { kind: Adjust::Deref(overloaded_deref), target }) = iter.next() {
+ if let Some(Adjustment {
+ kind: Adjust::Deref(overloaded_deref),
+ target,
+ }) = iter.next()
+ {
n_total += 1;
if overloaded_deref.is_some() {
n_needed = n_total;
diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs
index ed1342a54..e3ddbfb59 100644
--- a/src/tools/clippy/clippy_lints/src/format_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/format_impl.rs
@@ -1,11 +1,13 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArg, FormatArgsExpn};
+use clippy_utils::macros::{find_format_arg_expr, find_format_args, is_format_macro, root_macro_call_first_node};
use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
use if_chain::if_chain;
+use rustc_ast::{FormatArgsPiece, FormatTrait};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::Span;
use rustc_span::{sym, symbol::kw, Symbol};
declare_clippy_lint! {
@@ -89,7 +91,7 @@ declare_clippy_lint! {
}
#[derive(Clone, Copy)]
-struct FormatTrait {
+struct FormatTraitNames {
/// e.g. `sym::Display`
name: Symbol,
/// `f` in `fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {}`
@@ -99,7 +101,7 @@ struct FormatTrait {
#[derive(Default)]
pub struct FormatImpl {
// Whether we are inside Display or Debug trait impl - None for neither
- format_trait_impl: Option<FormatTrait>,
+ format_trait_impl: Option<FormatTraitNames>,
}
impl FormatImpl {
@@ -161,43 +163,57 @@ fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) {
}
}
-fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, impl_trait: FormatTrait) {
+fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, impl_trait: FormatTraitNames) {
// Check each arg in format calls - do we ever use Display on self (directly or via deref)?
- if_chain! {
- if let Some(outer_macro) = root_macro_call_first_node(cx, expr);
- if let macro_def_id = outer_macro.def_id;
- if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, outer_macro.expn);
- if is_format_macro(cx, macro_def_id);
- then {
- for arg in format_args.args {
- if arg.format.r#trait != impl_trait.name {
- continue;
+ if let Some(outer_macro) = root_macro_call_first_node(cx, expr)
+ && let macro_def_id = outer_macro.def_id
+ && is_format_macro(cx, macro_def_id)
+ {
+ find_format_args(cx, expr, outer_macro.expn, |format_args| {
+ for piece in &format_args.template {
+ if let FormatArgsPiece::Placeholder(placeholder) = piece
+ && let trait_name = match placeholder.format_trait {
+ FormatTrait::Display => sym::Display,
+ FormatTrait::Debug => sym::Debug,
+ FormatTrait::LowerExp => sym!(LowerExp),
+ FormatTrait::UpperExp => sym!(UpperExp),
+ FormatTrait::Octal => sym!(Octal),
+ FormatTrait::Pointer => sym::Pointer,
+ FormatTrait::Binary => sym!(Binary),
+ FormatTrait::LowerHex => sym!(LowerHex),
+ FormatTrait::UpperHex => sym!(UpperHex),
+ }
+ && trait_name == impl_trait.name
+ && let Ok(index) = placeholder.argument.index
+ && let Some(arg) = format_args.arguments.all_args().get(index)
+ && let Ok(arg_expr) = find_format_arg_expr(expr, arg)
+ {
+ check_format_arg_self(cx, expr.span, arg_expr, impl_trait);
}
- check_format_arg_self(cx, expr, &arg, impl_trait);
}
- }
+ });
}
}
-fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArg<'_>, impl_trait: FormatTrait) {
+fn check_format_arg_self(cx: &LateContext<'_>, span: Span, arg: &Expr<'_>, impl_trait: FormatTraitNames) {
// Handle multiple dereferencing of references e.g. &&self
// Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
// Since the argument to fmt is itself a reference: &self
- let reference = peel_ref_operators(cx, arg.param.value);
+ let reference = peel_ref_operators(cx, arg);
let map = cx.tcx.hir();
// Is the reference self?
if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {
- let FormatTrait { name, .. } = impl_trait;
+ let FormatTraitNames { name, .. } = impl_trait;
span_lint(
cx,
RECURSIVE_FORMAT_IMPL,
- expr.span,
+ span,
&format!("using `self` as `{name}` in `impl {name}` will cause infinite recursion"),
);
}
}
-fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: FormatTrait) {
+fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: FormatTraitNames) {
if_chain! {
if let Some(macro_call) = root_macro_call_first_node(cx, expr);
if let Some(name) = cx.tcx.get_diagnostic_name(macro_call.def_id);
@@ -227,7 +243,7 @@ fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait:
}
}
-fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Option<FormatTrait> {
+fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Option<FormatTraitNames> {
if_chain! {
if impl_item.ident.name == sym::fmt;
if let ImplItemKind::Fn(_, body_id) = impl_item.kind;
@@ -241,7 +257,7 @@ fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Optio
.and_then(|param| param.pat.simple_ident())
.map(|ident| ident.name);
- Some(FormatTrait {
+ Some(FormatTraitNames {
name,
formatter_name,
})
diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
index 2811a73f6..d3d0d91c1 100644
--- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
@@ -22,7 +22,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body:
if let Some(gen_span) = generics.span_for_param_suggestion() {
diag.span_suggestion_with_style(
gen_span,
- "add a type paremeter",
+ "add a type parameter",
format!(", {{ /* Generic name */ }}: {}", &param.name.ident().as_str()[5..]),
rustc_errors::Applicability::HasPlaceholders,
rustc_errors::SuggestionStyle::ShowAlways,
@@ -35,7 +35,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body:
ident.span.ctxt(),
ident.span.parent(),
),
- "add a type paremeter",
+ "add a type parameter",
format!("<{{ /* Generic name */ }}: {}>", &param.name.ident().as_str()[5..]),
rustc_errors::Applicability::HasPlaceholders,
rustc_errors::SuggestionStyle::ShowAlways,
diff --git a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
index 8b53ee68e..e5945939e 100644
--- a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
@@ -97,7 +97,7 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
let Some(correct_field) = correct_field else {
// There is no field corresponding to the getter name.
- // FIXME: This can be a false positive if the correct field is reachable trought deeper autodereferences than used_field is
+ // FIXME: This can be a false positive if the correct field is reachable through deeper autodereferences than used_field is
return;
};
diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs
index d2852b4ac..7c5e44bb7 100644
--- a/src/tools/clippy/clippy_lints/src/functions/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs
@@ -185,7 +185,7 @@ declare_clippy_lint! {
/// ### Examples
/// ```rust
/// // this could be annotated with `#[must_use]`.
- /// fn id<T>(t: T) -> T { t }
+ /// pub fn id<T>(t: T) -> T { t }
/// ```
#[clippy::version = "1.40.0"]
pub MUST_USE_CANDIDATE,
diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
index 29bdc46b6..d0ad26282 100644
--- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
@@ -1,7 +1,9 @@
+use hir::FnSig;
use rustc_ast::ast::Attribute;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefIdSet;
use rustc_hir::{self as hir, def::Res, QPath};
+use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LintContext};
use rustc_middle::{
lint::in_external_macro,
@@ -22,13 +24,13 @@ use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT};
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id());
- let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use);
+ let attr = cx.tcx.get_attr(item.owner_id, sym::must_use);
if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind {
let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
if let Some(attr) = attr {
- check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr);
- } else if is_public && !is_proc_macro(cx.sess(), attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) {
+ check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, sig);
+ } else if is_public && !is_proc_macro(attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) {
check_must_use_candidate(
cx,
sig.decl,
@@ -47,13 +49,10 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp
let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
let attrs = cx.tcx.hir().attrs(item.hir_id());
- let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use);
+ let attr = cx.tcx.get_attr(item.owner_id, sym::must_use);
if let Some(attr) = attr {
- check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr);
- } else if is_public
- && !is_proc_macro(cx.sess(), attrs)
- && trait_ref_of_method(cx, item.owner_id.def_id).is_none()
- {
+ check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, sig);
+ } else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id.def_id).is_none() {
check_must_use_candidate(
cx,
sig.decl,
@@ -73,12 +72,12 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
let attrs = cx.tcx.hir().attrs(item.hir_id());
- let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use);
+ let attr = cx.tcx.get_attr(item.owner_id, sym::must_use);
if let Some(attr) = attr {
- check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr);
+ check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, sig);
} else if let hir::TraitFn::Provided(eid) = *eid {
let body = cx.tcx.hir().body(eid);
- if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) {
+ if attr.is_none() && is_public && !is_proc_macro(attrs) {
check_must_use_candidate(
cx,
sig.decl,
@@ -100,6 +99,7 @@ fn check_needless_must_use(
item_span: Span,
fn_header_span: Span,
attr: &Attribute,
+ sig: &FnSig<'_>,
) {
if in_external_macro(cx.sess(), item_span) {
return;
@@ -115,6 +115,15 @@ fn check_needless_must_use(
},
);
} else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
+ // Ignore async functions unless Future::Output type is a must_use type
+ if sig.header.is_async() {
+ let infcx = cx.tcx.infer_ctxt().build();
+ if let Some(future_ty) = infcx.get_impl_future_output_ty(return_ty(cx, item_id))
+ && !is_must_use_ty(cx, future_ty) {
+ return;
+ }
+ }
+
span_lint_and_help(
cx,
DOUBLE_MUST_USE,
diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs
index 9fb73a371..ed0bd58c7 100644
--- a/src/tools/clippy/clippy_lints/src/future_not_send.rs
+++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs
@@ -9,7 +9,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
use rustc_span::{sym, Span};
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
-use rustc_trait_selection::traits::{self, FulfillmentError};
+use rustc_trait_selection::traits::{self, FulfillmentError, ObligationCtxt};
declare_clippy_lint! {
/// ### What it does
@@ -79,8 +79,10 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap();
let span = decl.output.span();
let infcx = cx.tcx.infer_ctxt().build();
+ let ocx = ObligationCtxt::new(&infcx);
let cause = traits::ObligationCause::misc(span, fn_def_id);
- let send_errors = traits::fully_solve_bound(&infcx, cause, cx.param_env, ret_ty, send_trait);
+ ocx.register_bound(cause, cx.param_env, ret_ty, send_trait);
+ let send_errors = ocx.select_all_or_error();
if !send_errors.is_empty() {
span_lint_and_then(
cx,
diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
index 9cadaaa49..725bd3d54 100644
--- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
+++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
@@ -1,8 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::sugg::Sugg;
use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks};
+use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -72,21 +74,20 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
return;
}
+ let ctxt = expr.span.ctxt();
+
if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr)
&& let ExprKind::Block(then_block, _) = then.kind
&& let Some(then_expr) = then_block.expr
&& let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
+ && then_expr.span.ctxt() == ctxt
&& is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome)
&& is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone)
&& !stmts_contains_early_return(then_block.stmts)
{
- let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
- let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
- format!("({cond_snip})")
- } else {
- cond_snip.into_owned()
- };
- let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
+ let mut app = Applicability::Unspecified;
+ let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app).maybe_par().to_string();
+ let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0;
let mut method_body = if then_block.stmts.is_empty() {
arg_snip.into_owned()
} else {
diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
index 6e1934393..57e6caa87 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
@@ -1,7 +1,7 @@
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::get_parent_expr;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
use if_chain::if_chain;
use rustc_ast::ast::{LitIntType, LitKind};
use rustc_errors::Applicability;
@@ -55,6 +55,9 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
if let ExprKind::AssignOp(op1, target, value) = ex.kind;
let ty = cx.typeck_results().expr_ty(target);
if Some(c) == get_int_max(ty);
+ let ctxt = expr.span.ctxt();
+ if ex.span.ctxt() == ctxt;
+ if expr1.span.ctxt() == ctxt;
if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target);
if BinOpKind::Add == op1.node;
if let ExprKind::Lit(ref lit) = value.kind;
@@ -62,8 +65,15 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
if block.expr.is_none();
then {
let mut app = Applicability::MachineApplicable;
- let code = snippet_with_applicability(cx, target.span, "_", &mut app);
- let sugg = if let Some(parent) = get_parent_expr(cx, expr) && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind && else_.hir_id == expr.hir_id {format!("{{{code} = {code}.saturating_add(1); }}")} else {format!("{code} = {code}.saturating_add(1);")};
+ let code = snippet_with_context(cx, target.span, ctxt, "_", &mut app).0;
+ let sugg = if let Some(parent) = get_parent_expr(cx, expr)
+ && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind
+ && else_.hir_id == expr.hir_id
+ {
+ format!("{{{code} = {code}.saturating_add(1); }}")
+ } else {
+ format!("{code} = {code}.saturating_add(1);")
+ };
span_lint_and_sugg(cx, IMPLICIT_SATURATING_ADD, expr.span, "manual saturating add detected", "use instead", sugg, app);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs
index d1d2db27c..fe28c526b 100644
--- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs
+++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs
@@ -167,7 +167,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
Finite
},
ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
- ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e),
+ ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e),
ExprKind::Call(path, _) => {
if let ExprKind::Path(ref qpath) = path.kind {
cx.qpath_res(qpath, path.hir_id)
diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
index 668110c7c..34e999158 100644
--- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
+++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
@@ -1,6 +1,6 @@
use clippy_utils::diagnostics::{self, span_lint_and_sugg};
use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source;
+use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty;
use rustc_errors::Applicability;
@@ -161,14 +161,9 @@ fn print_unchecked_duration_subtraction_sugg(
) {
let mut applicability = Applicability::MachineApplicable;
- let left_expr =
- source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability);
- let right_expr = source::snippet_with_applicability(
- cx,
- right_expr.span,
- "std::time::Duration::from_secs(1)",
- &mut applicability,
- );
+ let ctxt = expr.span.ctxt();
+ let left_expr = snippet_with_context(cx, left_expr.span, ctxt, "<instant>", &mut applicability).0;
+ let right_expr = snippet_with_context(cx, right_expr.span, ctxt, "<duration>", &mut applicability).0;
diagnostics::span_lint_and_sugg(
cx,
diff --git a/src/tools/clippy/clippy_lints/src/items_after_statements.rs b/src/tools/clippy/clippy_lints/src/items_after_statements.rs
index 46d439b44..a7ec57e28 100644
--- a/src/tools/clippy/clippy_lints/src/items_after_statements.rs
+++ b/src/tools/clippy/clippy_lints/src/items_after_statements.rs
@@ -1,8 +1,8 @@
//! lint when items are used after statements
-use clippy_utils::diagnostics::span_lint;
-use rustc_ast::ast::{Block, ItemKind, StmtKind};
-use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use clippy_utils::diagnostics::span_lint_hir;
+use rustc_hir::{Block, ItemKind, StmtKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -52,33 +52,34 @@ declare_clippy_lint! {
declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]);
-impl EarlyLintPass for ItemsAfterStatements {
- fn check_block(&mut self, cx: &EarlyContext<'_>, item: &Block) {
- if in_external_macro(cx.sess(), item.span) {
+impl LateLintPass<'_> for ItemsAfterStatements {
+ fn check_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>) {
+ if in_external_macro(cx.sess(), block.span) {
return;
}
- // skip initial items and trailing semicolons
- let stmts = item
+ // skip initial items
+ let stmts = block
.stmts
.iter()
- .map(|stmt| &stmt.kind)
- .skip_while(|s| matches!(**s, StmtKind::Item(..) | StmtKind::Empty));
+ .skip_while(|stmt| matches!(stmt.kind, StmtKind::Item(..)));
// lint on all further items
for stmt in stmts {
- if let StmtKind::Item(ref it) = *stmt {
- if in_external_macro(cx.sess(), it.span) {
+ if let StmtKind::Item(item_id) = stmt.kind {
+ let item = cx.tcx.hir().item(item_id);
+ if in_external_macro(cx.sess(), item.span) || !item.span.eq_ctxt(block.span) {
return;
}
- if let ItemKind::MacroDef(..) = it.kind {
+ if let ItemKind::Macro(..) = item.kind {
// do not lint `macro_rules`, but continue processing further statements
continue;
}
- span_lint(
+ span_lint_hir(
cx,
ITEMS_AFTER_STATEMENTS,
- it.span,
+ item.hir_id(),
+ item.span,
"adding items after statements is confusing, since items exist from the \
start of the scope",
);
diff --git a/src/tools/clippy/clippy_lints/src/large_futures.rs b/src/tools/clippy/clippy_lints/src/large_futures.rs
new file mode 100644
index 000000000..1b0544813
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/large_futures.rs
@@ -0,0 +1,87 @@
+use clippy_utils::source::snippet;
+use clippy_utils::{diagnostics::span_lint_and_sugg, ty::implements_trait};
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_target::abi::Size;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// It checks for the size of a `Future` created by `async fn` or `async {}`.
+ ///
+ /// ### Why is this bad?
+ /// Due to the current [unideal implemention](https://github.com/rust-lang/rust/issues/69826) of `Generator`,
+ /// large size of a `Future` may cause stack overflows.
+ ///
+ /// ### Example
+ /// ```rust
+ /// async fn wait(f: impl std::future::Future<Output = ()>) {}
+ ///
+ /// async fn big_fut(arg: [u8; 1024]) {}
+ ///
+ /// pub async fn test() {
+ /// let fut = big_fut([0u8; 1024]);
+ /// wait(fut).await;
+ /// }
+ /// ```
+ ///
+ /// `Box::pin` the big future instead.
+ ///
+ /// ```rust
+ /// async fn wait(f: impl std::future::Future<Output = ()>) {}
+ ///
+ /// async fn big_fut(arg: [u8; 1024]) {}
+ ///
+ /// pub async fn test() {
+ /// let fut = Box::pin(big_fut([0u8; 1024]));
+ /// wait(fut).await;
+ /// }
+ /// ```
+ #[clippy::version = "1.68.0"]
+ pub LARGE_FUTURES,
+ pedantic,
+ "large future may lead to unexpected stack overflows"
+}
+
+#[derive(Copy, Clone)]
+pub struct LargeFuture {
+ future_size_threshold: u64,
+}
+
+impl LargeFuture {
+ pub fn new(future_size_threshold: u64) -> Self {
+ Self { future_size_threshold }
+ }
+}
+
+impl_lint_pass!(LargeFuture => [LARGE_FUTURES]);
+
+impl<'tcx> LateLintPass<'tcx> for LargeFuture {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+ if matches!(expr.span.ctxt().outer_expn_data().kind, rustc_span::ExpnKind::Macro(..)) {
+ return;
+ }
+ if let ExprKind::Match(expr, _, MatchSource::AwaitDesugar) = expr.kind {
+ if let ExprKind::Call(func, [expr, ..]) = expr.kind
+ && let ExprKind::Path(QPath::LangItem(LangItem::IntoFutureIntoFuture, ..)) = func.kind
+ && let ty = cx.typeck_results().expr_ty(expr)
+ && let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait()
+ && implements_trait(cx, ty, future_trait_def_id, &[])
+ && let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty))
+ && let size = layout.layout.size()
+ && size >= Size::from_bytes(self.future_size_threshold)
+ {
+ span_lint_and_sugg(
+ cx,
+ LARGE_FUTURES,
+ expr.span,
+ &format!("large future with a size of {} bytes", size.bytes()),
+ "consider `Box::pin` on it",
+ format!("Box::pin({})", snippet(cx, expr.span, "..")),
+ Applicability::Unspecified,
+ );
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index e13bc4797..0805b4b19 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -1,13 +1,14 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators};
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators, sugg::Sugg};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefIdSet;
use rustc_hir::{
- def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item,
- ItemKind, Mutability, Node, TraitItemRef, TyKind, UnOp,
+ def::Res, def_id::DefId, lang_items::LangItem, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg,
+ GenericBound, ImplItem, ImplItemKind, ImplicitSelfKind, Item, ItemKind, Mutability, Node, PathSegment, PrimTy,
+ QPath, TraitItemRef, TyKind, TypeBindingKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, AssocKind, FnSig, Ty};
@@ -16,7 +17,6 @@ use rustc_span::{
source_map::{Span, Spanned, Symbol},
symbol::sym,
};
-use std::borrow::Cow;
declare_clippy_lint! {
/// ### What it does
@@ -251,33 +251,98 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items
}
#[derive(Debug, Clone, Copy)]
-enum LenOutput<'tcx> {
+enum LenOutput {
Integral,
Option(DefId),
- Result(DefId, Ty<'tcx>),
+ Result(DefId),
}
-fn parse_len_output<'tcx>(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> {
+
+fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
+ if let ty::Alias(_, alias_ty) = ty.kind() &&
+ let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(alias_ty.def_id) &&
+ let Item { kind: ItemKind::OpaqueTy(opaque), .. } = item &&
+ opaque.bounds.len() == 1 &&
+ let GenericBound::LangItemTrait(LangItem::Future, _, _, generic_args) = &opaque.bounds[0] &&
+ generic_args.bindings.len() == 1 &&
+ let TypeBindingKind::Equality {
+ term: rustc_hir::Term::Ty(rustc_hir::Ty {kind: TyKind::Path(QPath::Resolved(_, path)), .. }),
+ } = &generic_args.bindings[0].kind &&
+ path.segments.len() == 1 {
+ return Some(&path.segments[0]);
+ }
+
+ None
+}
+
+fn is_first_generic_integral<'tcx>(segment: &'tcx PathSegment<'tcx>) -> bool {
+ if let Some(generic_args) = segment.args {
+ if generic_args.args.is_empty() {
+ return false;
+ }
+ let arg = &generic_args.args[0];
+ if let GenericArg::Type(rustc_hir::Ty {
+ kind: TyKind::Path(QPath::Resolved(_, path)),
+ ..
+ }) = arg
+ {
+ let segments = &path.segments;
+ let segment = &segments[0];
+ let res = &segment.res;
+ if matches!(res, Res::PrimTy(PrimTy::Uint(_))) || matches!(res, Res::PrimTy(PrimTy::Int(_))) {
+ return true;
+ }
+ }
+ }
+
+ false
+}
+
+fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option<LenOutput> {
+ if let Some(segment) = extract_future_output(cx, sig.output()) {
+ let res = segment.res;
+
+ if matches!(res, Res::PrimTy(PrimTy::Uint(_))) || matches!(res, Res::PrimTy(PrimTy::Int(_))) {
+ return Some(LenOutput::Integral);
+ }
+
+ if let Res::Def(_, def_id) = res {
+ if cx.tcx.is_diagnostic_item(sym::Option, def_id) && is_first_generic_integral(segment) {
+ return Some(LenOutput::Option(def_id));
+ } else if cx.tcx.is_diagnostic_item(sym::Result, def_id) && is_first_generic_integral(segment) {
+ return Some(LenOutput::Result(def_id));
+ }
+ }
+
+ return None;
+ }
+
match *sig.output().kind() {
ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral),
ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) => {
subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did()))
},
- ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => subs
- .type_at(0)
- .is_integral()
- .then(|| LenOutput::Result(adt.did(), subs.type_at(1))),
+ ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => {
+ subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did()))
+ },
_ => None,
}
}
-impl<'tcx> LenOutput<'tcx> {
- fn matches_is_empty_output(self, ty: Ty<'tcx>) -> bool {
+impl LenOutput {
+ fn matches_is_empty_output<'tcx>(self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ if let Some(segment) = extract_future_output(cx, ty) {
+ return match (self, segment.res) {
+ (_, Res::PrimTy(PrimTy::Bool)) => true,
+ (Self::Option(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Option, def_id) => true,
+ (Self::Result(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Result, def_id) => true,
+ _ => false,
+ };
+ }
+
match (self, ty.kind()) {
(_, &ty::Bool) => true,
(Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(),
- (Self::Result(id, err_ty), &ty::Adt(adt, subs)) if id == adt.did() => {
- subs.type_at(0).is_bool() && subs.type_at(1) == err_ty
- },
+ (Self::Result(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(),
_ => false,
}
}
@@ -301,9 +366,14 @@ impl<'tcx> LenOutput<'tcx> {
}
/// Checks if the given signature matches the expectations for `is_empty`
-fn check_is_empty_sig<'tcx>(sig: FnSig<'tcx>, self_kind: ImplicitSelfKind, len_output: LenOutput<'tcx>) -> bool {
+fn check_is_empty_sig<'tcx>(
+ cx: &LateContext<'tcx>,
+ sig: FnSig<'tcx>,
+ self_kind: ImplicitSelfKind,
+ len_output: LenOutput,
+) -> bool {
match &**sig.inputs_and_output {
- [arg, res] if len_output.matches_is_empty_output(*res) => {
+ [arg, res] if len_output.matches_is_empty_output(cx, *res) => {
matches!(
(arg.kind(), self_kind),
(ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef)
@@ -315,11 +385,11 @@ fn check_is_empty_sig<'tcx>(sig: FnSig<'tcx>, self_kind: ImplicitSelfKind, len_o
}
/// Checks if the given type has an `is_empty` method with the appropriate signature.
-fn check_for_is_empty<'tcx>(
- cx: &LateContext<'tcx>,
+fn check_for_is_empty(
+ cx: &LateContext<'_>,
span: Span,
self_kind: ImplicitSelfKind,
- output: LenOutput<'tcx>,
+ output: LenOutput,
impl_ty: DefId,
item_name: Symbol,
item_kind: &str,
@@ -352,6 +422,7 @@ fn check_for_is_empty<'tcx>(
Some(is_empty)
if !(is_empty.fn_has_self_parameter
&& check_is_empty_sig(
+ cx,
cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(),
self_kind,
output,
@@ -431,7 +502,7 @@ fn check_len(
&format!("using `{op}is_empty` is clearer and more explicit"),
format!(
"{op}{}.is_empty()",
- snippet_with_applicability(cx, receiver.span, "_", &mut applicability)
+ snippet_with_context(cx, receiver.span, span.ctxt(), "_", &mut applicability).0,
),
applicability,
);
@@ -444,13 +515,7 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex
let mut applicability = Applicability::MachineApplicable;
let lit1 = peel_ref_operators(cx, lit1);
- let mut lit_str = snippet_with_applicability(cx, lit1.span, "_", &mut applicability);
-
- // Wrap the expression in parentheses if it's a deref expression. Otherwise operator precedence will
- // cause the code to dereference boolean(won't compile).
- if let ExprKind::Unary(UnOp::Deref, _) = lit1.kind {
- lit_str = Cow::from(format!("({lit_str})"));
- }
+ let lit_str = Sugg::hir_with_context(cx, lit1, span.ctxt(), "_", &mut applicability).maybe_par();
span_lint_and_sugg(
cx,
diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs
index 7600777fa..51b5de27d 100644
--- a/src/tools/clippy/clippy_lints/src/let_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs
@@ -124,7 +124,7 @@ declare_clippy_lint! {
/// ```
#[clippy::version = "1.69.0"]
pub LET_UNDERSCORE_UNTYPED,
- pedantic,
+ restriction,
"non-binding `let` without a type annotation"
}
diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
new file mode 100644
index 000000000..c01e3882d
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
@@ -0,0 +1,45 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use rustc_hir::{Local, TyKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Detects when a variable is declared with an explicit type of `_`.
+ /// ### Why is this bad?
+ /// It adds noise, `: _` provides zero clarity or utility.
+ /// ### Example
+ /// ```rust,ignore
+ /// let my_number: _ = 1;
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// let my_number = 1;
+ /// ```
+ #[clippy::version = "1.69.0"]
+ pub LET_WITH_TYPE_UNDERSCORE,
+ complexity,
+ "unneeded underscore type (`_`) in a variable declaration"
+}
+declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]);
+
+impl LateLintPass<'_> for UnderscoreTyped {
+ fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
+ if_chain! {
+ if !in_external_macro(cx.tcx.sess, local.span);
+ if let Some(ty) = local.ty; // Ensure that it has a type defined
+ if let TyKind::Infer = &ty.kind; // that type is '_'
+ if local.span.ctxt() == ty.span.ctxt();
+ then {
+ span_lint_and_help(cx,
+ LET_WITH_TYPE_UNDERSCORE,
+ local.span,
+ "variable declared with type underscore",
+ Some(ty.span.with_lo(local.pat.span.hi())),
+ "remove the explicit type `_` declaration"
+ )
+ }
+ };
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index c626e0bd9..b0ec14855 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -1,13 +1,11 @@
#![feature(array_windows)]
#![feature(binary_heap_into_iter_sorted)]
#![feature(box_patterns)]
-#![feature(drain_filter)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![feature(let_chains)]
#![feature(lint_reasons)]
#![feature(never_type)]
-#![feature(once_cell)]
#![feature(rustc_private)]
#![feature(stmt_expr_attributes)]
#![recursion_limit = "512"]
@@ -67,6 +65,7 @@ mod declared_lints;
mod renamed_lints;
// begin lints modules, do not remove this comment, it’s used in `update_lints`
+mod allow_attributes;
mod almost_complete_range;
mod approx_const;
mod as_conversions;
@@ -87,6 +86,7 @@ mod casts;
mod checked_conversions;
mod cognitive_complexity;
mod collapsible_if;
+mod collection_is_never_read;
mod comparison_chain;
mod copies;
mod copy_iterator;
@@ -161,12 +161,15 @@ mod items_after_statements;
mod iter_not_returning_iterator;
mod large_const_arrays;
mod large_enum_variant;
+mod large_futures;
mod large_include_file;
mod large_stack_arrays;
mod len_zero;
mod let_if_seq;
mod let_underscore;
+mod let_with_type_underscore;
mod lifetimes;
+mod lines_filter_map_ok;
mod literal_representation;
mod loops;
mod macro_use;
@@ -177,9 +180,11 @@ mod manual_bits;
mod manual_clamp;
mod manual_is_ascii_check;
mod manual_let_else;
+mod manual_main_separator_str;
mod manual_non_exhaustive;
mod manual_rem_euclid;
mod manual_retain;
+mod manual_slice_size_calculation;
mod manual_string_new;
mod manual_strip;
mod map_unit_fn;
@@ -192,6 +197,7 @@ mod minmax;
mod misc;
mod misc_early;
mod mismatching_type_param_order;
+mod missing_assert_message;
mod missing_const_for_fn;
mod missing_doc;
mod missing_enforced_import_rename;
@@ -249,6 +255,7 @@ mod question_mark_used;
mod ranges;
mod rc_clone_in_vec_init;
mod read_zero_byte_vec;
+mod redundant_async_block;
mod redundant_clone;
mod redundant_closure_call;
mod redundant_else;
@@ -276,6 +283,7 @@ mod slow_vector_initialization;
mod std_instead_of_core;
mod strings;
mod strlen_on_c_strings;
+mod suspicious_doc_comments;
mod suspicious_operation_groupings;
mod suspicious_trait_impl;
mod suspicious_xor_used_as_pow;
@@ -283,6 +291,7 @@ mod swap;
mod swap_ptr_to_ref;
mod tabs_in_doc_comments;
mod temporary_assignment;
+mod tests_outside_test_module;
mod to_digit_is_some;
mod trailing_empty_array;
mod trait_bounds;
@@ -294,8 +303,10 @@ mod uninit_vec;
mod unit_return_expecting_ord;
mod unit_types;
mod unnamed_address;
+mod unnecessary_box_returns;
mod unnecessary_owned_empty_strings;
mod unnecessary_self_imports;
+mod unnecessary_struct_initialization;
mod unnecessary_wraps;
mod unnested_or_patterns;
mod unsafe_removed_from_name;
@@ -338,13 +349,17 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se
}
#[doc(hidden)]
-pub fn read_conf(sess: &Session, path: &io::Result<Option<PathBuf>>) -> Conf {
+pub fn read_conf(sess: &Session, path: &io::Result<(Option<PathBuf>, Vec<String>)>) -> Conf {
+ if let Ok((_, warnings)) = path {
+ for warning in warnings {
+ sess.warn(warning);
+ }
+ }
let file_name = match path {
- Ok(Some(path)) => path,
- Ok(None) => return Conf::default(),
+ Ok((Some(path), _)) => path,
+ Ok((None, _)) => return Conf::default(),
Err(error) => {
- sess.struct_err(format!("error finding Clippy's configuration file: {error}"))
- .emit();
+ sess.err(format!("error finding Clippy's configuration file: {error}"));
return Conf::default();
},
};
@@ -533,6 +548,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
.collect(),
))
});
+ store.register_early_pass(|| Box::new(utils::format_args_collector::FormatArgsCollector));
store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
store.register_late_pass(|_| Box::new(utils::author::Author));
let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
@@ -651,7 +667,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum));
store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
store.register_late_pass(|_| Box::new(regex::Regex));
- store.register_late_pass(|_| Box::new(copies::CopyAndPaste));
+ let ignore_interior_mutability = conf.ignore_interior_mutability.clone();
+ store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone())));
store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator));
store.register_late_pass(|_| Box::new(format::UselessFormat));
store.register_late_pass(|_| Box::new(swap::Swap));
@@ -738,7 +755,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
store.register_late_pass(|_| Box::new(returns::Return));
store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf));
- store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements));
+ store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements));
store.register_early_pass(|| Box::new(precedence::Precedence));
store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals));
store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue));
@@ -800,6 +817,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv())));
store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
+ let future_size_threshold = conf.future_size_threshold;
+ store.register_late_pass(move |_| Box::new(large_futures::LargeFuture::new(future_size_threshold)));
store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex));
store.register_late_pass(|_| Box::new(if_not_else::IfNotElse));
store.register_late_pass(|_| Box::new(equatable_if_let::PatternEquality));
@@ -924,6 +943,22 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
))
});
store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
+ store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead));
+ store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
+ store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock));
+ store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
+ store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute));
+ store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(msrv())));
+ store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct));
+ store.register_late_pass(move |_| {
+ Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(
+ avoid_breaking_exported_api,
+ ))
+ });
+ store.register_late_pass(|_| Box::new(lines_filter_map_ok::LinesFilterMapOk));
+ store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule));
+ store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation));
+ store.register_early_pass(|| Box::new(suspicious_doc_comments::SuspiciousDocComments));
// add lints here, do not remove this comment, it's used in `new_lint`
}
diff --git a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs
new file mode 100644
index 000000000..b0f927647
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs
@@ -0,0 +1,100 @@
+use clippy_utils::{
+ diagnostics::span_lint_and_then, is_diag_item_method, is_trait_method, match_def_path, path_to_local_id, paths,
+ ty::match_type,
+};
+use rustc_errors::Applicability;
+use rustc_hir::{Body, Closure, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Detect uses of `lines.filter_map(Result::ok)` or `lines.flat_map(Result::ok)`
+ /// when `lines` has type `std::io::Lines`.
+ ///
+ /// ### Why is this bad?
+ /// `Lines` instances might produce a never-ending stream of `Err`, in which case
+ /// `filter_map(Result::ok)` will enter an infinite loop while waiting for an
+ /// `Ok` variant. Calling `next()` once is sufficient to enter the infinite loop,
+ /// even in the absence of explicit loops in the user code.
+ ///
+ /// This situation can arise when working with user-provided paths. On some platforms,
+ /// `std::fs::File::open(path)` might return `Ok(fs)` even when `path` is a directory,
+ /// but any later attempt to read from `fs` will return an error.
+ ///
+ /// ### Known problems
+ /// This lint suggests replacing `filter_map()` or `flat_map()` applied to a `Lines`
+ /// instance in all cases. There two cases where the suggestion might not be
+ /// appropriate or necessary:
+ ///
+ /// - If the `Lines` instance can never produce any error, or if an error is produced
+ /// only once just before terminating the iterator, using `map_while()` is not
+ /// necessary but will not do any harm.
+ /// - If the `Lines` instance can produce intermittent errors then recover and produce
+ /// successful results, using `map_while()` would stop at the first error.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # use std::{fs::File, io::{self, BufRead, BufReader}};
+ /// # let _ = || -> io::Result<()> {
+ /// let mut lines = BufReader::new(File::open("some-path")?).lines().filter_map(Result::ok);
+ /// // If "some-path" points to a directory, the next statement never terminates:
+ /// let first_line: Option<String> = lines.next();
+ /// # Ok(()) };
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # use std::{fs::File, io::{self, BufRead, BufReader}};
+ /// # let _ = || -> io::Result<()> {
+ /// let mut lines = BufReader::new(File::open("some-path")?).lines().map_while(Result::ok);
+ /// let first_line: Option<String> = lines.next();
+ /// # Ok(()) };
+ /// ```
+ #[clippy::version = "1.70.0"]
+ pub LINES_FILTER_MAP_OK,
+ suspicious,
+ "filtering `std::io::Lines` with `filter_map()` or `flat_map()` might cause an infinite loop"
+}
+declare_lint_pass!(LinesFilterMapOk => [LINES_FILTER_MAP_OK]);
+
+impl LateLintPass<'_> for LinesFilterMapOk {
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+ if let ExprKind::MethodCall(fm_method, fm_receiver, [fm_arg], fm_span) = expr.kind &&
+ is_trait_method(cx, expr, sym::Iterator) &&
+ (fm_method.ident.as_str() == "filter_map" || fm_method.ident.as_str() == "flat_map") &&
+ match_type(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), &paths::STD_IO_LINES)
+ {
+ let lint = match &fm_arg.kind {
+ // Detect `Result::ok`
+ ExprKind::Path(qpath) =>
+ cx.qpath_res(qpath, fm_arg.hir_id).opt_def_id().map(|did|
+ match_def_path(cx, did, &paths::CORE_RESULT_OK_METHOD)).unwrap_or_default(),
+ // Detect `|x| x.ok()`
+ ExprKind::Closure(Closure { body, .. }) =>
+ if let Body { params: [param], value, .. } = cx.tcx.hir().body(*body) &&
+ let ExprKind::MethodCall(method, receiver, [], _) = value.kind &&
+ path_to_local_id(receiver, param.pat.hir_id) &&
+ let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id)
+ {
+ is_diag_item_method(cx, method_did, sym::Result) && method.ident.as_str() == "ok"
+ } else {
+ false
+ }
+ _ => false,
+ };
+ if lint {
+ span_lint_and_then(cx,
+ LINES_FILTER_MAP_OK,
+ fm_span,
+ &format!("`{}()` will run forever if the iterator repeatedly produces an `Err`", fm_method.ident),
+ |diag| {
+ diag.span_note(
+ fm_receiver.span,
+ "this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error");
+ diag.span_suggestion(fm_span, "replace with", "map_while(Result::ok)", Applicability::MaybeIncorrect);
+ });
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
index 8c27c0940..1e02a30e3 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
@@ -9,7 +9,7 @@ use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Expr, Pat, PatKind};
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, DefIdTree};
+use rustc_middle::ty;
use rustc_span::source_map::Span;
/// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the
diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
index b1bc10802..f0a1b1dfe 100644
--- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
@@ -124,8 +124,7 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t
#[allow(clippy::too_many_lines)]
fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: HirId) -> NeverLoopResult {
match expr.kind {
- ExprKind::Box(e)
- | ExprKind::Unary(_, e)
+ ExprKind::Unary(_, e)
| ExprKind::Cast(e, _)
| ExprKind::Type(e, _)
| ExprKind::Field(e, _)
diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
index 540656a2c..9d9341559 100644
--- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
@@ -1,15 +1,17 @@
use super::SAME_ITEM_PUSH;
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::path_to_local;
-use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, Stmt, StmtKind};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
+use rustc_span::SyntaxContext;
use std::iter::Iterator;
/// Detects for loop pushing the same item into a Vec
@@ -20,9 +22,10 @@ pub(super) fn check<'tcx>(
body: &'tcx Expr<'_>,
_: &'tcx Expr<'_>,
) {
- fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) {
- let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
- let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
+ fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: SyntaxContext) {
+ let mut app = Applicability::Unspecified;
+ let vec_str = snippet_with_context(cx, vec.span, ctxt, "", &mut app).0;
+ let item_str = snippet_with_context(cx, pushed_item.span, ctxt, "", &mut app).0;
span_lint_and_help(
cx,
@@ -43,7 +46,7 @@ pub(super) fn check<'tcx>(
walk_expr(&mut same_item_push_visitor, body);
if_chain! {
if same_item_push_visitor.should_lint();
- if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push;
+ if let Some((vec, pushed_item, ctxt)) = same_item_push_visitor.vec_push;
let vec_ty = cx.typeck_results().expr_ty(vec);
let ty = vec_ty.walk().nth(1).unwrap().expect_ty();
if cx
@@ -69,11 +72,11 @@ pub(super) fn check<'tcx>(
then {
match init.kind {
// immutable bindings that are initialized with literal
- ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
+ ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt),
// immutable bindings that are initialized with constant
ExprKind::Path(ref path) => {
if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) {
- emit_lint(cx, vec, pushed_item);
+ emit_lint(cx, vec, pushed_item, ctxt);
}
}
_ => {},
@@ -82,11 +85,11 @@ pub(super) fn check<'tcx>(
}
},
// constant
- Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item),
+ Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt),
_ => {},
}
},
- ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
+ ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt),
_ => {},
}
}
@@ -98,7 +101,7 @@ struct SameItemPushVisitor<'a, 'tcx> {
non_deterministic_expr: bool,
multiple_pushes: bool,
// this field holds the last vec push operation visited, which should be the only push seen
- vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>,
+ vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, SyntaxContext)>,
cx: &'a LateContext<'tcx>,
used_locals: FxHashSet<HirId>,
}
@@ -118,7 +121,7 @@ impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> {
if_chain! {
if !self.non_deterministic_expr;
if !self.multiple_pushes;
- if let Some((vec, _)) = self.vec_push;
+ if let Some((vec, _, _)) = self.vec_push;
if let Some(hir_id) = path_to_local(vec);
then {
!self.used_locals.contains(&hir_id)
@@ -173,7 +176,10 @@ impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> {
// Given some statement, determine if that statement is a push on a Vec. If it is, return
// the Vec being pushed into and the item being pushed
-fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
+fn get_vec_push<'tcx>(
+ cx: &LateContext<'tcx>,
+ stmt: &'tcx Stmt<'_>,
+) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, SyntaxContext)> {
if_chain! {
// Extract method being called
if let StmtKind::Semi(semi_stmt) = &stmt.kind;
@@ -184,7 +190,7 @@ fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec);
if path.ident.name.as_str() == "push";
then {
- return Some((self_expr, pushed_item))
+ return Some((self_expr, pushed_item, semi_stmt.span.ctxt()))
}
}
None
diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
index 3778eb4c7..577bc1d66 100644
--- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
@@ -1,12 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::match_function_call_with_def_id;
use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{
AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound,
- ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind,
+ ImplItem, Item, ItemKind, LifetimeName, Node, Term, TraitRef, Ty, TyKind, TypeBindingKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -46,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
decl: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>,
span: Span,
- _: LocalDefId,
+ def_id: LocalDefId,
) {
if_chain! {
if let Some(header) = kind.header();
@@ -60,6 +59,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
if let ExprKind::Block(block, _) = body.value.kind;
if block.stmts.is_empty();
if let Some(closure_body) = desugared_async_block(cx, block);
+ if let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) =
+ cx.tcx.hir().get_by_def_id(def_id);
then {
let header_span = span.with_hi(ret_ty.span.hi());
@@ -70,15 +71,22 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
"this function can be simplified using the `async fn` syntax",
|diag| {
if_chain! {
+ if let Some(vis_snip) = snippet_opt(cx, *vis_span);
if let Some(header_snip) = snippet_opt(cx, header_span);
if let Some(ret_pos) = position_before_rarrow(&header_snip);
if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output);
then {
+ let header_snip = if vis_snip.is_empty() {
+ format!("async {}", &header_snip[..ret_pos])
+ } else {
+ format!("{} async {}", vis_snip, &header_snip[vis_snip.len() + 1..ret_pos])
+ };
+
let help = format!("make the function `async` and {ret_sugg}");
diag.span_suggestion(
header_span,
help,
- format!("async {}{ret_snip}", &header_snip[..ret_pos]),
+ format!("{header_snip}{ret_snip}"),
Applicability::MachineApplicable
);
@@ -175,16 +183,10 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName])
fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> {
if_chain! {
if let Some(block_expr) = block.expr;
- if let Some(args) = cx
- .tcx
- .lang_items()
- .identity_future_fn()
- .and_then(|def_id| match_function_call_with_def_id(cx, block_expr, def_id));
- if args.len() == 1;
if let Expr {
kind: ExprKind::Closure(&Closure { body, .. }),
..
- } = args[0];
+ } = block_expr;
let closure_body = cx.tcx.hir().body(body);
if closure_body.generator_kind == Some(GeneratorKind::Async(AsyncGeneratorKind::Block));
then {
diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs
index 462d73cf0..bc815dc4a 100644
--- a/src/tools/clippy/clippy_lints/src/manual_bits.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs
@@ -1,11 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::get_parent_expr;
use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
-use rustc_lint::{LateContext, LateLintPass};
+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::sym;
@@ -55,13 +56,17 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits {
if_chain! {
if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind;
if let BinOpKind::Mul = &bin_op.node;
+ if !in_external_macro(cx.sess(), expr.span);
+ let ctxt = expr.span.ctxt();
+ if left_expr.span.ctxt() == ctxt;
+ if right_expr.span.ctxt() == ctxt;
if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr);
if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_));
if let ExprKind::Lit(lit) = &other_expr.kind;
if let LitKind::Int(8, _) = lit.node;
then {
let mut app = Applicability::MachineApplicable;
- let ty_snip = snippet_with_applicability(cx, real_ty.span, "..", &mut app);
+ let ty_snip = snippet_with_context(cx, real_ty.span, ctxt, "..", &mut app).0;
let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS"));
span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
index f239736d3..440362b96 100644
--- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
@@ -6,7 +6,8 @@ use clippy_utils::ty::implements_trait;
use clippy_utils::visitors::is_const_evaluatable;
use clippy_utils::MaybePath;
use clippy_utils::{
- eq_expr_value, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks, peel_blocks_with_stmt,
+ eq_expr_value, in_constant, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks,
+ peel_blocks_with_stmt,
};
use itertools::Itertools;
use rustc_errors::Applicability;
@@ -117,7 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp {
if !self.msrv.meets(msrvs::CLAMP) {
return;
}
- if !expr.span.from_expansion() {
+ if !expr.span.from_expansion() && !in_constant(cx, expr.hir_id) {
let suggestion = is_if_elseif_else_pattern(cx, expr)
.or_else(|| is_max_min_pattern(cx, expr))
.or_else(|| is_call_max_min_pattern(cx, expr))
@@ -130,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp {
}
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
- if !self.msrv.meets(msrvs::CLAMP) {
+ if !self.msrv.meets(msrvs::CLAMP) || in_constant(cx, block.hir_id) {
return;
}
for suggestion in is_two_if_pattern(cx, block) {
diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
index 2fd32c009..31264261f 100644
--- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
@@ -1,5 +1,5 @@
use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, source::snippet};
+use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, sugg::Sugg};
use rustc_ast::ast::RangeLimits;
use rustc_ast::LitKind::{Byte, Char};
use rustc_errors::Applicability;
@@ -115,15 +115,8 @@ fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &Cha
CharRange::Otherwise => None,
} {
let default_snip = "..";
- // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for
- // macro span, so we check applicability manually by comparing `recv` is not default.
- let recv = snippet(cx, recv.span, default_snip);
-
- let applicability = if recv == default_snip {
- Applicability::HasPlaceholders
- } else {
- Applicability::MachineApplicable
- };
+ let mut app = Applicability::MachineApplicable;
+ let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par();
span_lint_and_sugg(
cx,
@@ -132,7 +125,7 @@ fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &Cha
"manual check for common ascii range",
"try",
format!("{recv}.{sugg}()"),
- applicability,
+ app,
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs
new file mode 100644
index 000000000..c292bbe4e
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs
@@ -0,0 +1,74 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::{is_trait_method, match_def_path, paths, peel_hir_expr_refs};
+use rustc_errors::Applicability;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{Expr, ExprKind, Mutability, QPath};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for references on `std::path::MAIN_SEPARATOR.to_string()` used
+ /// to build a `&str`.
+ ///
+ /// ### Why is this bad?
+ /// There exists a `std::path::MAIN_SEPARATOR_STR` which does not require
+ /// an extra memory allocation.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let s: &str = &std::path::MAIN_SEPARATOR.to_string();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let s: &str = std::path::MAIN_SEPARATOR_STR;
+ /// ```
+ #[clippy::version = "1.70.0"]
+ pub MANUAL_MAIN_SEPARATOR_STR,
+ complexity,
+ "`&std::path::MAIN_SEPARATOR.to_string()` can be replaced by `std::path::MAIN_SEPARATOR_STR`"
+}
+
+pub struct ManualMainSeparatorStr {
+ msrv: Msrv,
+}
+
+impl ManualMainSeparatorStr {
+ #[must_use]
+ pub fn new(msrv: Msrv) -> Self {
+ Self { msrv }
+ }
+}
+
+impl_lint_pass!(ManualMainSeparatorStr => [MANUAL_MAIN_SEPARATOR_STR]);
+
+impl LateLintPass<'_> for ManualMainSeparatorStr {
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+ if self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR) &&
+ let (target, _) = peel_hir_expr_refs(expr) &&
+ is_trait_method(cx, target, sym::ToString) &&
+ let ExprKind::MethodCall(path, receiver, &[], _) = target.kind &&
+ path.ident.name == sym::to_string &&
+ let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind &&
+ let Res::Def(DefKind::Const, receiver_def_id) = path.res &&
+ match_def_path(cx, receiver_def_id, &paths::PATH_MAIN_SEPARATOR) &&
+ let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind() &&
+ ty.is_str()
+ {
+ span_lint_and_sugg(
+ cx,
+ MANUAL_MAIN_SEPARATOR_STR,
+ expr.span,
+ "taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`",
+ "replace with",
+ "std::path::MAIN_SEPARATOR_STR".to_owned(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+
+ extract_msrv_attr!(LateContext);
+}
diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
index 9a84068d4..0e22485db 100644
--- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
@@ -8,7 +8,6 @@ use rustc_errors::Applicability;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::{self as hir, Expr, ExprKind, QPath};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
-use rustc_middle::ty::DefIdTree;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::{sym, Span};
diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
index 38f41d077..aafee9271 100644
--- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
@@ -1,7 +1,7 @@
use clippy_utils::consts::{constant_full_int, FullInt};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
use clippy_utils::{in_constant, path_to_local};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
@@ -60,12 +60,16 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
return;
}
+ // (x % c + c) % c
if let ExprKind::Binary(op1, expr1, right) = expr.kind
&& op1.node == BinOpKind::Rem
+ && let ctxt = expr.span.ctxt()
+ && expr1.span.ctxt() == ctxt
&& let Some(const1) = check_for_unsigned_int_constant(cx, right)
&& let ExprKind::Binary(op2, left, right) = expr1.kind
&& op2.node == BinOpKind::Add
&& let Some((const2, expr2)) = check_for_either_unsigned_int_constant(cx, left, right)
+ && expr2.span.ctxt() == ctxt
&& let ExprKind::Binary(op3, expr3, right) = expr2.kind
&& op3.node == BinOpKind::Rem
&& let Some(const3) = check_for_unsigned_int_constant(cx, right)
@@ -86,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
};
let mut app = Applicability::MachineApplicable;
- let rem_of = snippet_with_applicability(cx, expr3.span, "_", &mut app);
+ let rem_of = snippet_with_context(cx, expr3.span, ctxt, "_", &mut app).0;
span_lint_and_sugg(
cx,
MANUAL_REM_EUCLID,
diff --git a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
new file mode 100644
index 000000000..92ee79453
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
@@ -0,0 +1,93 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{expr_or_init, in_constant};
+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::symbol::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// When `a` is `&[T]`, detect `a.len() * size_of::<T>()` and suggest `size_of_val(a)`
+ /// instead.
+ ///
+ /// ### Why is this better?
+ /// * Shorter to write
+ /// * Removes the need for the human and the compiler to worry about overflow in the
+ /// multiplication
+ /// * Potentially faster at runtime as rust emits special no-wrapping flags when it
+ /// calculates the byte length
+ /// * Less turbofishing
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let data : &[i32] = &[1, 2, 3];
+ /// let newlen = data.len() * std::mem::size_of::<i32>();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # let data : &[i32] = &[1, 2, 3];
+ /// let newlen = std::mem::size_of_val(data);
+ /// ```
+ #[clippy::version = "1.70.0"]
+ pub MANUAL_SLICE_SIZE_CALCULATION,
+ complexity,
+ "manual slice size calculation"
+}
+declare_lint_pass!(ManualSliceSizeCalculation => [MANUAL_SLICE_SIZE_CALCULATION]);
+
+impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+ // Does not apply inside const because size_of_value is not cost in stable.
+ if !in_constant(cx, expr.hir_id)
+ && let ExprKind::Binary(ref op, left, right) = expr.kind
+ && BinOpKind::Mul == op.node
+ && let Some(_receiver) = simplify(cx, left, right)
+ {
+ span_lint_and_help(
+ cx,
+ MANUAL_SLICE_SIZE_CALCULATION,
+ expr.span,
+ "manual slice size calculation",
+ None,
+ "consider using std::mem::size_of_value instead");
+ }
+ }
+}
+
+fn simplify<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr1: &'tcx Expr<'tcx>,
+ expr2: &'tcx Expr<'tcx>,
+) -> Option<&'tcx Expr<'tcx>> {
+ let expr1 = expr_or_init(cx, expr1);
+ let expr2 = expr_or_init(cx, expr2);
+
+ simplify_half(cx, expr1, expr2).or_else(|| simplify_half(cx, expr2, expr1))
+}
+
+fn simplify_half<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr1: &'tcx Expr<'tcx>,
+ expr2: &'tcx Expr<'tcx>,
+) -> Option<&'tcx Expr<'tcx>> {
+ if
+ // expr1 is `[T1].len()`?
+ let ExprKind::MethodCall(method_path, receiver, _, _) = expr1.kind
+ && method_path.ident.name == sym::len
+ && let receiver_ty = cx.typeck_results().expr_ty(receiver)
+ && let ty::Slice(ty1) = receiver_ty.peel_refs().kind()
+ // expr2 is `size_of::<T2>()`?
+ && let ExprKind::Call(func, _) = expr2.kind
+ && let ExprKind::Path(ref func_qpath) = func.kind
+ && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
+ && cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id)
+ && let Some(ty2) = cx.typeck_results().node_substs(func.hir_id).types().next()
+ // T1 == T2?
+ && *ty1 == ty2
+ {
+ Some(receiver)
+ } else {
+ None
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/match_result_ok.rs b/src/tools/clippy/clippy_lints/src/match_result_ok.rs
index a020282d2..6ec978403 100644
--- a/src/tools/clippy/clippy_lints/src/match_result_ok.rs
+++ b/src/tools/clippy/clippy_lints/src/match_result_ok.rs
@@ -1,11 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher;
-use clippy_utils::method_chain_args;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::is_res_lang_ctor;
+use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, PatKind, QPath};
+use rustc_hir::{Expr, ExprKind, LangItem, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
@@ -58,17 +58,18 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
};
if_chain! {
- if let ExprKind::MethodCall(ok_path, result_types_0, ..) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
- if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation
- if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() method use std::marker::Sized;
- if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result);
- if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
-
+ if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
+ if let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind; //get operation
+ if ok_path.ident.as_str() == "ok";
+ if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
+ if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome);
+ let ctxt = expr.span.ctxt();
+ if let_expr.span.ctxt() == ctxt;
+ if let_pat.span.ctxt() == ctxt;
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, let_expr.span.until(ok_path.ident.span), "", &mut applicability);
+ let some_expr_string = snippet_with_context(cx, ok_pat.span, ctxt, "", &mut applicability).0;
+ let trimmed_ok = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0;
let sugg = format!(
"{ifwhile} let Ok({some_expr_string}) = {}",
trimmed_ok.trim().trim_end_matches('.'),
diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs
index 587c926dc..b94501bf0 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs
@@ -10,7 +10,6 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::LangItem::{OptionNone, ResultErr};
use rustc_hir::{Arm, Expr, PatKind};
use rustc_lint::LateContext;
-use rustc_middle::ty::DefIdTree;
use rustc_span::sym;
use super::MANUAL_UNWRAP_OR;
@@ -33,14 +32,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee:
let reindented_or_body =
reindent_multiline(or_body_snippet.into(), true, Some(indent));
- let suggestion = if scrutinee.span.from_expansion() {
- // we don't want parentheses around macro, e.g. `(some_macro!()).unwrap_or(0)`
- sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..")
- }
- else {
- sugg::Sugg::hir(cx, scrutinee, "..").maybe_par()
- };
-
+ let mut app = Applicability::MachineApplicable;
+ let suggestion = sugg::Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_par();
span_lint_and_sugg(
cx,
MANUAL_UNWRAP_OR, expr.span,
@@ -49,7 +42,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee:
format!(
"{suggestion}.unwrap_or({reindented_or_body})",
),
- Applicability::MachineApplicable,
+ app,
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs
index 1c216e135..df1e585f1 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs
@@ -10,9 +10,9 @@ use rustc_middle::ty;
use super::MATCH_BOOL;
-pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
+pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
// Type of expression is `bool`.
- if *cx.typeck_results().expr_ty(ex).kind() == ty::Bool {
+ if *cx.typeck_results().expr_ty(scrutinee).kind() == ty::Bool {
span_lint_and_then(
cx,
MATCH_BOOL,
@@ -36,24 +36,26 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
};
if let Some((true_expr, false_expr)) = exprs {
+ let mut app = Applicability::HasPlaceholders;
+ let ctxt = expr.span.ctxt();
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))
+ snippet(cx, scrutinee.span, "b"),
+ expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app),
+ expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
)),
(false, true) => Some(format!(
"if {} {}",
- snippet(cx, ex.span, "b"),
- expr_block(cx, true_expr, None, "..", Some(expr.span))
+ snippet(cx, scrutinee.span, "b"),
+ expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app)
)),
(true, false) => {
- let test = Sugg::hir(cx, ex, "..");
+ let test = Sugg::hir(cx, scrutinee, "..");
Some(format!(
"if {} {}",
!test,
- expr_block(cx, false_expr, None, "..", Some(expr.span))
+ expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
))
},
(true, true) => None,
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs b/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs
index 80f964ba1..aba4c85c5 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs
@@ -1,13 +1,14 @@
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
-use clippy_utils::source::snippet;
+use clippy_utils::source::{snippet, walk_span_to_context};
use clippy_utils::sugg::Sugg;
use core::iter::once;
+use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
use rustc_lint::LateContext;
use super::MATCH_REF_PATS;
-pub(crate) fn check<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>)
+pub(crate) fn check<'a, 'b, I>(cx: &LateContext<'_>, scrutinee: &Expr<'_>, pats: I, expr: &Expr<'_>)
where
'b: 'a,
I: Clone + Iterator<Item = &'a Pat<'b>>,
@@ -17,13 +18,28 @@ where
}
let (first_sugg, msg, title);
- let span = ex.span.source_callsite();
- if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind {
- first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
+ let ctxt = expr.span.ctxt();
+ let mut app = Applicability::Unspecified;
+ if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = scrutinee.kind {
+ if scrutinee.span.ctxt() != ctxt {
+ return;
+ }
+ first_sugg = once((
+ scrutinee.span,
+ Sugg::hir_with_context(cx, inner, ctxt, "..", &mut app).to_string(),
+ ));
msg = "try";
title = "you don't need to add `&` to both the expression and the patterns";
} else {
- first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
+ let Some(span) = walk_span_to_context(scrutinee.span, ctxt) else {
+ return;
+ };
+ first_sugg = once((
+ span,
+ Sugg::hir_with_context(cx, scrutinee, ctxt, "..", &mut app)
+ .deref()
+ .to_string(),
+ ));
msg = "instead of prefixing all patterns with `&`, you can dereference the expression";
title = "you don't need to add `&` to all patterns";
}
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs
index 065a5c726..89da7a55c 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs
@@ -1,10 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::macros::HirNode;
-use clippy_utils::source::{indent_of, snippet, snippet_block, snippet_with_applicability};
-use clippy_utils::sugg::Sugg;
+use clippy_utils::source::{indent_of, snippet, snippet_block_with_context, snippet_with_applicability};
use clippy_utils::{get_parent_expr, is_refutable, peel_blocks};
use rustc_errors::Applicability;
-use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind};
+use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind, StmtKind};
use rustc_lint::LateContext;
use rustc_span::Span;
@@ -24,21 +23,30 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
let matched_vars = ex.span;
let bind_names = arms[0].pat.span;
let match_body = peel_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()
- };
+ let mut app = Applicability::MaybeIncorrect;
+ let mut snippet_body = snippet_block_with_context(
+ cx,
+ match_body.span,
+ arms[0].span.ctxt(),
+ "..",
+ Some(expr.span),
+ &mut app,
+ )
+ .0
+ .to_string();
// Do we need to add ';' to suggestion ?
- if let ExprKind::Block(block, _) = match_body.kind {
- // macro + expr_ty(body) == ()
- if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() {
- snippet_body.push(';');
+ if let Node::Stmt(stmt) = cx.tcx.hir().get_parent(expr.hir_id)
+ && let StmtKind::Expr(_) = stmt.kind
+ && match match_body.kind {
+ // We don't need to add a ; to blocks, unless that block is from a macro expansion
+ ExprKind::Block(block, _) => block.span.from_expansion(),
+ _ => true,
}
+ {
+ snippet_body.push(';');
}
- let mut applicability = Applicability::MaybeIncorrect;
match arms[0].pat.kind {
PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => {
let (target_span, sugg) = match opt_parent_assign_span(cx, ex) {
@@ -48,7 +56,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
(ex, expr),
(bind_names, matched_vars),
&snippet_body,
- &mut applicability,
+ &mut app,
Some(span),
true,
);
@@ -60,7 +68,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
"this assignment could be simplified",
"consider removing the `match` expression",
sugg,
- applicability,
+ app,
);
return;
@@ -69,10 +77,10 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
span,
format!(
"let {} = {};\n{}let {} = {snippet_body};",
- snippet_with_applicability(cx, bind_names, "..", &mut applicability),
- snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
+ snippet_with_applicability(cx, bind_names, "..", &mut app),
+ snippet_with_applicability(cx, matched_vars, "..", &mut app),
" ".repeat(indent_of(cx, expr.span).unwrap_or(0)),
- snippet_with_applicability(cx, pat_span, "..", &mut applicability)
+ snippet_with_applicability(cx, pat_span, "..", &mut app)
),
),
None => {
@@ -81,7 +89,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
(ex, expr),
(bind_names, matched_vars),
&snippet_body,
- &mut applicability,
+ &mut app,
None,
true,
);
@@ -96,7 +104,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
"this match could be written as a `let` statement",
"consider using a `let` statement",
sugg,
- applicability,
+ app,
);
},
PatKind::Wild => {
@@ -106,7 +114,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
(ex, expr),
(bind_names, matched_vars),
&snippet_body,
- &mut applicability,
+ &mut app,
None,
false,
);
@@ -118,7 +126,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
"this match could be replaced by its scrutinee and body",
"consider using the scrutinee and body instead",
sugg,
- applicability,
+ app,
);
} else {
span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs
index 7b15a307f..97ecca450 100644
--- a/src/tools/clippy/clippy_lints/src/matches/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs
@@ -925,7 +925,7 @@ declare_clippy_lint! {
#[clippy::version = "1.66.0"]
pub MANUAL_FILTER,
complexity,
- "reimplentation of `filter`"
+ "reimplementation of `filter`"
}
#[derive(Default)]
diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
index 81bebff34..7b609ff3d 100644
--- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
@@ -1,6 +1,6 @@
use super::REDUNDANT_PATTERN_MATCHING;
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet;
+use clippy_utils::source::{snippet, walk_span_to_context};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop};
use clippy_utils::visitors::any_temporaries_need_ordered_drop;
@@ -12,7 +12,7 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp};
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty};
+use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
use rustc_span::{sym, Symbol};
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
@@ -150,22 +150,25 @@ fn find_sugg_for_if_let<'tcx>(
// if/while let ... = ... { ... }
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
let expr_span = expr.span;
+ let ctxt = expr.span.ctxt();
// if/while let ... = ... { ... }
- // ^^^
- let op_span = result_expr.span.source_callsite();
+ // ^^^
+ let Some(res_span) = walk_span_to_context(result_expr.span.source_callsite(), ctxt) else {
+ return;
+ };
// if/while let ... = ... { ... }
- // ^^^^^^^^^^^^^^^^^^^
- let span = expr_span.until(op_span.shrink_to_hi());
+ // ^^^^^^^^^^^^^^^^^^^^^^
+ let span = expr_span.until(res_span.shrink_to_hi());
- let app = if needs_drop {
+ let mut app = if needs_drop {
Applicability::MaybeIncorrect
} else {
Applicability::MachineApplicable
};
- let sugg = Sugg::hir_with_macro_callsite(cx, result_expr, "_")
+ let sugg = Sugg::hir_with_context(cx, result_expr, ctxt, "_", &mut app)
.maybe_par()
.to_string();
diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
index b33a24781..04225beeb 100644
--- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
@@ -321,7 +321,6 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
self.has_significant_drop = true;
}
}
- ExprKind::Box(..) |
ExprKind::Array(..) |
ExprKind::Call(..) |
ExprKind::Unary(..) |
diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
index 19b49c44d..ad47c1389 100644
--- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
@@ -67,8 +67,10 @@ fn report_single_pattern(
els: Option<&Expr<'_>>,
) {
let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
+ let ctxt = expr.span.ctxt();
+ let mut app = Applicability::HasPlaceholders;
let els_str = els.map_or(String::new(), |els| {
- format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
+ format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app))
});
let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
@@ -103,7 +105,7 @@ fn report_single_pattern(
// PartialEq for different reference counts may not exist.
"&".repeat(ref_count_diff),
snippet(cx, arms[0].pat.span, ".."),
- expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
+ expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
);
(msg, sugg)
} else {
@@ -112,21 +114,13 @@ fn report_single_pattern(
"if let {} = {} {}{els_str}",
snippet(cx, arms[0].pat.span, ".."),
snippet(cx, ex.span, ".."),
- expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
+ expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
);
(msg, sugg)
}
};
- span_lint_and_sugg(
- cx,
- lint,
- expr.span,
- msg,
- "try this",
- sugg,
- Applicability::HasPlaceholders,
- );
+ span_lint_and_sugg(cx, lint, expr.span, msg, "try this", sugg, app);
}
fn check_opt_like<'a>(
diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs
index 35024ec12..8a921d4af 100644
--- a/src/tools/clippy/clippy_lints/src/mem_replace.rs
+++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs
@@ -1,12 +1,13 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_non_aggregate_primitive_type;
-use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res};
+use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res, peel_ref_operators};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::LangItem::OptionNone;
-use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
+use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -101,40 +102,26 @@ declare_clippy_lint! {
impl_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) {
- // Check that second argument is `Option::None`
- if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
- // 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, replaced) => {
- if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind {
- replaced_path
- } else {
- return;
- }
- },
- ExprKind::Path(QPath::Resolved(None, 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_option_with_none(cx: &LateContext<'_>, dest: &Expr<'_>, expr_span: Span) {
+ // 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
+ // the replacee's expr after peeling off the `&mut`
+ let sugg_expr = peel_ref_operators(cx, dest);
+ 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()",
+ Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "", &mut applicability).maybe_par()
+ ),
+ applicability,
+ );
}
fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
@@ -200,10 +187,6 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
if is_non_aggregate_primitive_type(expr_type) {
return;
}
- // disable lint for Option since it is covered in another lint
- if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
- return;
- }
if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) {
span_lint_and_then(
cx,
@@ -246,11 +229,13 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
if cx.tcx.is_diagnostic_item(sym::mem_replace, def_id);
then {
- check_replace_option_with_none(cx, src, dest, expr.span);
- check_replace_with_uninit(cx, src, dest, expr.span);
- if self.msrv.meets(msrvs::MEM_TAKE) {
+ // Check that second argument is `Option::None`
+ if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
+ check_replace_option_with_none(cx, dest, expr.span);
+ } else if self.msrv.meets(msrvs::MEM_TAKE) {
check_replace_with_default(cx, src, dest, expr.span);
}
+ check_replace_with_uninit(cx, src, dest, expr.span);
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs
index 4720a6e68..008533488 100644
--- a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs
@@ -1,6 +1,6 @@
use super::{contains_return, BIND_INSTEAD_OF_MAP};
use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::{snippet, snippet_with_macro_callsite};
+use clippy_utils::source::{snippet, snippet_with_context};
use clippy_utils::{peel_blocks, visitors::find_all_ret_expressions};
use if_chain::if_chain;
use rustc_errors::Applicability;
@@ -8,7 +8,6 @@ use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::{LangItem, QPath};
use rustc_lint::LateContext;
-use rustc_middle::ty::DefIdTree;
use rustc_span::Span;
pub(crate) struct OptionAndThenSome;
@@ -77,11 +76,8 @@ pub(crate) trait BindInsteadOfMap {
if !contains_return(inner_expr);
if let Some(msg) = Self::lint_msg(cx);
then {
- 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 mut app = Applicability::MachineApplicable;
+ let some_inner_snip = snippet_with_context(cx, inner_expr.span, closure_expr.span.ctxt(), "_", &mut app).0;
let closure_args_snip = snippet(cx, closure_args_span, "..");
let option_snip = snippet(cx, recv.span, "..");
@@ -93,7 +89,7 @@ pub(crate) trait BindInsteadOfMap {
&msg,
"try this",
note,
- Applicability::MachineApplicable,
+ app,
);
true
} else {
diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs
index 56b7fbb9d..079df2226 100644
--- a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs
@@ -6,7 +6,7 @@ use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_lint::Lint;
-use rustc_middle::ty::{self, DefIdTree};
+use rustc_middle::ty;
/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
pub(super) fn check(
diff --git a/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs
new file mode 100644
index 000000000..67ad58d5a
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs
@@ -0,0 +1,53 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_range_full;
+use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::{Expr, ExprKind, LangItem, QPath};
+use rustc_lint::LateContext;
+use rustc_span::symbol::sym;
+use rustc_span::Span;
+
+use super::CLEAR_WITH_DRAIN;
+
+// Add `String` here when it is added to diagnostic items
+const ACCEPTABLE_TYPES_WITH_ARG: [rustc_span::Symbol; 2] = [sym::Vec, sym::VecDeque];
+
+const ACCEPTABLE_TYPES_WITHOUT_ARG: [rustc_span::Symbol; 3] = [sym::BinaryHeap, sym::HashMap, sym::HashSet];
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: Option<&Expr<'_>>) {
+ if let Some(arg) = arg {
+ if match_acceptable_type(cx, recv, &ACCEPTABLE_TYPES_WITH_ARG)
+ && let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind
+ && is_range_full(cx, arg, Some(container_path))
+ {
+ suggest(cx, expr, recv, span);
+ }
+ } else if match_acceptable_type(cx, recv, &ACCEPTABLE_TYPES_WITHOUT_ARG) {
+ suggest(cx, expr, recv, span);
+ }
+}
+
+fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, types: &[rustc_span::Symbol]) -> bool {
+ let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
+ types.iter().any(|&ty| is_type_diagnostic_item(cx, expr_ty, ty))
+ // String type is a lang item but not a diagnostic item for now so we need a separate check
+ || is_type_lang_item(cx, expr_ty, LangItem::String)
+}
+
+fn suggest(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span) {
+ if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
+ // Use `opt_item_name` while `String` is not a diagnostic item
+ && let Some(ty_name) = cx.tcx.opt_item_name(adt.did())
+ {
+ span_lint_and_sugg(
+ cx,
+ CLEAR_WITH_DRAIN,
+ span.with_hi(expr.span.hi()),
+ &format!("`drain` used to clear a `{ty_name}`"),
+ "try",
+ "clear()".to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs
index 355f53532..5e8ad0861 100644
--- a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs
@@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::paths;
-use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::{is_type_diagnostic_item, match_type};
use rustc_errors::Applicability;
use rustc_hir as hir;
@@ -33,7 +33,9 @@ pub(super) fn check(
return;
};
- let snippet = snippet_with_macro_callsite(cx, receiver.span, "..");
+ // Sometimes unnecessary ::<_> after Rc/Arc/Weak
+ let mut app = Applicability::Unspecified;
+ let snippet = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "..", &mut app).0;
span_lint_and_sugg(
cx,
@@ -42,7 +44,7 @@ pub(super) fn check(
"using `.clone()` on a ref-counted pointer",
"try this",
format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)),
- Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak
+ app,
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
index a22285058..92d21bb89 100644
--- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
@@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
+use clippy_utils::macros::{find_format_args, format_args_inputs_span, root_macro_call_first_node};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use rustc_errors::Applicability;
@@ -136,18 +136,19 @@ pub(super) fn check<'tcx>(
if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) {
return;
}
- let Some(format_args) = FormatArgsExpn::find_nested(cx, arg_root, macro_call.expn) else { return };
- let span = format_args.inputs_span();
- let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
- span_lint_and_sugg(
- cx,
- EXPECT_FUN_CALL,
- span_replace_word,
- &format!("use of `{name}` followed by a function call"),
- "try this",
- format!("unwrap_or_else({closure_args} panic!({sugg}))"),
- applicability,
- );
+ find_format_args(cx, arg_root, macro_call.expn, |format_args| {
+ let span = format_args_inputs_span(format_args);
+ let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
+ span_lint_and_sugg(
+ cx,
+ EXPECT_FUN_CALL,
+ span_replace_word,
+ &format!("use of `{name}` followed by a function call"),
+ "try this",
+ format!("unwrap_or_else({closure_args} panic!({sugg}))"),
+ applicability,
+ );
+ });
return;
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
index 3da230e12..f6772c5c6 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
@@ -1,7 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher::Range;
-use clippy_utils::is_integer_const;
-use rustc_ast::ast::RangeLimits;
+use clippy_utils::is_range_full;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::LateContext;
@@ -15,8 +13,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span
&& let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
&& let Some(ty_name) = cx.tcx.get_diagnostic_name(adt.did())
&& matches!(ty_name, sym::Vec | sym::VecDeque)
- && let Some(range) = Range::hir(arg)
- && is_full_range(cx, recv, range)
+ && let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind
+ && is_range_full(cx, arg, Some(container_path))
{
span_lint_and_sugg(
cx,
@@ -29,19 +27,3 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span
);
};
}
-
-fn is_full_range(cx: &LateContext<'_>, container: &Expr<'_>, range: Range<'_>) -> bool {
- range.start.map_or(true, |e| is_integer_const(cx, e, 0))
- && range.end.map_or(true, |e| {
- if range.limits == RangeLimits::HalfOpen
- && let ExprKind::Path(QPath::Resolved(None, container_path)) = container.kind
- && let ExprKind::MethodCall(name, self_arg, [], _) = e.kind
- && name.ident.name == sym::len
- && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
- {
- container_path.res == path.res
- } else {
- false
- }
- })
-}
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 702df4b28..64bf55ba2 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -9,6 +9,7 @@ mod chars_last_cmp;
mod chars_last_cmp_with_unwrap;
mod chars_next_cmp;
mod chars_next_cmp_with_unwrap;
+mod clear_with_drain;
mod clone_on_copy;
mod clone_on_ref_ptr;
mod cloned_instead_of_copied;
@@ -110,7 +111,7 @@ use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_
use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, return_ty};
use if_chain::if_chain;
use rustc_hir as hir;
-use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind};
+use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
@@ -340,8 +341,9 @@ declare_clippy_lint! {
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:
+ /// Checks for methods with certain name prefixes or suffixes, and which
+ /// do not adhere to standard conventions regarding how `self` is taken.
+ /// The actual rules are:
///
/// |Prefix |Postfix |`self` taken | `self` type |
/// |-------|------------|-------------------------------|--------------|
@@ -3189,6 +3191,31 @@ declare_clippy_lint! {
"single command line argument that looks like it should be multiple arguments"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `.drain(..)` for the sole purpose of clearing a container.
+ ///
+ /// ### Why is this bad?
+ /// This creates an unnecessary iterator that is dropped immediately.
+ ///
+ /// Calling `.clear()` also makes the intent clearer.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let mut v = vec![1, 2, 3];
+ /// v.drain(..);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let mut v = vec![1, 2, 3];
+ /// v.clear();
+ /// ```
+ #[clippy::version = "1.69.0"]
+ pub CLEAR_WITH_DRAIN,
+ nursery,
+ "calling `drain` in order to `clear` a container"
+}
+
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
@@ -3317,6 +3344,7 @@ impl_lint_pass!(Methods => [
SEEK_TO_START_INSTEAD_OF_REWIND,
NEEDLESS_COLLECT,
SUSPICIOUS_COMMAND_ARG_SPACE,
+ CLEAR_WITH_DRAIN,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@@ -3561,8 +3589,15 @@ impl Methods {
Some(("bytes", recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
_ => {},
},
- ("drain", [arg]) => {
- iter_with_drain::check(cx, expr, recv, span, arg);
+ ("drain", ..) => {
+ if let Node::Stmt(Stmt { hir_id: _, kind, .. }) = cx.tcx.hir().get_parent(expr.hir_id)
+ && matches!(kind, StmtKind::Semi(_))
+ && args.len() <= 1
+ {
+ clear_with_drain::check(cx, expr, recv, span, args.first());
+ } else if let [arg] = args {
+ iter_with_drain::check(cx, expr, recv, span, arg);
+ }
},
("ends_with", [arg]) => {
if let ExprKind::MethodCall(.., span) = expr.kind {
diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
index 3a23ecc50..41ceef19e 100644
--- a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
@@ -6,7 +6,6 @@ use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_lint::LateContext;
-use rustc_middle::ty::DefIdTree;
use rustc_span::symbol::sym;
use super::OPTION_MAP_OR_NONE;
diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
index 4460f38fc..7ce28ea93 100644
--- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
@@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::eager_or_lazy::switch_to_lazy_eval;
-use clippy_utils::source::{snippet, snippet_with_macro_callsite};
+use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::{contains_return, is_trait_item, last_path_segment};
use if_chain::if_chain;
@@ -9,7 +9,6 @@ use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, sym, Symbol};
-use std::borrow::Cow;
use super::OR_FUN_CALL;
@@ -111,37 +110,24 @@ pub(super) fn check<'tcx>(
if poss.contains(&name);
then {
+ let ctxt = span.ctxt();
+ let mut app = Applicability::HasPlaceholders;
let sugg = {
let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) {
(false, Some(fun_span)) => (fun_span, false),
_ => (arg.span, true),
};
- let format_span = |span: Span| {
- let not_macro_argument_snippet = snippet_with_macro_callsite(cx, span, "..");
- let snip = if not_macro_argument_snippet == "vec![]" {
- let macro_expanded_snipped = snippet(cx, snippet_span, "..");
- match macro_expanded_snipped.strip_prefix("$crate::vec::") {
- Some(stripped) => Cow::Owned(stripped.to_owned()),
- None => macro_expanded_snipped,
- }
- } else {
- not_macro_argument_snippet
- };
-
- snip.to_string()
- };
-
- let snip = format_span(snippet_span);
+ let snip = snippet_with_context(cx, snippet_span, ctxt, "..", &mut app).0;
let snip = if use_lambda {
let l_arg = if fn_has_arguments { "_" } else { "" };
format!("|{l_arg}| {snip}")
} else {
- snip
+ snip.into_owned()
};
if let Some(f) = second_arg {
- let f = format_span(f.span);
+ let f = snippet_with_context(cx, f.span, ctxt, "..", &mut app).0;
format!("{snip}, {f}")
} else {
snip
@@ -155,7 +141,7 @@ pub(super) fn check<'tcx>(
&format!("use of `{name}` followed by a function call"),
"try this",
format!("{name}_{suffix}({sugg})"),
- Applicability::HasPlaceholders,
+ app,
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs
index 5201da52b..67618f703 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs
@@ -33,10 +33,6 @@ struct SortByKeyDetection {
/// contains a and the other replaces it with b)
fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool {
match (&a_expr.kind, &b_expr.kind) {
- // Two boxes with mirrored contents
- (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => {
- mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
- },
// Two arrays with mirrored contents
(ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => {
iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
index df26b36b7..4c4c003ca 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -369,10 +369,10 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
Node::Item(item) => {
if let ItemKind::Fn(_, _, body_id) = &item.kind
&& let output_ty = return_ty(cx, item.owner_id)
- && Inherited::build(cx.tcx, item.owner_id.def_id).enter(|inherited| {
- let fn_ctxt = FnCtxt::new(inherited, cx.param_env, item.owner_id.def_id);
- fn_ctxt.can_coerce(ty, output_ty)
- }) {
+ && let inherited = Inherited::new(cx.tcx, item.owner_id.def_id)
+ && let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.owner_id.def_id)
+ && fn_ctxt.can_coerce(ty, output_ty)
+ {
if has_lifetime(output_ty) && has_lifetime(ty) {
return false;
}
diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs
index 0705029a6..3752b9a94 100644
--- a/src/tools/clippy/clippy_lints/src/misc.rs
+++ b/src/tools/clippy/clippy_lints/src/misc.rs
@@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then};
-use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::source::{snippet, snippet_opt, snippet_with_context};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
@@ -181,20 +181,17 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
if let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., name, None) = local.pat.kind;
if let Some(init) = local.init;
then {
- // use the macro callsite when the init span (but not the whole local span)
- // comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];`
- let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() {
- Sugg::hir_with_macro_callsite(cx, init, "..")
- } else {
- Sugg::hir(cx, init, "..")
- };
+ let ctxt = local.span.ctxt();
+ let mut app = Applicability::MachineApplicable;
+ let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app);
let (mutopt, initref) = if mutabl == Mutability::Mut {
("mut ", sugg_init.mut_addr())
} else {
("", sugg_init.addr())
};
let tyopt = if let Some(ty) = local.ty {
- format!(": &{mutopt}{ty}", ty=snippet(cx, ty.span, ".."))
+ let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0;
+ format!(": &{mutopt}{ty_snip}")
} else {
String::new()
};
@@ -212,7 +209,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
"let {name}{tyopt} = {initref};",
name=snippet(cx, name.span, ".."),
),
- Applicability::MachineApplicable,
+ app,
);
}
);
diff --git a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
new file mode 100644
index 000000000..2214a568d
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
@@ -0,0 +1,82 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node, PanicExpn};
+use clippy_utils::{is_in_cfg_test, is_in_test_function};
+use rustc_hir::Expr;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks assertions without a custom panic message.
+ ///
+ /// ### Why is this bad?
+ /// Without a good custom message, it'd be hard to understand what went wrong when the assertion fails.
+ /// A good custom message should be more about why the failure of the assertion is problematic
+ /// and not what is failed because the assertion already conveys that.
+ ///
+ /// ### Known problems
+ /// This lint cannot check the quality of the custom panic messages.
+ /// Hence, you can suppress this lint simply by adding placeholder messages
+ /// like "assertion failed". However, we recommend coming up with good messages
+ /// that provide useful information instead of placeholder messages that
+ /// don't provide any extra information.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # struct Service { ready: bool }
+ /// fn call(service: Service) {
+ /// assert!(service.ready);
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # struct Service { ready: bool }
+ /// fn call(service: Service) {
+ /// assert!(service.ready, "`service.poll_ready()` must be called first to ensure that service is ready to receive requests");
+ /// }
+ /// ```
+ #[clippy::version = "1.69.0"]
+ pub MISSING_ASSERT_MESSAGE,
+ restriction,
+ "checks assertions without a custom panic message"
+}
+
+declare_lint_pass!(MissingAssertMessage => [MISSING_ASSERT_MESSAGE]);
+
+impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+ let single_argument = match cx.tcx.get_diagnostic_name(macro_call.def_id) {
+ Some(sym::assert_macro | sym::debug_assert_macro) => true,
+ Some(
+ sym::assert_eq_macro | sym::assert_ne_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro,
+ ) => false,
+ _ => return,
+ };
+
+ // This lint would be very noisy in tests, so just ignore if we're in test context
+ if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) {
+ return;
+ }
+
+ let panic_expn = if single_argument {
+ let Some((_, panic_expn)) = find_assert_args(cx, expr, macro_call.expn) else { return };
+ panic_expn
+ } else {
+ let Some((_, _, panic_expn)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return };
+ panic_expn
+ };
+
+ if let PanicExpn::Empty = panic_expn {
+ span_lint_and_help(
+ cx,
+ MISSING_ASSERT_MESSAGE,
+ macro_call.span,
+ "assert without any message",
+ None,
+ "consider describing why the failing assert is problematic",
+ );
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
index 87bd007a2..f1831a304 100644
--- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
/// 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.
///
+ /// If you are marking a public function with `const`, removing it again will break API compatibility.
/// ### Example
/// ```rust
/// # struct Foo {
diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs
index 9659ca8ce..f2773cad4 100644
--- a/src/tools/clippy/clippy_lints/src/missing_doc.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs
@@ -8,12 +8,12 @@
use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_from_proc_macro;
-use hir::def_id::LocalDefId;
use if_chain::if_chain;
use rustc_ast::ast::{self, MetaItem, MetaItemKind};
use rustc_hir as hir;
+use rustc_hir::def_id::LocalDefId;
use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::ty::{DefIdTree, Visibility};
+use rustc_middle::ty::Visibility;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::source_map::Span;
@@ -21,8 +21,7 @@ use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
- /// Warns if there is missing doc for any documentable item
- /// (public or private).
+ /// Warns if there is missing doc for any private documentable item
///
/// ### Why is this bad?
/// Doc is good. *rustc* has a `MISSING_DOCS`
@@ -32,7 +31,7 @@ declare_clippy_lint! {
#[clippy::version = "pre 1.29.0"]
pub MISSING_DOCS_IN_PRIVATE_ITEMS,
restriction,
- "detects missing documentation for public and private members"
+ "detects missing documentation for private members"
}
pub struct MissingDoc {
@@ -107,11 +106,14 @@ impl MissingDoc {
if vis == Visibility::Public || vis != Visibility::Restricted(CRATE_DEF_ID.into()) {
return;
}
+ } else if def_id != CRATE_DEF_ID && cx.effective_visibilities.is_exported(def_id) {
+ return;
}
let has_doc = attrs
.iter()
.any(|a| a.doc_str().is_some() || Self::has_include(a.meta()));
+
if !has_doc {
span_lint(
cx,
diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
index 63c575fca..5418616de 100644
--- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
+++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
@@ -11,6 +11,7 @@ use rustc_ast::Mutability;
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_lint_pass, declare_tool_lint};
use rustc_span::Span;
@@ -120,33 +121,15 @@ fn collect_unsafe_exprs<'tcx>(
unsafe_ops.push(("raw pointer dereference occurs here", expr.span));
},
- ExprKind::Call(path_expr, _) => match path_expr.kind {
- ExprKind::Path(QPath::Resolved(
- _,
- hir::Path {
- res: Res::Def(kind, def_id),
- ..
- },
- )) if kind.is_fn_like() => {
- let sig = cx.tcx.fn_sig(*def_id);
- if sig.0.unsafety() == Unsafety::Unsafe {
- unsafe_ops.push(("unsafe function call occurs here", expr.span));
- }
- },
-
- ExprKind::Path(QPath::TypeRelative(..)) => {
- if let Some(sig) = cx
- .typeck_results()
- .type_dependent_def_id(path_expr.hir_id)
- .map(|def_id| cx.tcx.fn_sig(def_id))
- {
- if sig.0.unsafety() == Unsafety::Unsafe {
- unsafe_ops.push(("unsafe function call occurs here", expr.span));
- }
- }
- },
-
- _ => {},
+ ExprKind::Call(path_expr, _) => {
+ let sig = match *cx.typeck_results().expr_ty(path_expr).kind() {
+ ty::FnDef(id, _) => cx.tcx.fn_sig(id).skip_binder(),
+ ty::FnPtr(sig) => sig,
+ _ => return Continue(Descend::Yes),
+ };
+ if sig.unsafety() == Unsafety::Unsafe {
+ unsafe_ops.push(("unsafe function call occurs here", expr.span));
+ }
},
ExprKind::MethodCall(..) => {
diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs
index 8aa814b74..309f67521 100644
--- a/src/tools/clippy/clippy_lints/src/mut_key.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_key.rs
@@ -1,10 +1,11 @@
use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::is_interior_mut_ty;
use clippy_utils::{def_path_def_ids, trait_ref_of_method};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::TypeVisitableExt;
-use rustc_middle::ty::{Adt, Array, Ref, Slice, Tuple, Ty};
+use rustc_middle::query::Key;
+use rustc_middle::ty::{Adt, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
@@ -153,53 +154,18 @@ impl MutableKeyType {
let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet]
.iter()
.any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did()));
- if is_keyed_type && self.is_interior_mutable_type(cx, substs.type_at(0)) {
- span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
+ if !is_keyed_type {
+ return;
}
- }
- }
- /// Determines if a type contains interior mutability which would affect its implementation of
- /// [`Hash`] or [`Ord`].
- fn is_interior_mutable_type<'tcx>(&self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
- match *ty.kind() {
- Ref(_, inner_ty, mutbl) => mutbl == hir::Mutability::Mut || self.is_interior_mutable_type(cx, inner_ty),
- Slice(inner_ty) => self.is_interior_mutable_type(cx, inner_ty),
- Array(inner_ty, size) => {
- size.try_eval_target_usize(cx.tcx, cx.param_env)
- .map_or(true, |u| u != 0)
- && self.is_interior_mutable_type(cx, inner_ty)
- },
- Tuple(fields) => fields.iter().any(|ty| self.is_interior_mutable_type(cx, ty)),
- Adt(def, substs) => {
- // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to
- // that of their type parameters. Note: we don't include `HashSet` and `HashMap`
- // because they have no impl for `Hash` or `Ord`.
- let def_id = def.did();
- let is_std_collection = [
- sym::Option,
- sym::Result,
- sym::LinkedList,
- sym::Vec,
- sym::VecDeque,
- sym::BTreeMap,
- sym::BTreeSet,
- sym::Rc,
- sym::Arc,
- ]
- .iter()
- .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def_id));
- let is_box = Some(def_id) == cx.tcx.lang_items().owned_box();
- if is_std_collection || is_box || self.ignore_mut_def_ids.contains(&def_id) {
- // The type is mutable if any of its type parameters are
- substs.types().any(|ty| self.is_interior_mutable_type(cx, ty))
- } else {
- !ty.has_escaping_bound_vars()
- && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
- && !ty.is_freeze(cx.tcx, cx.param_env)
- }
- },
- _ => false,
+ let subst_ty = substs.type_at(0);
+ // Determines if a type contains interior mutability which would affect its implementation of
+ // [`Hash`] or [`Ord`].
+ if is_interior_mut_ty(cx, subst_ty)
+ && !matches!(subst_ty.ty_adt_id(), Some(adt_id) if self.ignore_mut_def_ids.contains(&adt_id))
+ {
+ span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
+ }
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs
index a4eec95b3..c87059bf6 100644
--- a/src/tools/clippy/clippy_lints/src/needless_bool.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs
@@ -340,18 +340,11 @@ fn suggest_bool_comparison<'a, 'tcx>(
cx: &LateContext<'tcx>,
e: &'tcx Expr<'_>,
expr: &Expr<'_>,
- mut applicability: Applicability,
+ mut app: Applicability,
message: &str,
conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
) {
- let hint = if expr.span.from_expansion() {
- if applicability != Applicability::Unspecified {
- applicability = Applicability::MaybeIncorrect;
- }
- Sugg::hir_with_macro_callsite(cx, expr, "..")
- } else {
- Sugg::hir_with_applicability(cx, expr, "..", &mut applicability)
- };
+ let hint = Sugg::hir_with_context(cx, expr, e.span.ctxt(), "..", &mut app);
span_lint_and_sugg(
cx,
BOOL_COMPARISON,
@@ -359,7 +352,7 @@ fn suggest_bool_comparison<'a, 'tcx>(
message,
"try simplifying it as shown",
conv_hint(hint).to_string(),
- applicability,
+ app,
);
}
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index 1ab81aee7..0bb1775aa 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -122,11 +122,11 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
let sized_trait = need!(cx.tcx.lang_items().sized_trait());
- let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds().iter())
+ let preds = traits::elaborate(cx.tcx, cx.param_env.caller_bounds().iter())
.filter(|p| !p.is_global())
- .filter_map(|obligation| {
+ .filter_map(|pred| {
// Note that we do not want to deal with qualified predicates here.
- match obligation.predicate.kind().no_bound_vars() {
+ match pred.kind().no_bound_vars() {
Some(ty::PredicateKind::Clause(ty::Clause::Trait(pred))) if pred.def_id() != sized_trait => {
Some(pred)
},
diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
index 97c8cfbd3..e2a7ba02a 100644
--- a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
@@ -6,7 +6,6 @@ use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{AsyncGeneratorKind, Block, Body, Expr, ExprKind, GeneratorKind, LangItem, MatchSource, QPath};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::DefIdTree;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
index fb9a4abd0..ed3e2c6e7 100644
--- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs
+++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
@@ -1,6 +1,6 @@
use clippy_utils::consts::{self, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::has_enclosing_paren;
use if_chain::if_chain;
use rustc_ast::util::parser::PREC_PREFIX;
@@ -60,8 +60,8 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
then {
let mut applicability = Applicability::MachineApplicable;
- let snip = snippet_with_applicability(cx, exp.span, "..", &mut applicability);
- let suggestion = if exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) {
+ let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability);
+ let suggestion = if !from_macro && exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) {
format!("-({snip})")
} else {
format!("-{snip}")
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index 79c1ae486..e3712190e 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -127,8 +127,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
| ExprKind::Type(inner, _)
| ExprKind::Unary(_, inner)
| ExprKind::Field(inner, _)
- | ExprKind::AddrOf(_, _, inner)
- | ExprKind::Box(inner) => has_no_effect(cx, inner),
+ | ExprKind::AddrOf(_, _, inner) => has_no_effect(cx, inner),
ExprKind::Struct(_, fields, ref base) => {
!has_drop(cx, cx.typeck_results().expr_ty(expr))
&& fields.iter().all(|field| has_no_effect(cx, field.expr))
@@ -234,8 +233,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
| ExprKind::Type(inner, _)
| ExprKind::Unary(_, inner)
| ExprKind::Field(inner, _)
- | ExprKind::AddrOf(_, _, inner)
- | ExprKind::Box(inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
+ | ExprKind::AddrOf(_, _, inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
ExprKind::Struct(_, fields, ref base) => {
if has_drop(cx, cx.typeck_results().expr_ty(expr)) {
None
diff --git a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs
index bc64ccb29..8fd9ae351 100644
--- a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs
+++ b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs
@@ -1,9 +1,10 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{BytePos, Pos};
use rustc_target::spec::abi::Abi;
declare_clippy_lint! {
@@ -38,25 +39,28 @@ impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let ItemKind::Fn(fn_sig, _, _) = &item.kind {
let attrs = cx.tcx.hir().attrs(item.hir_id());
- let mut applicability = Applicability::MachineApplicable;
- let snippet = snippet_with_applicability(cx, fn_sig.span, "..", &mut applicability);
+ let mut app = Applicability::MaybeIncorrect;
+ let snippet = snippet_with_applicability(cx, fn_sig.span, "..", &mut app);
for attr in attrs {
if let Some(ident) = attr.ident()
&& ident.name == rustc_span::sym::no_mangle
&& fn_sig.header.abi == Abi::Rust
- && !snippet.contains("extern") {
+ && let Some((fn_attrs, _)) = snippet.split_once("fn")
+ && !fn_attrs.contains("extern")
+ {
+ let sugg_span = fn_sig.span
+ .with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len()))
+ .shrink_to_lo();
- let suggestion = snippet.split_once("fn")
- .map_or(String::new(), |(first, second)| format!(r#"{first}extern "C" fn{second}"#));
-
- span_lint_and_sugg(
+ span_lint_and_then(
cx,
NO_MANGLE_WITH_RUST_ABI,
fn_sig.span,
- "attribute #[no_mangle] set on a Rust ABI function",
- "try",
- suggestion,
- applicability
+ "`#[no_mangle]` set on a function with the default (`Rust`) ABI",
+ |diag| {
+ diag.span_suggestion(sugg_span, "set an ABI", "extern \"C\" ", app)
+ .span_suggestion(sugg_span, "or explicitly set the default", "extern \"Rust\" ", app);
+ },
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
index 2ecb04874..e1de494eb 100644
--- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
+++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
@@ -53,6 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
|| is_type_diagnostic_item(cx, obj_ty, sym::DirBuilder)))
|| (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS));
if let ExprKind::Lit(_) = param.kind;
+ if param.span.ctxt() == expr.span.ctxt();
then {
let Some(snip) = snippet_opt(cx, param.span) else {
@@ -71,6 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id();
if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE);
if let ExprKind::Lit(_) = param.kind;
+ if param.span.ctxt() == expr.span.ctxt();
if let Some(snip) = snippet_opt(cx, param.span);
if !snip.starts_with("0o");
then {
diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
index 87a8a2ed1..e57137356 100644
--- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
@@ -143,6 +143,10 @@ impl ArithmeticSideEffects {
return;
}
let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) {
+ if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op.node {
+ // At least for integers, shifts are already handled by the CTFE
+ return;
+ }
let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs);
let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs);
match (
@@ -150,11 +154,22 @@ impl ArithmeticSideEffects {
Self::literal_integer(cx, actual_rhs),
) {
(None, None) => false,
- (None, Some(n)) | (Some(n), None) => match (&op.node, n) {
- (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false,
+ (None, Some(n)) => match (&op.node, n) {
+ // Division and module are always valid if applied to non-zero integers
+ (hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => true,
+ // Adding or subtracting zeros is always a no-op
+ (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
+ // Multiplication by 1 or 0 will never overflow
+ | (hir::BinOpKind::Mul, 0 | 1)
+ => true,
+ _ => false,
+ },
+ (Some(n), None) => match (&op.node, n) {
+ // Adding or subtracting zeros is always a no-op
(hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
- | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _)
- | (hir::BinOpKind::Mul, 0 | 1) => true,
+ // Multiplication by 1 or 0 will never overflow
+ | (hir::BinOpKind::Mul, 0 | 1)
+ => true,
_ => false,
},
(Some(_), Some(_)) => {
diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
index c5ea09590..bbbcda069 100644
--- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
@@ -12,6 +12,7 @@ use rustc_hir::{
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::SyntaxContext;
declare_clippy_lint! {
/// ### What it does
@@ -95,10 +96,10 @@ struct OptionOccurrence {
none_expr: String,
}
-fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String {
+fn format_option_in_sugg(cond_sugg: Sugg<'_>, as_ref: bool, as_mut: bool) -> String {
format!(
"{}{}",
- Sugg::hir_with_macro_callsite(cx, cond_expr, "..").maybe_par(),
+ cond_sugg.maybe_par(),
if as_mut {
".as_mut()"
} else if as_ref {
@@ -111,6 +112,7 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo
fn try_get_option_occurrence<'tcx>(
cx: &LateContext<'tcx>,
+ ctxt: SyntaxContext,
pat: &Pat<'tcx>,
expr: &Expr<'_>,
if_then: &'tcx Expr<'_>,
@@ -160,11 +162,23 @@ fn try_get_option_occurrence<'tcx>(
}
}
+ let mut app = Applicability::Unspecified;
return Some(OptionOccurrence {
- option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
+ option: format_option_in_sugg(
+ Sugg::hir_with_context(cx, cond_expr, ctxt, "..", &mut app),
+ as_ref,
+ as_mut,
+ ),
method_sugg: method_sugg.to_string(),
- some_expr: format!("|{capture_mut}{capture_name}| {}", Sugg::hir_with_macro_callsite(cx, some_body, "..")),
- none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")),
+ some_expr: format!(
+ "|{capture_mut}{capture_name}| {}",
+ Sugg::hir_with_context(cx, some_body, ctxt, "..", &mut app),
+ ),
+ none_expr: format!(
+ "{}{}",
+ if method_sugg == "map_or" { "" } else { "|| " },
+ Sugg::hir_with_context(cx, none_body, ctxt, "..", &mut app),
+ ),
});
}
}
@@ -194,7 +208,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
}) = higher::IfLet::hir(cx, expr)
{
if !is_else_clause(cx.tcx, expr) {
- return try_get_option_occurrence(cx, let_pat, let_expr, if_then, if_else);
+ return try_get_option_occurrence(cx, expr.span.ctxt(), let_pat, let_expr, if_then, if_else);
}
}
None
@@ -203,7 +217,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurrence> {
if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
if let Some((let_pat, if_then, if_else)) = try_convert_match(cx, arms) {
- return try_get_option_occurrence(cx, let_pat, ex, if_then, if_else);
+ return try_get_option_occurrence(cx, expr.span.ctxt(), let_pat, ex, if_then, if_else);
}
}
None
diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
index 5aa3c6f2f..a8c4823fe 100644
--- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
@@ -36,7 +36,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if_chain! {
if let ItemKind::Impl(Impl { of_trait: Some(ref trait_ref), items: impl_items, .. }) = item.kind;
- if !cx.tcx.has_attr(item.owner_id.to_def_id(), sym::automatically_derived);
+ if !cx.tcx.has_attr(item.owner_id, sym::automatically_derived);
if let Some(eq_trait) = cx.tcx.lang_items().eq_trait();
if trait_ref.path.res.def_id() == eq_trait;
then {
diff --git a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs
index e7095ec19..664d44d65 100644
--- a/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs
+++ b/src/tools/clippy/clippy_lints/src/permissions_set_readonly_false.rs
@@ -21,7 +21,7 @@ declare_clippy_lint! {
/// let mut permissions = metadata.permissions();
/// permissions.set_readonly(false);
/// ```
- #[clippy::version = "1.66.0"]
+ #[clippy::version = "1.68.0"]
pub PERMISSIONS_SET_READONLY_FALSE,
suspicious,
"Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`"
diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
new file mode 100644
index 000000000..a0f831764
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
@@ -0,0 +1,108 @@
+use std::ops::ControlFlow;
+
+use clippy_utils::{
+ diagnostics::span_lint_and_sugg,
+ peel_blocks,
+ source::{snippet, walk_span_to_context},
+ visitors::for_each_expr,
+};
+use rustc_errors::Applicability;
+use rustc_hir::{AsyncGeneratorKind, Closure, Expr, ExprKind, GeneratorKind, MatchSource};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::{lint::in_external_macro, ty::UpvarCapture};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `async` block that only returns `await` on a future.
+ ///
+ /// ### Why is this bad?
+ /// It is simpler and more efficient to use the future directly.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let f = async {
+ /// 1 + 2
+ /// };
+ /// let fut = async {
+ /// f.await
+ /// };
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let f = async {
+ /// 1 + 2
+ /// };
+ /// let fut = f;
+ /// ```
+ #[clippy::version = "1.69.0"]
+ pub REDUNDANT_ASYNC_BLOCK,
+ complexity,
+ "`async { future.await }` can be replaced by `future`"
+}
+declare_lint_pass!(RedundantAsyncBlock => [REDUNDANT_ASYNC_BLOCK]);
+
+impl<'tcx> LateLintPass<'tcx> for RedundantAsyncBlock {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ let span = expr.span;
+ if !in_external_macro(cx.tcx.sess, span) &&
+ let Some(body_expr) = desugar_async_block(cx, expr) &&
+ let Some(expr) = desugar_await(peel_blocks(body_expr)) &&
+ // The await prefix must not come from a macro as its content could change in the future.
+ expr.span.ctxt() == body_expr.span.ctxt() &&
+ // An async block does not have immediate side-effects from a `.await` point-of-view.
+ (!expr.can_have_side_effects() || desugar_async_block(cx, expr).is_some()) &&
+ let Some(shortened_span) = walk_span_to_context(expr.span, span.ctxt())
+ {
+ span_lint_and_sugg(
+ cx,
+ REDUNDANT_ASYNC_BLOCK,
+ span,
+ "this async expression only awaits a single future",
+ "you can reduce it to",
+ snippet(cx, shortened_span, "..").into_owned(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+}
+
+/// If `expr` is a desugared `async` block, return the original expression if it does not capture
+/// any variable by ref.
+fn desugar_async_block<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+ if let ExprKind::Closure(Closure { body, def_id, .. }) = expr.kind &&
+ let body = cx.tcx.hir().body(*body) &&
+ matches!(body.generator_kind, Some(GeneratorKind::Async(AsyncGeneratorKind::Block)))
+ {
+ cx
+ .typeck_results()
+ .closure_min_captures
+ .get(def_id)
+ .map_or(true, |m| {
+ m.values().all(|places| {
+ places
+ .iter()
+ .all(|place| matches!(place.info.capture_kind, UpvarCapture::ByValue))
+ })
+ })
+ .then_some(body.value)
+ } else {
+ None
+ }
+}
+
+/// If `expr` is a desugared `.await`, return the original expression if it does not come from a
+/// macro expansion.
+fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+ if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind &&
+ let ExprKind::Call(_, [into_future_arg]) = match_value.kind &&
+ let ctxt = expr.span.ctxt() &&
+ for_each_expr(into_future_arg, |e|
+ walk_span_to_context(e.span, ctxt)
+ .map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))).is_none()
+ {
+ Some(into_future_arg)
+ } else {
+ None
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
index 44bf824aa..038dfe8e4 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
@@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet;
-use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
+use rustc_ast::ast::{ConstItem, Item, ItemKind, StaticItem, Ty, TyKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -100,13 +100,13 @@ impl EarlyLintPass for RedundantStaticLifetimes {
}
if !item.span.from_expansion() {
- if let ItemKind::Const(_, ref var_type, _) = item.kind {
+ if let ItemKind::Const(box ConstItem { ty: 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 {
+ if let ItemKind::Static(box StaticItem { ty: ref var_type, .. }) = item.kind {
Self::visit_type(var_type, cx, "statics have by default a `'static` lifetime");
}
}
diff --git a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs
index 448a32b77..c984a8286 100644
--- a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs
@@ -3,7 +3,7 @@ use clippy_utils::last_path_segment;
use clippy_utils::source::snippet;
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::{GenericArg, Mutability, Ty, TyKind};
+use rustc_hir::{GenericArg, GenericArgsParentheses, Mutability, Ty, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;
@@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef {
if cx.tcx.is_diagnostic_item(sym::Option, def_id);
if let Some(params) = last_path_segment(qpath).args ;
- if !params.parenthesized;
+ if params.parenthesized == GenericArgsParentheses::No;
if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg {
GenericArg::Type(inner_ty) => Some(inner_ty),
_ => None,
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index f0d7dd23a..df126d761 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -9,7 +9,7 @@ use rustc_hir::intravisit::FnKind;
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem, MatchSource, PatKind, QPath, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
@@ -175,7 +175,7 @@ impl<'tcx> LateLintPass<'tcx> for Return {
} else {
RetReplacement::Empty
};
- check_final_expr(cx, body.value, vec![], replacement);
+ check_final_expr(cx, body.value, vec![], replacement, None);
},
FnKind::ItemFn(..) | FnKind::Method(..) => {
check_block_return(cx, &body.value.kind, sp, vec![]);
@@ -188,11 +188,11 @@ impl<'tcx> LateLintPass<'tcx> for Return {
fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec<Span>) {
if let ExprKind::Block(block, _) = expr_kind {
if let Some(block_expr) = block.expr {
- check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty);
+ check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty, None);
} else if let Some(stmt) = block.stmts.iter().last() {
match stmt.kind {
StmtKind::Expr(expr) => {
- check_final_expr(cx, expr, semi_spans, RetReplacement::Empty);
+ check_final_expr(cx, expr, semi_spans, RetReplacement::Empty, None);
},
StmtKind::Semi(semi_expr) => {
// Remove ending semicolons and any whitespace ' ' in between.
@@ -202,7 +202,7 @@ fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>,
span_find_starting_semi(cx.sess().source_map(), semi_span.with_hi(sp.hi()));
semi_spans.push(semi_span_to_remove);
}
- check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty);
+ check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty, None);
},
_ => (),
}
@@ -216,6 +216,7 @@ fn check_final_expr<'tcx>(
semi_spans: Vec<Span>, /* containing all the places where we would need to remove semicolons if finding an
* needless return */
replacement: RetReplacement<'tcx>,
+ match_ty_opt: Option<Ty<'_>>,
) {
let peeled_drop_expr = expr.peel_drop_temps();
match &peeled_drop_expr.kind {
@@ -244,7 +245,22 @@ fn check_final_expr<'tcx>(
RetReplacement::Expr(snippet, applicability)
}
} else {
- replacement
+ match match_ty_opt {
+ Some(match_ty) => {
+ match match_ty.kind() {
+ // If the code got till here with
+ // tuple not getting detected before it,
+ // then we are sure it's going to be Unit
+ // type
+ ty::Tuple(_) => RetReplacement::Unit,
+ // We don't want to anything in this case
+ // cause we can't predict what the user would
+ // want here
+ _ => return,
+ }
+ },
+ None => replacement,
+ }
};
if !cx.tcx.hir().attrs(expr.hir_id).is_empty() {
@@ -268,8 +284,9 @@ fn check_final_expr<'tcx>(
// note, if without else is going to be a type checking error anyways
// (except for unit type functions) so we don't match it
ExprKind::Match(_, arms, MatchSource::Normal) => {
+ let match_ty = cx.typeck_results().expr_ty(peeled_drop_expr);
for arm in arms.iter() {
- check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit);
+ check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit, Some(match_ty));
}
},
// if it's a whole block, check it
@@ -293,6 +310,7 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec<Span>,
if ret_span.from_expansion() {
return;
}
+
let applicability = replacement.applicability().unwrap_or(Applicability::MachineApplicable);
let return_replacement = replacement.to_string();
let sugg_help = replacement.sugg_help();
diff --git a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs
index 66638eed9..355f907e2 100644
--- a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs
+++ b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs
@@ -1,7 +1,6 @@
use crate::rustc_lint::LintContext;
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::sugg;
+use clippy_utils::source::snippet_with_context;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Block, ExprKind};
@@ -44,7 +43,8 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned {
if let Some(expr) = block.expr;
let t_expr = cx.typeck_results().expr_ty(expr);
if t_expr.is_unit();
- if let snippet = snippet_with_macro_callsite(cx, expr.span, "}");
+ let mut app = Applicability::MaybeIncorrect;
+ if let snippet = snippet_with_context(cx, expr.span, block.span.ctxt(), "}", &mut app).0;
if !snippet.ends_with('}') && !snippet.ends_with(';');
if cx.sess().source_map().is_multiline(block.span);
then {
@@ -52,17 +52,14 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned {
if let ExprKind::DropTemps(..) = &expr.kind {
return;
}
-
- let sugg = sugg::Sugg::hir_with_macro_callsite(cx, expr, "..");
- let suggestion = format!("{sugg};");
span_lint_and_sugg(
cx,
SEMICOLON_IF_NOTHING_RETURNED,
expr.span.source_callsite(),
"consider adding a `;` to the last statement for consistent formatting",
"add a `;` here",
- suggestion,
- Applicability::MaybeIncorrect,
+ format!("{snippet};"),
+ app,
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs
index 87f966ced..ae7d19624 100644
--- a/src/tools/clippy/clippy_lints/src/shadow.rs
+++ b/src/tools/clippy/clippy_lints/src/shadow.rs
@@ -213,8 +213,7 @@ fn is_self_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, mut expr: &Expr<'_>, hir_
}
loop {
expr = match expr.kind {
- ExprKind::Box(e)
- | ExprKind::AddrOf(_, _, e)
+ ExprKind::AddrOf(_, _, e)
| ExprKind::Block(
&Block {
stmts: [],
diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
index c3e99aa00..869358fb1 100644
--- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
+++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
@@ -404,7 +404,6 @@ impl<'cx, 'sdt, 'tcx> Visitor<'tcx> for SigDropFinder<'cx, 'sdt, 'tcx> {
| hir::ExprKind::Assign(..)
| hir::ExprKind::AssignOp(..)
| hir::ExprKind::Binary(..)
- | hir::ExprKind::Box(..)
| hir::ExprKind::Call(..)
| hir::ExprKind::Field(..)
| hir::ExprKind::If(..)
diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
index d46f6a635..5743dd21c 100644
--- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
@@ -1,6 +1,7 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
use rustc_ast::node_id::{NodeId, NodeMap};
-use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind};
+use rustc_ast::visit::{walk_expr, Visitor};
+use rustc_ast::{ptr::P, Crate, Expr, ExprKind, Item, ItemKind, MacroDef, ModKind, Ty, TyKind, UseTreeKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -55,7 +56,7 @@ impl EarlyLintPass for SingleComponentPathImports {
return;
}
- self.check_mod(cx, &krate.items);
+ self.check_mod(&krate.items);
}
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
@@ -84,8 +85,43 @@ impl EarlyLintPass for SingleComponentPathImports {
}
}
+#[derive(Default)]
+struct ImportUsageVisitor {
+ // keep track of imports reused with `self` keyword, such as `self::std` in the example below.
+ // Removing the `use std;` would make this a compile error (#10549)
+ // ```
+ // use std;
+ //
+ // fn main() {
+ // let _ = self::std::io::stdout();
+ // }
+ // ```
+ imports_referenced_with_self: Vec<Symbol>,
+}
+
+impl<'tcx> Visitor<'tcx> for ImportUsageVisitor {
+ fn visit_expr(&mut self, expr: &Expr) {
+ if let ExprKind::Path(_, path) = &expr.kind
+ && path.segments.len() > 1
+ && path.segments[0].ident.name == kw::SelfLower
+ {
+ self.imports_referenced_with_self.push(path.segments[1].ident.name);
+ }
+ walk_expr(self, expr);
+ }
+
+ fn visit_ty(&mut self, ty: &Ty) {
+ if let TyKind::Path(_, path) = &ty.kind
+ && path.segments.len() > 1
+ && path.segments[0].ident.name == kw::SelfLower
+ {
+ self.imports_referenced_with_self.push(path.segments[1].ident.name);
+ }
+ }
+}
+
impl SingleComponentPathImports {
- fn check_mod(&mut self, cx: &EarlyContext<'_>, items: &[P<Item>]) {
+ fn check_mod(&mut self, items: &[P<Item>]) {
// keep track of imports reused with `self` keyword, such as `self::crypto_hash` in the example
// below. Removing the `use crypto_hash;` would make this a compile error
// ```
@@ -108,18 +144,16 @@ impl SingleComponentPathImports {
// ```
let mut macros = Vec::new();
+ let mut import_usage_visitor = ImportUsageVisitor::default();
for item in items {
- self.track_uses(
- cx,
- item,
- &mut imports_reused_with_self,
- &mut single_use_usages,
- &mut macros,
- );
+ self.track_uses(item, &mut imports_reused_with_self, &mut single_use_usages, &mut macros);
+ import_usage_visitor.visit_item(item);
}
for usage in single_use_usages {
- if !imports_reused_with_self.contains(&usage.name) {
+ if !imports_reused_with_self.contains(&usage.name)
+ && !import_usage_visitor.imports_referenced_with_self.contains(&usage.name)
+ {
self.found.entry(usage.item_id).or_default().push(usage);
}
}
@@ -127,7 +161,6 @@ impl SingleComponentPathImports {
fn track_uses(
&mut self,
- cx: &EarlyContext<'_>,
item: &Item,
imports_reused_with_self: &mut Vec<Symbol>,
single_use_usages: &mut Vec<SingleUse>,
@@ -139,7 +172,7 @@ impl SingleComponentPathImports {
match &item.kind {
ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => {
- self.check_mod(cx, items);
+ self.check_mod(items);
},
ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => {
macros.push(item.ident.name);
diff --git a/src/tools/clippy/clippy_lints/src/size_of_ref.rs b/src/tools/clippy/clippy_lints/src/size_of_ref.rs
index 3fcdb4288..8abec06c6 100644
--- a/src/tools/clippy/clippy_lints/src/size_of_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/size_of_ref.rs
@@ -45,7 +45,7 @@ declare_clippy_lint! {
/// }
/// }
/// ```
- #[clippy::version = "1.67.0"]
+ #[clippy::version = "1.68.0"]
pub SIZE_OF_REF,
suspicious,
"Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended"
diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
index d6b336bef..a13bc7a51 100644
--- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
+++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
@@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_help;
use rustc_hir::def_id::DefId;
use rustc_hir::{def::Res, HirId, Path, PathSegment};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::DefIdTree;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{sym, symbol::kw, Span};
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs b/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs
new file mode 100644
index 000000000..e5746ca99
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs
@@ -0,0 +1,94 @@
+use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then};
+use if_chain::if_chain;
+use rustc_ast::{token::CommentKind, AttrKind, AttrStyle, Attribute, Item};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Detects the use of outer doc comments (`///`, `/**`) followed by a bang (`!`): `///!`
+ ///
+ /// ### Why is this bad?
+ /// Triple-slash comments (known as "outer doc comments") apply to items that follow it.
+ /// An outer doc comment followed by a bang (i.e. `///!`) has no specific meaning.
+ ///
+ /// The user most likely meant to write an inner doc comment (`//!`, `/*!`), which
+ /// applies to the parent item (i.e. the item that the comment is contained in,
+ /// usually a module or crate).
+ ///
+ /// ### Known problems
+ /// Inner doc comments can only appear before items, so there are certain cases where the suggestion
+ /// made by this lint is not valid code. For example:
+ /// ```rs
+ /// fn foo() {}
+ /// ///!
+ /// fn bar() {}
+ /// ```
+ /// This lint detects the doc comment and suggests changing it to `//!`, but an inner doc comment
+ /// is not valid at that position.
+ ///
+ /// ### Example
+ /// In this example, the doc comment is attached to the *function*, rather than the *module*.
+ /// ```rust
+ /// pub mod util {
+ /// ///! This module contains utility functions.
+ ///
+ /// pub fn dummy() {}
+ /// }
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// pub mod util {
+ /// //! This module contains utility functions.
+ ///
+ /// pub fn dummy() {}
+ /// }
+ /// ```
+ #[clippy::version = "1.70.0"]
+ pub SUSPICIOUS_DOC_COMMENTS,
+ suspicious,
+ "suspicious usage of (outer) doc comments"
+}
+declare_lint_pass!(SuspiciousDocComments => [SUSPICIOUS_DOC_COMMENTS]);
+
+const WARNING: &str = "this is an outer doc comment and does not apply to the parent module or crate";
+const HELP: &str = "use an inner doc comment to document the parent module or crate";
+
+impl EarlyLintPass for SuspiciousDocComments {
+ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+ let replacements = collect_doc_comment_replacements(&item.attrs);
+
+ if let Some(((lo_span, _), (hi_span, _))) = replacements.first().zip(replacements.last()) {
+ let span = lo_span.to(*hi_span);
+
+ span_lint_and_then(cx, SUSPICIOUS_DOC_COMMENTS, span, WARNING, |diag| {
+ multispan_sugg_with_applicability(diag, HELP, Applicability::MaybeIncorrect, replacements);
+ });
+ }
+ }
+}
+
+fn collect_doc_comment_replacements(attrs: &[Attribute]) -> Vec<(Span, String)> {
+ attrs
+ .iter()
+ .filter_map(|attr| {
+ if_chain! {
+ if let AttrKind::DocComment(com_kind, sym) = attr.kind;
+ if let AttrStyle::Outer = attr.style;
+ if let Some(com) = sym.as_str().strip_prefix('!');
+ then {
+ let sugg = match com_kind {
+ CommentKind::Line => format!("//!{com}"),
+ CommentKind::Block => format!("/*!{com}*/")
+ };
+ Some((attr.span, sugg))
+ } else {
+ None
+ }
+ }
+ })
+ .collect()
+}
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
index e111c7d22..fab8e9c2e 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
@@ -578,7 +578,7 @@ fn ident_difference_expr_with_base_location(
| (Assign(_, _, _), Assign(_, _, _))
| (TryBlock(_), TryBlock(_))
| (Await(_), Await(_))
- | (Async(_, _, _), Async(_, _, _))
+ | (Async(_, _), Async(_, _))
| (Block(_, _), Block(_, _))
| (Closure(_), Closure(_))
| (Match(_, _), Match(_, _))
@@ -596,8 +596,7 @@ fn ident_difference_expr_with_base_location(
| (MethodCall(_), MethodCall(_))
| (Call(_, _), Call(_, _))
| (ConstBlock(_), ConstBlock(_))
- | (Array(_), Array(_))
- | (Box(_), Box(_)) => {
+ | (Array(_), Array(_)) => {
// keep going
},
_ => {
diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs
index 0f062cecf..f7eef03d1 100644
--- a/src/tools/clippy/clippy_lints/src/swap.rs
+++ b/src/tools/clippy/clippy_lints/src/swap.rs
@@ -1,15 +1,17 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
-use rustc_lint::{LateContext, LateLintPass};
+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::Spanned;
+use rustc_span::SyntaxContext;
use rustc_span::{sym, symbol::Ident, Span};
declare_clippy_lint! {
@@ -80,43 +82,45 @@ impl<'tcx> LateLintPass<'tcx> for Swap {
}
fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, span: Span, is_xor_based: bool) {
+ let ctxt = span.ctxt();
let mut applicability = Applicability::MachineApplicable;
if !can_mut_borrow_both(cx, e1, e2) {
- if let ExprKind::Index(lhs1, idx1) = e1.kind {
- if let ExprKind::Index(lhs2, idx2) = e2.kind {
- if eq_expr_value(cx, lhs1, lhs2) {
- let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
+ if let ExprKind::Index(lhs1, idx1) = e1.kind
+ && let ExprKind::Index(lhs2, idx2) = e2.kind
+ && eq_expr_value(cx, lhs1, lhs2)
+ && e1.span.ctxt() == ctxt
+ && e2.span.ctxt() == ctxt
+ {
+ let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
- if matches!(ty.kind(), ty::Slice(_))
- || matches!(ty.kind(), ty::Array(_, _))
- || is_type_diagnostic_item(cx, ty, sym::Vec)
- || is_type_diagnostic_item(cx, ty, sym::VecDeque)
- {
- let slice = Sugg::hir_with_applicability(cx, lhs1, "<slice>", &mut applicability);
- span_lint_and_sugg(
- cx,
- MANUAL_SWAP,
- span,
- &format!("this looks like you are swapping elements of `{slice}` manually"),
- "try",
- format!(
- "{}.swap({}, {})",
- slice.maybe_par(),
- snippet_with_applicability(cx, idx1.span, "..", &mut applicability),
- snippet_with_applicability(cx, idx2.span, "..", &mut applicability),
- ),
- applicability,
- );
- }
- }
+ if matches!(ty.kind(), ty::Slice(_))
+ || matches!(ty.kind(), ty::Array(_, _))
+ || is_type_diagnostic_item(cx, ty, sym::Vec)
+ || is_type_diagnostic_item(cx, ty, sym::VecDeque)
+ {
+ let slice = Sugg::hir_with_applicability(cx, lhs1, "<slice>", &mut applicability);
+ span_lint_and_sugg(
+ cx,
+ MANUAL_SWAP,
+ span,
+ &format!("this looks like you are swapping elements of `{slice}` manually"),
+ "try",
+ format!(
+ "{}.swap({}, {});",
+ slice.maybe_par(),
+ snippet_with_context(cx, idx1.span, ctxt, "..", &mut applicability).0,
+ snippet_with_context(cx, idx2.span, ctxt, "..", &mut applicability).0,
+ ),
+ applicability,
+ );
}
}
return;
}
- let first = Sugg::hir_with_applicability(cx, e1, "..", &mut applicability);
- let second = Sugg::hir_with_applicability(cx, e2, "..", &mut applicability);
+ let first = Sugg::hir_with_context(cx, e1, ctxt, "..", &mut applicability);
+ let second = Sugg::hir_with_context(cx, e2, ctxt, "..", &mut applicability);
let Some(sugg) = std_or_core(cx) else { return };
span_lint_and_then(
@@ -128,7 +132,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa
diag.span_suggestion(
span,
"try",
- format!("{sugg}::mem::swap({}, {})", first.mut_addr(), second.mut_addr()),
+ format!("{sugg}::mem::swap({}, {});", first.mut_addr(), second.mut_addr()),
applicability,
);
if !is_xor_based {
@@ -144,19 +148,19 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
return;
}
- for w in block.stmts.windows(3) {
+ for [s1, s2, s3] in block.stmts.array_windows::<3>() {
if_chain! {
// let t = foo();
- if let StmtKind::Local(tmp) = w[0].kind;
+ if let StmtKind::Local(tmp) = s1.kind;
if let Some(tmp_init) = tmp.init;
if let PatKind::Binding(.., ident, None) = tmp.pat.kind;
// foo() = bar();
- if let StmtKind::Semi(first) = w[1].kind;
+ if let StmtKind::Semi(first) = s2.kind;
if let ExprKind::Assign(lhs1, rhs1, _) = first.kind;
// bar() = t;
- if let StmtKind::Semi(second) = w[2].kind;
+ if let StmtKind::Semi(second) = s3.kind;
if let ExprKind::Assign(lhs2, rhs2, _) = second.kind;
if let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind;
if rhs2.segments.len() == 1;
@@ -164,8 +168,15 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
if ident.name == rhs2.segments[0].ident.name;
if eq_expr_value(cx, tmp_init, lhs1);
if eq_expr_value(cx, rhs1, lhs2);
+
+ let ctxt = s1.span.ctxt();
+ if s2.span.ctxt() == ctxt;
+ if s3.span.ctxt() == ctxt;
+ if first.span.ctxt() == ctxt;
+ if second.span.ctxt() == ctxt;
+
then {
- let span = w[0].span.to(second.span);
+ let span = s1.span.to(s3.span);
generate_swap_warning(cx, lhs1, lhs2, span, false);
}
}
@@ -178,8 +189,10 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
if let Some((lhs0, rhs0)) = parse(first)
&& let Some((lhs1, rhs1)) = parse(second)
&& first.span.eq_ctxt(second.span)
+ && !in_external_macro(cx.sess(), first.span)
&& is_same(cx, lhs0, rhs1)
&& is_same(cx, lhs1, rhs0)
+ && !is_same(cx, lhs1, rhs1) // Ignore a = b; a = a (#10421)
&& let Some(lhs_sugg) = match &lhs0 {
ExprOrIdent::Expr(expr) => Sugg::hir_opt(cx, expr),
ExprOrIdent::Ident(ident) => Some(Sugg::NonParen(ident.as_str().into())),
@@ -246,17 +259,20 @@ fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr<
/// Implementation of the xor case for `MANUAL_SWAP` lint.
fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) {
- for window in block.stmts.windows(3) {
+ for [s1, s2, s3] in block.stmts.array_windows::<3>() {
+ let ctxt = s1.span.ctxt();
if_chain! {
- if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(&window[0]);
- if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(&window[1]);
- if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(&window[2]);
+ if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt);
+ if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(s2, ctxt);
+ if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(s3, ctxt);
if eq_expr_value(cx, lhs0, rhs1);
if eq_expr_value(cx, lhs2, rhs1);
if eq_expr_value(cx, lhs1, rhs0);
if eq_expr_value(cx, lhs1, rhs2);
+ if s2.span.ctxt() == ctxt;
+ if s3.span.ctxt() == ctxt;
then {
- let span = window[0].span.to(window[2].span);
+ let span = s1.span.to(s3.span);
generate_swap_warning(cx, lhs0, rhs0, span, true);
}
};
@@ -264,9 +280,12 @@ fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) {
}
/// Returns the lhs and rhs of an xor assignment statement.
-fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> {
- if let StmtKind::Semi(expr) = stmt.kind {
- if let ExprKind::AssignOp(
+fn extract_sides_of_xor_assign<'a, 'hir>(
+ stmt: &'a Stmt<'hir>,
+ ctxt: SyntaxContext,
+) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> {
+ if let StmtKind::Semi(expr) = stmt.kind
+ && let ExprKind::AssignOp(
Spanned {
node: BinOpKind::BitXor,
..
@@ -274,9 +293,10 @@ fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Ex
lhs,
rhs,
) = expr.kind
- {
- return Some((lhs, rhs));
- }
+ && expr.span.ctxt() == ctxt
+ {
+ Some((lhs, rhs))
+ } else {
+ None
}
- None
}
diff --git a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
new file mode 100644
index 000000000..0a0a77082
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
@@ -0,0 +1,71 @@
+use clippy_utils::{diagnostics::span_lint_and_note, is_in_cfg_test, is_in_test_function};
+use rustc_hir::{intravisit::FnKind, Body, FnDecl};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{def_id::LocalDefId, Span};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Triggers when a testing function (marked with the `#[test]` attribute) isn't inside a testing module
+ /// (marked with `#[cfg(test)]`).
+ /// ### Why is this bad?
+ /// The idiomatic (and more performant) way of writing tests is inside a testing module (flagged with `#[cfg(test)]`),
+ /// having test functions outside of this module is confusing and may lead to them being "hidden".
+ /// ### Example
+ /// ```rust
+ /// #[test]
+ /// fn my_cool_test() {
+ /// // [...]
+ /// }
+ ///
+ /// #[cfg(test)]
+ /// mod tests {
+ /// // [...]
+ /// }
+ ///
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// #[cfg(test)]
+ /// mod tests {
+ /// #[test]
+ /// fn my_cool_test() {
+ /// // [...]
+ /// }
+ /// }
+ /// ```
+ #[clippy::version = "1.70.0"]
+ pub TESTS_OUTSIDE_TEST_MODULE,
+ restriction,
+ "A test function is outside the testing module."
+}
+
+declare_lint_pass!(TestsOutsideTestModule => [TESTS_OUTSIDE_TEST_MODULE]);
+
+impl LateLintPass<'_> for TestsOutsideTestModule {
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'_>,
+ kind: FnKind<'_>,
+ _: &FnDecl<'_>,
+ body: &Body<'_>,
+ sp: Span,
+ _: LocalDefId,
+ ) {
+ if_chain! {
+ if !matches!(kind, FnKind::Closure);
+ if is_in_test_function(cx.tcx, body.id().hir_id);
+ if !is_in_cfg_test(cx.tcx, body.id().hir_id);
+ then {
+ span_lint_and_note(
+ cx,
+ TESTS_OUTSIDE_TEST_MODULE,
+ sp,
+ "this function marked with #[test] is outside a #[cfg(test)] module",
+ None,
+ "move it to a testing module marked with #[cfg(test)]",
+ );
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
index c01cbe509..0dc30f7a9 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
@@ -458,7 +458,7 @@ declare_clippy_lint! {
/// ```rust
/// let null_fn: Option<fn()> = None;
/// ```
- #[clippy::version = "1.67.0"]
+ #[clippy::version = "1.68.0"]
pub TRANSMUTE_NULL_TO_FN,
correctness,
"transmute results in a null function pointer, which is undefined behavior"
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
index 8530b4324..85cd74f23 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
@@ -2,8 +2,9 @@ use super::utils::check_cast;
use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::sugg::Sugg;
+use rustc_ast::ExprPrecedence;
use rustc_errors::Applicability;
-use rustc_hir::Expr;
+use rustc_hir::{Expr, Node};
use rustc_lint::LateContext;
use rustc_middle::ty::{cast::CastKind, Ty};
@@ -19,7 +20,7 @@ pub(super) fn check<'tcx>(
) -> bool {
use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast};
let mut app = Applicability::MachineApplicable;
- let sugg = match check_cast(cx, e, from_ty, to_ty) {
+ let mut sugg = match check_cast(cx, e, from_ty, to_ty) {
Some(PtrPtrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) => {
Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app)
.as_ty(to_ty.to_string())
@@ -39,6 +40,12 @@ pub(super) fn check<'tcx>(
_ => return false,
};
+ if let Node::Expr(parent) = cx.tcx.hir().get_parent(e.hir_id)
+ && parent.precedence().order() > ExprPrecedence::Cast.order()
+ {
+ sugg = format!("({sugg})");
+ }
+
span_lint_and_sugg(
cx,
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
diff --git a/src/tools/clippy/clippy_lints/src/transmute/utils.rs b/src/tools/clippy/clippy_lints/src/transmute/utils.rs
index cddaf9450..62efd13b8 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/utils.rs
@@ -33,38 +33,37 @@ pub(super) fn check_cast<'tcx>(
let hir_id = e.hir_id;
let local_def_id = hir_id.owner.def_id;
- Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
- let fn_ctxt = FnCtxt::new(inherited, cx.param_env, local_def_id);
+ let inherited = Inherited::new(cx.tcx, local_def_id);
+ let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, local_def_id);
- // If we already have errors, we can't be sure we can pointer cast.
+ // If we already have errors, we can't be sure we can pointer cast.
+ assert!(
+ !fn_ctxt.errors_reported_since_creation(),
+ "Newly created FnCtxt contained errors"
+ );
+
+ if let Ok(check) = cast::CastCheck::new(
+ &fn_ctxt,
+ e,
+ from_ty,
+ to_ty,
+ // We won't show any error to the user, so we don't care what the span is here.
+ DUMMY_SP,
+ DUMMY_SP,
+ hir::Constness::NotConst,
+ ) {
+ let res = check.do_check(&fn_ctxt);
+
+ // do_check's documentation says that it might return Ok and create
+ // errors in the fcx instead of returning Err in some cases. Those cases
+ // should be filtered out before getting here.
assert!(
!fn_ctxt.errors_reported_since_creation(),
- "Newly created FnCtxt contained errors"
+ "`fn_ctxt` contained errors after cast check!"
);
- if let Ok(check) = cast::CastCheck::new(
- &fn_ctxt,
- e,
- from_ty,
- to_ty,
- // We won't show any error to the user, so we don't care what the span is here.
- DUMMY_SP,
- DUMMY_SP,
- hir::Constness::NotConst,
- ) {
- let res = check.do_check(&fn_ctxt);
-
- // do_check's documentation says that it might return Ok and create
- // errors in the fcx instead of returning Err in some cases. Those cases
- // should be filtered out before getting here.
- assert!(
- !fn_ctxt.errors_reported_since_creation(),
- "`fn_ctxt` contained errors after cast check!"
- );
-
- res.ok()
- } else {
- None
- }
- })
+ res.ok()
+ } else {
+ None
+ }
}
diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
index 65dfe7637..acdf54710 100644
--- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
+++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
@@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
if let QPath::Resolved(None, path) = *qpath;
if let [ref bx] = *path.segments;
if let Some(params) = bx.args;
- if !params.parenthesized;
+ if params.parenthesized == hir::GenericArgsParentheses::No;
if let Some(inner) = params.args.iter().find_map(|arg| match arg {
GenericArg::Type(ty) => Some(ty),
_ => None,
diff --git a/src/tools/clippy/clippy_lints/src/types/utils.rs b/src/tools/clippy/clippy_lints/src/types/utils.rs
index 7f43b7841..a30748db8 100644
--- a/src/tools/clippy/clippy_lints/src/types/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/types/utils.rs
@@ -1,6 +1,6 @@
use clippy_utils::last_path_segment;
use if_chain::if_chain;
-use rustc_hir::{GenericArg, QPath, TyKind};
+use rustc_hir::{GenericArg, GenericArgsParentheses, QPath, TyKind};
use rustc_lint::LateContext;
use rustc_span::source_map::Span;
@@ -8,7 +8,7 @@ pub(super) fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>)
let last = last_path_segment(qpath);
if_chain! {
if let Some(params) = last.args;
- if !params.parenthesized;
+ if params.parenthesized == GenericArgsParentheses::No;
if let Some(ty) = params.args.iter().find_map(|arg| match arg {
GenericArg::Type(ty) => Some(ty),
_ => None,
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
index d6167a621..cc7c2b039 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
@@ -1,11 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::get_parent_node;
-use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::source::snippet_with_context;
use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source};
use core::ops::ControlFlow;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, Node, PatKind, QPath, TyKind};
+use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, MatchSource, Node, PatKind, QPath, TyKind};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
@@ -41,6 +41,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
);
}
} else {
+ if let ExprKind::Match(_, _, MatchSource::AwaitDesugar) = init.kind {
+ return
+ }
+
span_lint_and_then(
cx,
LET_UNIT_VALUE,
@@ -48,12 +52,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
"this let-binding has unit value",
|diag| {
if let Some(expr) = &local.init {
- let snip = snippet_with_macro_callsite(cx, expr.span, "()");
+ let mut app = Applicability::MachineApplicable;
+ let snip = snippet_with_context(cx, expr.span, local.span.ctxt(), "()", &mut app).0;
diag.span_suggestion(
local.span,
"omit the `let` binding",
format!("{snip};"),
- Applicability::MachineApplicable, // snippet
+ app,
);
}
},
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
new file mode 100644
index 000000000..912bcda63
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
@@ -0,0 +1,120 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_errors::Applicability;
+use rustc_hir::{def_id::LocalDefId, FnDecl, FnRetTy, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::Symbol;
+
+declare_clippy_lint! {
+ /// ### What it does
+ ///
+ /// Checks for a return type containing a `Box<T>` where `T` implements `Sized`
+ ///
+ /// ### Why is this bad?
+ ///
+ /// It's better to just return `T` in these cases. The caller may not need
+ /// the value to be boxed, and it's expensive to free the memory once the
+ /// `Box<T>` been dropped.
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn foo() -> Box<String> {
+ /// Box::new(String::from("Hello, world!"))
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// fn foo() -> String {
+ /// String::from("Hello, world!")
+ /// }
+ /// ```
+ #[clippy::version = "1.70.0"]
+ pub UNNECESSARY_BOX_RETURNS,
+ pedantic,
+ "Needlessly returning a Box"
+}
+
+pub struct UnnecessaryBoxReturns {
+ avoid_breaking_exported_api: bool,
+}
+
+impl_lint_pass!(UnnecessaryBoxReturns => [UNNECESSARY_BOX_RETURNS]);
+
+impl UnnecessaryBoxReturns {
+ pub fn new(avoid_breaking_exported_api: bool) -> Self {
+ Self {
+ avoid_breaking_exported_api,
+ }
+ }
+
+ fn check_fn_item(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, def_id: LocalDefId, name: Symbol) {
+ // we don't want to tell someone to break an exported function if they ask us not to
+ if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) {
+ return;
+ }
+
+ // functions which contain the word "box" are exempt from this lint
+ if name.as_str().contains("box") {
+ return;
+ }
+
+ let FnRetTy::Return(return_ty_hir) = &decl.output else { return };
+
+ let return_ty = cx
+ .tcx
+ .erase_late_bound_regions(cx.tcx.fn_sig(def_id).skip_binder())
+ .output();
+
+ if !return_ty.is_box() {
+ return;
+ }
+
+ let boxed_ty = return_ty.boxed_ty();
+
+ // it's sometimes useful to return Box<T> if T is unsized, so don't lint those
+ if boxed_ty.is_sized(cx.tcx, cx.param_env) {
+ span_lint_and_then(
+ cx,
+ UNNECESSARY_BOX_RETURNS,
+ return_ty_hir.span,
+ format!("boxed return of the sized type `{boxed_ty}`").as_str(),
+ |diagnostic| {
+ diagnostic.span_suggestion(
+ return_ty_hir.span,
+ "try",
+ boxed_ty.to_string(),
+ // the return value and function callers also needs to
+ // be changed, so this can't be MachineApplicable
+ Applicability::Unspecified,
+ );
+ diagnostic.help("changing this also requires a change to the return expressions in this function");
+ },
+ );
+ }
+ }
+}
+
+impl LateLintPass<'_> for UnnecessaryBoxReturns {
+ fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
+ let TraitItemKind::Fn(signature, _) = &item.kind else { return };
+ self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name);
+ }
+
+ fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) {
+ // Ignore implementations of traits, because the lint should be on the
+ // trait, not on the implmentation of it.
+ let Node::Item(parent) = cx.tcx.hir().get_parent(item.hir_id()) else { return };
+ let ItemKind::Impl(parent) = parent.kind else { return };
+ if parent.of_trait.is_some() {
+ return;
+ }
+
+ let ImplItemKind::Fn(signature, ..) = &item.kind else { return };
+ self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name);
+ }
+
+ fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
+ let ItemKind::Fn(signature, ..) = &item.kind else { return };
+ self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name);
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
new file mode 100644
index 000000000..084b03198
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
@@ -0,0 +1,88 @@
+use clippy_utils::{diagnostics::span_lint_and_sugg, get_parent_expr, path_to_local, source::snippet, ty::is_copy};
+use rustc_hir::{BindingAnnotation, Expr, ExprKind, Node, PatKind, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for initialization of a `struct` by copying a base without setting
+ /// any field.
+ ///
+ /// ### Why is this bad?
+ /// Readability suffers from unnecessary struct building.
+ ///
+ /// ### Example
+ /// ```rust
+ /// struct S { s: String }
+ ///
+ /// let a = S { s: String::from("Hello, world!") };
+ /// let b = S { ..a };
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// struct S { s: String }
+ ///
+ /// let a = S { s: String::from("Hello, world!") };
+ /// let b = a;
+ /// ```
+ ///
+ /// ### Known Problems
+ /// Has false positives when the base is a place expression that cannot be
+ /// moved out of, see [#10547](https://github.com/rust-lang/rust-clippy/issues/10547).
+ #[clippy::version = "1.70.0"]
+ pub UNNECESSARY_STRUCT_INITIALIZATION,
+ nursery,
+ "struct built from a base that can be written mode concisely"
+}
+declare_lint_pass!(UnnecessaryStruct => [UNNECESSARY_STRUCT_INITIALIZATION]);
+
+impl LateLintPass<'_> for UnnecessaryStruct {
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+ if let ExprKind::Struct(_, &[], Some(base)) = expr.kind {
+ if let Some(parent) = get_parent_expr(cx, expr) &&
+ let parent_ty = cx.typeck_results().expr_ty_adjusted(parent) &&
+ parent_ty.is_any_ptr()
+ {
+ if is_copy(cx, cx.typeck_results().expr_ty(expr)) && path_to_local(base).is_some() {
+ // When the type implements `Copy`, a reference to the new struct works on the
+ // copy. Using the original would borrow it.
+ return;
+ }
+
+ if parent_ty.is_mutable_ptr() && !is_mutable(cx, base) {
+ // The original can be used in a mutable reference context only if it is mutable.
+ return;
+ }
+ }
+
+ // TODO: do not propose to replace *XX if XX is not Copy
+ if let ExprKind::Unary(UnOp::Deref, target) = base.kind &&
+ matches!(target.kind, ExprKind::Path(..)) &&
+ !is_copy(cx, cx.typeck_results().expr_ty(expr))
+ {
+ // `*base` cannot be used instead of the struct in the general case if it is not Copy.
+ return;
+ }
+
+ span_lint_and_sugg(
+ cx,
+ UNNECESSARY_STRUCT_INITIALIZATION,
+ expr.span,
+ "unnecessary struct building",
+ "replace with",
+ snippet(cx, base.span, "..").into_owned(),
+ rustc_errors::Applicability::MachineApplicable,
+ );
+ }
+ }
+}
+
+fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ if let Some(hir_id) = path_to_local(expr) &&
+ let Node::Pat(pat) = cx.tcx.hir().get(hir_id)
+ {
+ matches!(pat.kind, PatKind::Binding(BindingAnnotation::MUT, ..))
+ } else {
+ true
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index e7c540006..5a0298745 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -10,8 +10,8 @@ use rustc_hir::{
def::{CtorOf, DefKind, Res},
def_id::LocalDefId,
intravisit::{walk_inf, walk_ty, Visitor},
- Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath,
- TyKind,
+ Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericArgsParentheses, GenericParam, GenericParamKind, HirId, Impl,
+ ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass};
@@ -96,19 +96,27 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
// avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
// we're in an `impl` or nested item, that we don't want to lint
let stack_item = if_chain! {
- if let ItemKind::Impl(Impl { self_ty, .. }) = item.kind;
+ if let ItemKind::Impl(Impl { self_ty, generics,.. }) = item.kind;
if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind;
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
if parameters.as_ref().map_or(true, |params| {
- !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
+ params.parenthesized == GenericArgsParentheses::No
+ && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
});
if !item.span.from_expansion();
if !is_from_proc_macro(cx, item); // expensive, should be last check
then {
+ // Self cannot be used inside const generic parameters
+ let types_to_skip = generics.params.iter().filter_map(|param| {
+ match param {
+ GenericParam { kind: GenericParamKind::Const { ty: Ty { hir_id, ..}, ..}, ..} => Some(*hir_id),
+ _ => None,
+ }
+ }).chain(std::iter::once(self_ty.hir_id)).collect();
StackItem::Check {
impl_id: item.owner_id.def_id,
in_body: 0,
- types_to_skip: std::iter::once(self_ty.hir_id).collect(),
+ types_to_skip,
}
} else {
StackItem::NoCheck
diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
index fede625f7..ddbe6b2c7 100644
--- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs
+++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
@@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
-use clippy_utils::source::{snippet, snippet_with_macro_callsite};
+use clippy_utils::source::{snippet, snippet_with_context};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts};
use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, path_to_local, paths};
@@ -68,15 +68,16 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
let a = cx.typeck_results().expr_ty(e);
let b = cx.typeck_results().expr_ty(recv);
if same_type_and_consts(a, b) {
- let sugg = snippet_with_macro_callsite(cx, recv.span, "<expr>").to_string();
+ let mut app = Applicability::MachineApplicable;
+ let sugg = snippet_with_context(cx, recv.span, e.span.ctxt(), "<expr>", &mut app).0;
span_lint_and_sugg(
cx,
USELESS_CONVERSION,
e.span,
&format!("useless conversion to the same type: `{b}`"),
"consider removing `.into()`",
- sugg,
- Applicability::MachineApplicable, // snippet
+ sugg.into_owned(),
+ app,
);
}
}
@@ -165,7 +166,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
if same_type_and_consts(a, b);
then {
- let sugg = Sugg::hir_with_macro_callsite(cx, arg, "<expr>").maybe_par();
+ let mut app = Applicability::MachineApplicable;
+ let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "<expr>", &mut app).maybe_par();
let sugg_msg =
format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));
span_lint_and_sugg(
@@ -175,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
&format!("useless conversion to the same type: `{b}`"),
&sugg_msg,
sugg.to_string(),
- Applicability::MachineApplicable, // snippet
+ app,
);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index c37e5bb67..bc4adf159 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -395,11 +395,6 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
}
self.expr(field!(let_expr.init));
},
- ExprKind::Box(inner) => {
- bind!(self, inner);
- kind!("Box({inner})");
- self.expr(inner);
- },
ExprKind::Array(elements) => {
bind!(self, elements);
kind!("Array({elements})");
@@ -588,7 +583,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
},
}
},
- ExprKind::Err(_) => kind!("Err"),
+ ExprKind::Err(_) => kind!("Err(_)"),
ExprKind::DropTemps(expr) => {
bind!(self, expr);
kind!("DropTemps({expr})");
diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs
index 1c7f3e96d..896a01af3 100644
--- a/src/tools/clippy/clippy_lints/src/utils/conf.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs
@@ -249,7 +249,7 @@ define_Conf! {
/// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
/// ```
(arithmetic_side_effects_allowed_unary: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
- /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
+ /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX, UNNECESSARY_BOX_RETURNS.
///
/// Suppress lints whenever the suggested change would cause breakage for other crates.
(avoid_breaking_exported_api: bool = true),
@@ -275,13 +275,13 @@ define_Conf! {
///
/// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value
/// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
- /// default configuration of Clippy. By default any configuration will replace the default value.
+ /// default configuration of Clippy. By default, any configuration will replace the default value.
(disallowed_names: Vec<String> = super::DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()),
/// Lint: DOC_MARKDOWN.
///
/// The list of words this lint should not consider as identifiers needing ticks. The value
/// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
- /// default configuration of Clippy. By default any configuraction will replace the default value. For example:
+ /// default configuration of Clippy. By default, any configuration will replace the default value. For example:
/// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
/// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
///
@@ -390,7 +390,7 @@ define_Conf! {
/// Enforce the named macros always use the braces specified.
///
/// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
- /// is could be used with a full path two `MacroMatcher`s have to be added one with the full path
+ /// could be used with a full path two `MacroMatcher`s have to be added one with the full path
/// `crate_name::macro_name` and one with just the macro name.
(standard_macro_braces: Vec<crate::nonstandard_macro_braces::MacroMatcher> = Vec::new()),
/// Lint: MISSING_ENFORCED_IMPORT_RENAMES.
@@ -408,7 +408,7 @@ define_Conf! {
/// Lint: INDEX_REFUTABLE_SLICE.
///
/// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in
- /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed.
+ /// the slice pattern that is suggested. If more elements are necessary, the lint is suppressed.
/// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
(max_suggested_slice_pattern_length: u64 = 3),
/// Lint: AWAIT_HOLDING_INVALID_TYPE.
@@ -437,7 +437,7 @@ define_Conf! {
///
/// The maximum size of the `Err`-variant in a `Result` returned from a function
(large_error_threshold: u64 = 128),
- /// Lint: MUTABLE_KEY_TYPE.
+ /// Lint: MUTABLE_KEY_TYPE, IFS_SAME_COND.
///
/// A list of paths to types that should be treated like `Arc`, i.e. ignored but
/// for the generic parameters for determining interior mutability
@@ -459,6 +459,10 @@ define_Conf! {
/// Whether to **only** check for missing documentation in items visible within the current
/// crate. For example, `pub(crate)` items.
(missing_docs_in_crate_items: bool = false),
+ /// Lint: LARGE_FUTURES.
+ ///
+ /// The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint
+ (future_size_threshold: u64 = 16 * 1024),
}
/// Search for the configuration file.
@@ -466,7 +470,7 @@ define_Conf! {
/// # Errors
///
/// Returns any unexpected filesystem error encountered when searching for the config file
-pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
+pub fn lookup_conf_file() -> io::Result<(Option<PathBuf>, Vec<String>)> {
/// Possible filename to search for.
const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
@@ -474,9 +478,11 @@ pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
// 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);
+ .map_or_else(|| PathBuf::from("."), PathBuf::from)
+ .canonicalize()?;
let mut found_config: Option<PathBuf> = None;
+ let mut warnings = vec![];
loop {
for config_file_name in &CONFIG_FILE_NAMES {
@@ -487,12 +493,12 @@ pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
Ok(md) if md.is_dir() => {},
Ok(_) => {
// warn if we happen to find two config files #8323
- if let Some(ref found_config_) = found_config {
- eprintln!(
- "Using config file `{}`\nWarning: `{}` will be ignored.",
- found_config_.display(),
- config_file.display(),
- );
+ if let Some(ref found_config) = found_config {
+ warnings.push(format!(
+ "using config file `{}`, `{}` will be ignored",
+ found_config.display(),
+ config_file.display()
+ ));
} else {
found_config = Some(config_file);
}
@@ -502,12 +508,12 @@ pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
}
if found_config.is_some() {
- return Ok(found_config);
+ return Ok((found_config, warnings));
}
// If the current directory has no parent, we're done searching.
if !current.pop() {
- return Ok(None);
+ return Ok((None, warnings));
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
new file mode 100644
index 000000000..09fcb82c3
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
@@ -0,0 +1,98 @@
+use clippy_utils::macros::collect_ast_format_args;
+use clippy_utils::source::snippet_opt;
+use itertools::Itertools;
+use rustc_ast::{Expr, ExprKind, FormatArgs};
+use rustc_lexer::{tokenize, TokenKind};
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::hygiene;
+use std::iter::once;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Collects [`rustc_ast::FormatArgs`] so that future late passes can call
+ /// [`clippy_utils::macros::find_format_args`]
+ pub FORMAT_ARGS_COLLECTOR,
+ internal_warn,
+ "collects `format_args` AST nodes for use in later lints"
+}
+
+declare_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]);
+
+impl EarlyLintPass for FormatArgsCollector {
+ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+ if let ExprKind::FormatArgs(args) = &expr.kind {
+ if has_span_from_proc_macro(cx, args) {
+ return;
+ }
+
+ collect_ast_format_args(expr.span, args);
+ }
+ }
+}
+
+/// Detects if the format string or an argument has its span set by a proc macro to something inside
+/// a macro callsite, e.g.
+///
+/// ```ignore
+/// println!(some_proc_macro!("input {}"), a);
+/// ```
+///
+/// Where `some_proc_macro` expands to
+///
+/// ```ignore
+/// println!("output {}", a);
+/// ```
+///
+/// But with the span of `"output {}"` set to the macro input
+///
+/// ```ignore
+/// println!(some_proc_macro!("input {}"), a);
+/// // ^^^^^^^^^^
+/// ```
+fn has_span_from_proc_macro(cx: &EarlyContext<'_>, args: &FormatArgs) -> bool {
+ let ctxt = args.span.ctxt();
+
+ // `format!("{} {} {c}", "one", "two", c = "three")`
+ // ^^^^^ ^^^^^ ^^^^^^^
+ let argument_span = args
+ .arguments
+ .explicit_args()
+ .iter()
+ .map(|argument| hygiene::walk_chain(argument.expr.span, ctxt));
+
+ // `format!("{} {} {c}", "one", "two", c = "three")`
+ // ^^ ^^ ^^^^^^
+ let between_spans = once(args.span)
+ .chain(argument_span)
+ .tuple_windows()
+ .map(|(start, end)| start.between(end));
+
+ for between_span in between_spans {
+ let mut seen_comma = false;
+
+ let Some(snippet) = snippet_opt(cx, between_span) else { return true };
+ for token in tokenize(&snippet) {
+ match token.kind {
+ TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace => {},
+ TokenKind::Comma if !seen_comma => seen_comma = true,
+ // named arguments, `start_val, name = end_val`
+ // ^^^^^^^^^ between_span
+ TokenKind::Ident | TokenKind::Eq if seen_comma => {},
+ // An unexpected token usually indicates that we crossed a macro boundary
+ //
+ // `println!(some_proc_macro!("input {}"), a)`
+ // ^^^ between_span
+ // `println!("{}", val!(x))`
+ // ^^^^^^^ between_span
+ _ => return true,
+ }
+ }
+
+ if !seen_comma {
+ return true;
+ }
+ }
+
+ false
+}
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs
index 688a8b865..f8978e30a 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs
@@ -11,7 +11,7 @@ use rustc_hir::def_id::DefId;
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir::interpret::ConstValue;
-use rustc_middle::ty::{self};
+use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::Symbol;
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index b1b5164ff..3d0d4a525 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -26,7 +26,7 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::Ident;
use rustc_span::{sym, Loc, Span, Symbol};
use serde::{ser::SerializeStruct, Serialize, Serializer};
-use std::collections::BinaryHeap;
+use std::collections::{BTreeSet, BinaryHeap};
use std::fmt;
use std::fmt::Write as _;
use std::fs::{self, OpenOptions};
@@ -264,6 +264,9 @@ struct LintMetadata {
/// This field is only used in the output and will only be
/// mapped shortly before the actual output.
applicability: Option<ApplicabilityInfo>,
+ /// All the past names of lints which have been renamed.
+ #[serde(skip_serializing_if = "BTreeSet::is_empty")]
+ former_ids: BTreeSet<String>,
}
impl LintMetadata {
@@ -283,6 +286,7 @@ impl LintMetadata {
version,
docs,
applicability: None,
+ former_ids: BTreeSet::new(),
}
}
}
@@ -901,6 +905,7 @@ fn collect_renames(lints: &mut Vec<LintMetadata>) {
if name == lint_name;
if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX);
then {
+ lint.former_ids.insert(past_name.to_owned());
writeln!(collected, "* `{past_name}`").unwrap();
names.push(past_name.to_string());
}
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
index b59ef4086..14ed1368e 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
@@ -11,7 +11,7 @@ use rustc_hir::def_id::DefId;
use rustc_hir::{Expr, ExprKind, Local, Mutability, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc};
-use rustc_middle::ty::{self, DefIdTree, Ty};
+use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::Symbol;
use rustc_span::Span;
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index 787e9fd98..dc647af26 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -1,5 +1,6 @@
pub mod author;
pub mod conf;
pub mod dump_hir;
+pub mod format_args_collector;
#[cfg(feature = "internal")]
pub mod internal_lints;
diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
index e4d1ee195..36f910c98 100644
--- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
@@ -155,19 +155,13 @@ impl LateLintPass<'_> for WildcardImports {
)
};
- let imports_string = if used_imports.len() == 1 {
- used_imports.iter().next().unwrap().to_string()
+ let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(false);
+ let imports_string = if imports.len() == 1 {
+ imports.pop().unwrap()
+ } else if braced_glob {
+ imports.join(", ")
} else {
- let mut imports = used_imports
- .iter()
- .map(ToString::to_string)
- .collect::<Vec<_>>();
- imports.sort();
- if braced_glob {
- imports.join(", ")
- } else {
- format!("{{{}}}", imports.join(", "))
- }
+ format!("{{{}}}", imports.join(", "))
};
let sugg = if braced_glob {
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index df3350388..d7c94b909 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -1,10 +1,11 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
-use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall};
+use clippy_utils::macros::{find_format_args, format_arg_removal_span, root_macro_call_first_node, MacroCall};
use clippy_utils::source::{expand_past_previous_comma, snippet_opt};
use clippy_utils::{is_in_cfg_test, is_in_test_function};
-use rustc_ast::LitKind;
+use rustc_ast::token::LitKind;
+use rustc_ast::{FormatArgPosition, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, FormatTrait};
use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind};
+use rustc_hir::{Expr, Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{sym, BytePos};
@@ -297,34 +298,40 @@ impl<'tcx> LateLintPass<'tcx> for Write {
_ => return,
}
- let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn) else { return };
-
- // ignore `writeln!(w)` and `write!(v, some_macro!())`
- if format_args.format_string.span.from_expansion() {
- return;
- }
+ find_format_args(cx, expr, macro_call.expn, |format_args| {
+ // ignore `writeln!(w)` and `write!(v, some_macro!())`
+ if format_args.span.from_expansion() {
+ return;
+ }
- match diag_name {
- sym::print_macro | sym::eprint_macro | sym::write_macro => {
- check_newline(cx, &format_args, &macro_call, name);
- },
- sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
- check_empty_string(cx, &format_args, &macro_call, name);
- },
- _ => {},
- }
+ match diag_name {
+ sym::print_macro | sym::eprint_macro | sym::write_macro => {
+ check_newline(cx, format_args, &macro_call, name);
+ },
+ sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
+ check_empty_string(cx, format_args, &macro_call, name);
+ },
+ _ => {},
+ }
- check_literal(cx, &format_args, name);
+ check_literal(cx, format_args, name);
- if !self.in_debug_impl {
- for arg in &format_args.args {
- if arg.format.r#trait == sym::Debug {
- span_lint(cx, USE_DEBUG, arg.span, "use of `Debug`-based formatting");
+ if !self.in_debug_impl {
+ for piece in &format_args.template {
+ if let &FormatArgsPiece::Placeholder(FormatPlaceholder {
+ span: Some(span),
+ format_trait: FormatTrait::Debug,
+ ..
+ }) = piece
+ {
+ span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
+ }
}
}
- }
+ });
}
}
+
fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind
&& let Some(trait_id) = trait_ref.trait_def_id()
@@ -335,16 +342,18 @@ fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
}
}
-fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) {
- let format_string_parts = &format_args.format_string.parts;
- let mut format_string_span = format_args.format_string.span;
-
- let Some(last) = format_string_parts.last() else { return };
+fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
+ let Some(FormatArgsPiece::Literal(last)) = format_args.template.last() else { return };
let count_vertical_whitespace = || {
- format_string_parts
+ format_args
+ .template
.iter()
- .flat_map(|part| part.as_str().chars())
+ .filter_map(|piece| match piece {
+ FormatArgsPiece::Literal(literal) => Some(literal),
+ FormatArgsPiece::Placeholder(_) => None,
+ })
+ .flat_map(|literal| literal.as_str().chars())
.filter(|ch| matches!(ch, '\r' | '\n'))
.count()
};
@@ -352,10 +361,9 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
if last.as_str().ends_with('\n')
// ignore format strings with other internal vertical whitespace
&& count_vertical_whitespace() == 1
-
- // ignore trailing arguments: `print!("Issue\n{}", 1265);`
- && format_string_parts.len() > format_args.args.len()
{
+ let mut format_string_span = format_args.span;
+
let lint = if name == "write" {
format_string_span = expand_past_previous_comma(cx, format_string_span);
@@ -373,7 +381,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
let Some(format_snippet) = snippet_opt(cx, format_string_span) else { return };
- if format_string_parts.len() == 1 && last.as_str() == "\n" {
+ if format_args.template.len() == 1 && last.as_str() == "\n" {
// print!("\n"), write!(f, "\n")
diag.multipart_suggestion(
@@ -398,11 +406,12 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
}
}
-fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) {
- if let [part] = &format_args.format_string.parts[..]
- && let mut span = format_args.format_string.span
- && part.as_str() == "\n"
+fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
+ if let [FormatArgsPiece::Literal(literal)] = &format_args.template[..]
+ && literal.as_str() == "\n"
{
+ let mut span = format_args.span;
+
let lint = if name == "writeln" {
span = expand_past_previous_comma(cx, span);
@@ -428,33 +437,49 @@ fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, ma
}
}
-fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &str) {
- let mut counts = HirIdMap::<usize>::default();
- for param in format_args.params() {
- *counts.entry(param.value.hir_id).or_default() += 1;
+fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
+ let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos);
+
+ let mut counts = vec![0u32; format_args.arguments.all_args().len()];
+ for piece in &format_args.template {
+ if let FormatArgsPiece::Placeholder(placeholder) = piece {
+ counts[arg_index(&placeholder.argument)] += 1;
+ }
}
- for arg in &format_args.args {
- let value = arg.param.value;
-
- if counts[&value.hir_id] == 1
- && arg.format.is_default()
- && let ExprKind::Lit(lit) = &value.kind
- && !value.span.from_expansion()
- && let Some(value_string) = snippet_opt(cx, value.span)
- {
- let (replacement, replace_raw) = match lit.node {
- LitKind::Str(..) => extract_str_literal(&value_string),
- LitKind::Char(ch) => (
- match ch {
- '"' => "\\\"",
- '\'' => "'",
- _ => &value_string[1..value_string.len() - 1],
+ for piece in &format_args.template {
+ if let FormatArgsPiece::Placeholder(FormatPlaceholder {
+ argument,
+ span: Some(placeholder_span),
+ format_trait: FormatTrait::Display,
+ format_options,
+ }) = piece
+ && *format_options == FormatOptions::default()
+ && let index = arg_index(argument)
+ && counts[index] == 1
+ && let Some(arg) = format_args.arguments.by_index(index)
+ && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind
+ && !arg.expr.span.from_expansion()
+ && let Some(value_string) = snippet_opt(cx, arg.expr.span)
+ {
+ let (replacement, replace_raw) = match lit.kind {
+ LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) {
+ Some(extracted) => extracted,
+ None => return,
+ },
+ LitKind::Char => (
+ match lit.symbol.as_str() {
+ "\"" => "\\\"",
+ "\\'" => "'",
+ _ => match value_string.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
+ Some(stripped) => stripped,
+ None => return,
+ },
}
.to_string(),
false,
),
- LitKind::Bool(b) => (b.to_string(), false),
+ LitKind::Bool => (lit.symbol.to_string(), false),
_ => continue,
};
@@ -464,7 +489,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &
PRINT_LITERAL
};
- let format_string_is_raw = format_args.format_string.style.is_some();
+ let Some(format_string_snippet) = snippet_opt(cx, format_args.span) else { continue };
+ let format_string_is_raw = format_string_snippet.starts_with('r');
+
let replacement = match (format_string_is_raw, replace_raw) {
(false, false) => Some(replacement),
(false, true) => Some(replacement.replace('"', "\\\"").replace('\\', "\\\\")),
@@ -485,23 +512,24 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &
span_lint_and_then(
cx,
lint,
- value.span,
+ arg.expr.span,
"literal with an empty format string",
|diag| {
if let Some(replacement) = replacement
// `format!("{}", "a")`, `format!("{named}", named = "b")
// ~~~~~ ~~~~~~~~~~~~~
- && let Some(value_span) = format_args.value_with_prev_comma_span(value.hir_id)
+ && let Some(removal_span) = format_arg_removal_span(format_args, index)
{
let replacement = replacement.replace('{', "{{").replace('}', "}}");
diag.multipart_suggestion(
"try this",
- vec![(arg.span, replacement), (value_span, String::new())],
+ vec![(*placeholder_span, replacement), (removal_span, String::new())],
Applicability::MachineApplicable,
);
}
},
);
+
}
}
}
@@ -511,13 +539,13 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &
/// `r#"a"#` -> (`a`, true)
///
/// `"b"` -> (`b`, false)
-fn extract_str_literal(literal: &str) -> (String, bool) {
+fn extract_str_literal(literal: &str) -> Option<(String, bool)> {
let (literal, raw) = match literal.strip_prefix('r') {
Some(stripped) => (stripped.trim_matches('#'), true),
None => (literal, false),
};
- (literal[1..literal.len() - 1].to_string(), raw)
+ Some((literal.strip_prefix('"')?.strip_suffix('"')?.to_string(), raw))
}
enum UnescapeErr {
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index 173469f6c..124ebd164 100644
--- a/src/tools/clippy/clippy_utils/Cargo.toml
+++ b/src/tools/clippy/clippy_utils/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clippy_utils"
-version = "0.1.69"
+version = "0.1.70"
edition = "2021"
publish = false
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index d82098523..1f15598db 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -143,7 +143,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
(Paren(l), _) => eq_expr(l, r),
(_, Paren(r)) => eq_expr(l, r),
(Err, Err) => true,
- (Box(l), Box(r)) | (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r),
+ (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r),
(Array(l), Array(r)) => over(l, r, |l, r| eq_expr(l, r)),
(Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)),
(Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value),
@@ -209,7 +209,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
&& eq_fn_decl(lf, rf)
&& eq_expr(le, re)
},
- (Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb),
+ (Async(lc, lb), Async(rc, rb)) => lc == rc && eq_block(lb, rb),
(Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt),
(AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re),
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
@@ -286,8 +286,30 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
match (l, r) {
(ExternCrate(l), ExternCrate(r)) => l == r,
(Use(l), Use(r)) => eq_use_tree(l, r),
- (Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
- (Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
+ (
+ Static(box ast::StaticItem {
+ ty: lt,
+ mutability: lm,
+ expr: le,
+ }),
+ Static(box ast::StaticItem {
+ ty: rt,
+ mutability: rm,
+ expr: re,
+ }),
+ ) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
+ (
+ Const(box ast::ConstItem {
+ defaultness: ld,
+ ty: lt,
+ expr: le,
+ }),
+ Const(box ast::ConstItem {
+ defaultness: rd,
+ ty: rt,
+ expr: re,
+ }),
+ ) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
(
Fn(box ast::Fn {
defaultness: ld,
@@ -451,7 +473,18 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
use AssocItemKind::*;
match (l, r) {
- (Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
+ (
+ Const(box ast::ConstItem {
+ defaultness: ld,
+ ty: lt,
+ expr: le,
+ }),
+ Const(box ast::ConstItem {
+ defaultness: rd,
+ ty: rt,
+ expr: re,
+ }),
+ ) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
(
Fn(box ast::Fn {
defaultness: ld,
diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs
index 7987a233b..b4ad42a50 100644
--- a/src/tools/clippy/clippy_utils/src/attrs.rs
+++ b/src/tools/clippy/clippy_utils/src/attrs.rs
@@ -145,8 +145,8 @@ pub fn get_unique_attr<'a>(
/// Return true if the attributes contain any of `proc_macro`,
/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
-pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool {
- attrs.iter().any(|attr| sess.is_proc_macro_attr(attr))
+pub fn is_proc_macro(attrs: &[ast::Attribute]) -> bool {
+ attrs.iter().any(rustc_ast::Attribute::is_proc_macro_attr)
}
/// Return true if the attributes contain `#[doc(hidden)]`
diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
index 43f0df145..d3a6929f6 100644
--- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
+++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
@@ -112,7 +112,6 @@ fn qpath_search_pat(path: &QPath<'_>) -> (Pat, Pat) {
/// Get the search patterns to use for the given expression
fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) {
match e.kind {
- ExprKind::Box(e) => (Pat::Str("box"), expr_search_pat(tcx, e).1),
ExprKind::ConstBlock(_) => (Pat::Str("const"), Pat::Str("}")),
ExprKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")),
ExprKind::Unary(UnOp::Deref, e) => (Pat::Str("*"), expr_search_pat(tcx, e).1),
diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
index ee2f816f1..28c857170 100644
--- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
+++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
@@ -199,11 +199,9 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
},
// Memory allocation, custom operator, loop, or call to an unknown function
- ExprKind::Box(_)
- | ExprKind::Unary(..)
- | ExprKind::Binary(..)
- | ExprKind::Loop(..)
- | ExprKind::Call(..) => self.eagerness = Lazy,
+ ExprKind::Unary(..) | ExprKind::Binary(..) | ExprKind::Loop(..) | ExprKind::Call(..) => {
+ self.eagerness = Lazy;
+ },
ExprKind::ConstBlock(_)
| ExprKind::Array(_)
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index 0603755f8..3ee714782 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -249,7 +249,6 @@ impl HirEqInterExpr<'_, '_, '_> {
both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
&& both(le, re, |l, r| self.eq_expr(l, r))
},
- (&ExprKind::Box(l), &ExprKind::Box(r)) => self.eq_expr(l, r),
(&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => {
self.inner.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
},
@@ -402,14 +401,9 @@ impl HirEqInterExpr<'_, '_, '_> {
}
fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool {
- if !(left.parenthesized || right.parenthesized) {
+ 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
}
@@ -628,7 +622,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_expr(j);
}
},
- ExprKind::Box(e) | ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => {
+ ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => {
self.hash_expr(e);
},
ExprKind::Call(fun, args) => {
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index f02f8ecb4..6b677df46 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -3,7 +3,6 @@
#![feature(let_chains)]
#![feature(lint_reasons)]
#![feature(never_type)]
-#![feature(once_cell)]
#![feature(rustc_private)]
#![recursion_limit = "512"]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
@@ -33,7 +32,6 @@ extern crate rustc_lexer;
extern crate rustc_lint;
extern crate rustc_middle;
extern crate rustc_mir_dataflow;
-extern crate rustc_parse_format;
extern crate rustc_session;
extern crate rustc_span;
extern crate rustc_target;
@@ -78,7 +76,7 @@ use std::sync::OnceLock;
use std::sync::{Mutex, MutexGuard};
use if_chain::if_chain;
-use rustc_ast::ast::{self, LitKind};
+use rustc_ast::ast::{self, LitKind, RangeLimits};
use rustc_ast::Attribute;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::unhash::UnhashMap;
@@ -96,6 +94,7 @@ use rustc_hir::{
use rustc_lexer::{tokenize, TokenKind};
use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::place::PlaceBase;
+use rustc_middle::mir::ConstantKind;
use rustc_middle::ty as rustc_ty;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
use rustc_middle::ty::binding::BindingMode;
@@ -104,7 +103,7 @@ use rustc_middle::ty::fast_reject::SimplifiedType::{
PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType,
};
use rustc_middle::ty::{
- layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, UpvarCapture,
+ layout::IntegerExt, BorrowKind, ClosureKind, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, UpvarCapture,
};
use rustc_middle::ty::{FloatTy, IntTy, UintTy};
use rustc_span::hygiene::{ExpnKind, MacroKind};
@@ -114,7 +113,8 @@ use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::Span;
use rustc_target::abi::Integer;
-use crate::consts::{constant, Constant};
+use crate::consts::{constant, miri_to_const, Constant};
+use crate::higher::Range;
use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
use crate::visitors::for_each_expr;
@@ -617,7 +617,7 @@ fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Re
/// Can return multiple resolutions when there are multiple versions of the same crate, e.g.
/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0.
///
-/// Also returns multiple results when there are mulitple paths under the same name e.g. `std::vec`
+/// Also returns multiple results when there are multiple paths under the same name e.g. `std::vec`
/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`].
///
/// This function is expensive and should be used sparingly.
@@ -1491,6 +1491,68 @@ pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
}
}
+/// Checks whether the given `Expr` is a range equivalent to a `RangeFull`.
+/// For the lower bound, this means that:
+/// - either there is none
+/// - or it is the smallest value that can be represented by the range's integer type
+/// For the upper bound, this means that:
+/// - either there is none
+/// - or it is the largest value that can be represented by the range's integer type and is
+/// inclusive
+/// - or it is a call to some container's `len` method and is exclusive, and the range is passed to
+/// a method call on that same container (e.g. `v.drain(..v.len())`)
+/// If the given `Expr` is not some kind of range, the function returns `false`.
+pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
+ let ty = cx.typeck_results().expr_ty(expr);
+ if let Some(Range { start, end, limits }) = Range::hir(expr) {
+ let start_is_none_or_min = start.map_or(true, |start| {
+ if let rustc_ty::Adt(_, subst) = ty.kind()
+ && let bnd_ty = subst.type_at(0)
+ && let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx)
+ && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, min_val.to_valtree()))
+ && let min_const_kind = ConstantKind::from_value(const_val, bnd_ty)
+ && let Some(min_const) = miri_to_const(cx.tcx, min_const_kind)
+ && let Some((start_const, _)) = constant(cx, cx.typeck_results(), start)
+ {
+ start_const == min_const
+ } else {
+ false
+ }
+ });
+ let end_is_none_or_max = end.map_or(true, |end| {
+ match limits {
+ RangeLimits::Closed => {
+ if let rustc_ty::Adt(_, subst) = ty.kind()
+ && let bnd_ty = subst.type_at(0)
+ && let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx)
+ && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, max_val.to_valtree()))
+ && let max_const_kind = ConstantKind::from_value(const_val, bnd_ty)
+ && let Some(max_const) = miri_to_const(cx.tcx, max_const_kind)
+ && let Some((end_const, _)) = constant(cx, cx.typeck_results(), end)
+ {
+ end_const == max_const
+ } else {
+ false
+ }
+ },
+ RangeLimits::HalfOpen => {
+ if let Some(container_path) = container_path
+ && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind
+ && name.ident.name == sym::len
+ && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
+ {
+ container_path.res == path.res
+ } else {
+ false
+ }
+ },
+ }
+ });
+ return start_is_none_or_min && end_is_none_or_max;
+ }
+ false
+}
+
/// 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 {
@@ -1904,16 +1966,7 @@ pub fn is_async_fn(kind: FnKind<'_>) -> bool {
/// Peels away all the compiler generated code surrounding the body of an async function,
pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
- if let ExprKind::Call(
- _,
- &[
- Expr {
- kind: ExprKind::Closure(&Closure { body, .. }),
- ..
- },
- ],
- ) = body.value.kind
- {
+ if let ExprKind::Closure(&Closure { body, .. }) = body.value.kind {
if let ExprKind::Block(
Block {
stmts: [],
@@ -2114,9 +2167,7 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
traits::impossible_predicates(
cx.tcx,
- traits::elaborate_predicates(cx.tcx, predicates)
- .map(|o| o.predicate)
- .collect::<Vec<_>>(),
+ traits::elaborate(cx.tcx, predicates).collect::<Vec<_>>(),
)
}
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index be6133d32..62d388a5e 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -1,22 +1,18 @@
#![allow(clippy::similar_names)] // `expr` and `expn`
-use crate::source::snippet_opt;
use crate::visitors::{for_each_expr, Descend};
use arrayvec::ArrayVec;
-use itertools::{izip, Either, Itertools};
-use rustc_ast::ast::LitKind;
-use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, LangItem, Node, QPath, TyKind};
-use rustc_lexer::unescape::unescape_literal;
-use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
+use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
use rustc_lint::LateContext;
-use rustc_parse_format::{self as rpf, Alignment};
use rustc_span::def_id::DefId;
use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
-use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol};
-use std::iter::{once, zip};
+use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, Symbol};
+use std::cell::RefCell;
use std::ops::ControlFlow;
+use std::sync::atomic::{AtomicBool, Ordering};
const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
sym::assert_eq_macro,
@@ -213,6 +209,7 @@ pub fn is_assert_macro(cx: &LateContext<'_>, def_id: DefId) -> bool {
matches!(name, sym::assert_macro | sym::debug_assert_macro)
}
+#[derive(Debug)]
pub enum PanicExpn<'a> {
/// No arguments - `panic!()`
Empty,
@@ -221,15 +218,12 @@ pub enum PanicExpn<'a> {
/// A single argument that implements `Display` - `panic!("{}", object)`
Display(&'a Expr<'a>),
/// Anything else - `panic!("error {}: {}", a, b)`
- Format(FormatArgsExpn<'a>),
+ Format(&'a Expr<'a>),
}
impl<'a> PanicExpn<'a> {
- pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Self> {
- if !macro_backtrace(expr.span).any(|macro_call| is_panic(cx, macro_call.def_id)) {
- return None;
- }
- let ExprKind::Call(callee, [arg]) = &expr.kind else { return None };
+ pub fn parse(expr: &'a Expr<'a>) -> Option<Self> {
+ let ExprKind::Call(callee, [arg, rest @ ..]) = &expr.kind else { return None };
let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None };
let result = match path.segments.last().unwrap().ident.as_str() {
"panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
@@ -238,7 +232,22 @@ impl<'a> PanicExpn<'a> {
let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None };
Self::Display(e)
},
- "panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?),
+ "panic_fmt" => Self::Format(arg),
+ // Since Rust 1.52, `assert_{eq,ne}` macros expand to use:
+ // `core::panicking::assert_failed(.., left_val, right_val, None | Some(format_args!(..)));`
+ "assert_failed" => {
+ // It should have 4 arguments in total (we already matched with the first argument,
+ // so we're just checking for 3)
+ if rest.len() != 3 {
+ return None;
+ }
+ // `msg_arg` is either `None` (no custom message) or `Some(format_args!(..))` (custom message)
+ let msg_arg = &rest[2];
+ match msg_arg.kind {
+ ExprKind::Call(_, [fmt_arg]) => Self::Format(fmt_arg),
+ _ => Self::Empty,
+ }
+ },
_ => return None,
};
Some(result)
@@ -251,7 +260,17 @@ pub fn find_assert_args<'a>(
expr: &'a Expr<'a>,
expn: ExpnId,
) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> {
- find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p))
+ find_assert_args_inner(cx, expr, expn).map(|([e], mut p)| {
+ // `assert!(..)` expands to `core::panicking::panic("assertion failed: ...")` (which we map to
+ // `PanicExpn::Str(..)`) and `assert!(.., "..")` expands to
+ // `core::panicking::panic_fmt(format_args!(".."))` (which we map to `PanicExpn::Format(..)`).
+ // So even we got `PanicExpn::Str(..)` that means there is no custom message provided
+ if let PanicExpn::Str(_) = p {
+ p = PanicExpn::Empty;
+ }
+
+ (e, p)
+ })
}
/// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro
@@ -275,13 +294,12 @@ fn find_assert_args_inner<'a, const N: usize>(
Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?,
};
let mut args = ArrayVec::new();
- let mut panic_expn = None;
- let _: Option<!> = for_each_expr(expr, |e| {
+ let panic_expn = for_each_expr(expr, |e| {
if args.is_full() {
- if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() {
- panic_expn = PanicExpn::parse(cx, e);
+ match PanicExpn::parse(e) {
+ Some(expn) => ControlFlow::Break(expn),
+ None => ControlFlow::Continue(Descend::Yes),
}
- ControlFlow::Continue(Descend::from(panic_expn.is_none()))
} else if is_assert_arg(cx, e, expn) {
args.push(e);
ControlFlow::Continue(Descend::No)
@@ -339,241 +357,127 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) ->
}
}
-/// The format string doesn't exist in the HIR, so we reassemble it from source code
-#[derive(Debug)]
-pub struct FormatString {
- /// Span of the whole format string literal, including `[r#]"`.
- pub span: Span,
- /// Snippet of the whole format string literal, including `[r#]"`.
- pub snippet: String,
- /// If the string is raw `r"..."`/`r#""#`, how many `#`s does it have on each side.
- pub style: Option<usize>,
- /// The unescaped value of the format string, e.g. `"val – {}"` for the literal
- /// `"val \u{2013} {}"`.
- pub unescaped: String,
- /// The format string split by format args like `{..}`.
- pub parts: Vec<Symbol>,
+thread_local! {
+ /// We preserve the [`FormatArgs`] structs from the early pass for use in the late pass to be
+ /// able to access the many features of a [`LateContext`].
+ ///
+ /// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an
+ /// assumption that the early pass the populates the map and the later late passes will all be
+ /// running on the same thread.
+ static AST_FORMAT_ARGS: RefCell<FxHashMap<Span, FormatArgs>> = {
+ static CALLED: AtomicBool = AtomicBool::new(false);
+ debug_assert!(
+ !CALLED.swap(true, Ordering::SeqCst),
+ "incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread",
+ );
+
+ RefCell::default()
+ };
}
-impl FormatString {
- fn new(cx: &LateContext<'_>, pieces: &Expr<'_>) -> Option<Self> {
- // format_args!(r"a {} b \", 1);
- //
- // expands to
- //
- // ::core::fmt::Arguments::new_v1(&["a ", " b \\"],
- // &[::core::fmt::ArgumentV1::new_display(&1)]);
- //
- // where `pieces` is the expression `&["a ", " b \\"]`. It has the span of `r"a {} b \"`
- let span = pieces.span;
- let snippet = snippet_opt(cx, span)?;
-
- let (inner, style) = match tokenize(&snippet).next()?.kind {
- TokenKind::Literal { kind, .. } => {
- let style = match kind {
- LiteralKind::Str { .. } => None,
- LiteralKind::RawStr { n_hashes: Some(n), .. } => Some(n.into()),
- _ => return None,
- };
-
- let start = style.map_or(1, |n| 2 + n);
- let end = snippet.len() - style.map_or(1, |n| 1 + n);
-
- (&snippet[start..end], style)
- },
- _ => return None,
- };
+/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this should only be called by
+/// `FormatArgsCollector`
+pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) {
+ AST_FORMAT_ARGS.with(|ast_format_args| {
+ ast_format_args.borrow_mut().insert(span, format_args.clone());
+ });
+}
- let mode = if style.is_some() {
- unescape::Mode::RawStr
+/// Calls `callback` with an AST [`FormatArgs`] node if a `format_args` expansion is found as a
+/// descendant of `expn_id`
+pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, callback: impl FnOnce(&FormatArgs)) {
+ let format_args_expr = for_each_expr(start, |expr| {
+ let ctxt = expr.span.ctxt();
+ if ctxt.outer_expn().is_descendant_of(expn_id) {
+ if macro_backtrace(expr.span)
+ .map(|macro_call| cx.tcx.item_name(macro_call.def_id))
+ .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))
+ {
+ ControlFlow::Break(expr)
+ } else {
+ ControlFlow::Continue(Descend::Yes)
+ }
} else {
- unescape::Mode::Str
- };
-
- let mut unescaped = String::with_capacity(inner.len());
- // Sometimes the original string comes from a macro which accepts a malformed string, such as in a
- // #[display(""somestring)] attribute (accepted by the `displaythis` crate). Reconstructing the
- // string from the span will not be possible, so we will just return None here.
- let mut unparsable = false;
- unescape_literal(inner, mode, &mut |_, ch| match ch {
- Ok(ch) => unescaped.push(ch),
- Err(e) if !e.is_fatal() => (),
- Err(_) => unparsable = true,
- });
- if unparsable {
- return None;
+ ControlFlow::Continue(Descend::No)
}
+ });
- let mut parts = Vec::new();
- let _: Option<!> = for_each_expr(pieces, |expr| {
- if let ExprKind::Lit(lit) = &expr.kind
- && let LitKind::Str(symbol, _) = lit.node
- {
- parts.push(symbol);
- }
- ControlFlow::Continue(())
+ if let Some(expr) = format_args_expr {
+ AST_FORMAT_ARGS.with(|ast_format_args| {
+ ast_format_args.borrow().get(&expr.span).map(callback);
});
-
- Some(Self {
- span,
- snippet,
- style,
- unescaped,
- parts,
- })
}
}
-struct FormatArgsValues<'tcx> {
- /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for
- /// `format!("{x} {} {}", 1, z + 2)`.
- value_args: Vec<&'tcx Expr<'tcx>>,
- /// Maps an `rt::v1::Argument::position` or an `rt::v1::Count::Param` to its index in
- /// `value_args`
- pos_to_value_index: Vec<usize>,
- /// Used to check if a value is declared inline & to resolve `InnerSpan`s.
- format_string_span: SpanData,
-}
-
-impl<'tcx> FormatArgsValues<'tcx> {
- fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self {
- let mut pos_to_value_index = Vec::new();
- let mut value_args = Vec::new();
- let _: Option<!> = for_each_expr(args, |expr| {
- if expr.span.ctxt() == args.span.ctxt() {
- // ArgumentV1::new_<format_trait>(<val>)
- // ArgumentV1::from_usize(<val>)
- if let ExprKind::Call(callee, [val]) = expr.kind
- && let ExprKind::Path(QPath::TypeRelative(ty, _)) = callee.kind
- && let TyKind::Path(QPath::LangItem(LangItem::FormatArgument, _, _)) = ty.kind
- {
- let val_idx = if val.span.ctxt() == expr.span.ctxt()
- && let ExprKind::Field(_, field) = val.kind
- && let Ok(idx) = field.name.as_str().parse()
- {
- // tuple index
- idx
- } else {
- // assume the value expression is passed directly
- pos_to_value_index.len()
- };
-
- pos_to_value_index.push(val_idx);
- }
- ControlFlow::Continue(Descend::Yes)
- } else {
- // assume that any expr with a differing span is a value
- value_args.push(expr);
- ControlFlow::Continue(Descend::No)
- }
- });
-
- Self {
- value_args,
- pos_to_value_index,
- format_string_span,
+/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if
+/// it cannot be found it will return the [`rustc_ast::Expr`].
+pub fn find_format_arg_expr<'hir, 'ast>(
+ start: &'hir Expr<'hir>,
+ target: &'ast FormatArgument,
+) -> Result<&'hir rustc_hir::Expr<'hir>, &'ast rustc_ast::Expr> {
+ for_each_expr(start, |expr| {
+ if expr.span == target.expr.span {
+ ControlFlow::Break(expr)
+ } else {
+ ControlFlow::Continue(())
}
- }
+ })
+ .ok_or(&target.expr)
}
-/// The positions of a format argument's value, precision and width
+/// Span of the `:` and format specifiers
///
-/// A position is an index into the second argument of `Arguments::new_v1[_formatted]`
-#[derive(Debug, Default, Copy, Clone)]
-struct ParamPosition {
- /// The position stored in `rt::v1::Argument::position`.
- value: usize,
- /// The position stored in `rt::v1::FormatSpec::width` if it is a `Count::Param`.
- width: Option<usize>,
- /// The position stored in `rt::v1::FormatSpec::precision` if it is a `Count::Param`.
- precision: Option<usize>,
+/// ```ignore
+/// format!("{:.}"), format!("{foo:.}")
+/// ^^ ^^
+/// ```
+pub fn format_placeholder_format_span(placeholder: &FormatPlaceholder) -> Option<Span> {
+ let base = placeholder.span?.data();
+
+ // `base.hi` is `{...}|`, subtract 1 byte (the length of '}') so that it points before the closing
+ // brace `{...|}`
+ Some(Span::new(
+ placeholder.argument.span?.hi(),
+ base.hi - BytePos(1),
+ base.ctxt,
+ base.parent,
+ ))
}
-impl<'tcx> Visitor<'tcx> for ParamPosition {
- fn visit_expr_field(&mut self, field: &'tcx ExprField<'tcx>) {
- match field.ident.name {
- sym::position => {
- if let ExprKind::Lit(lit) = &field.expr.kind
- && let LitKind::Int(pos, _) = lit.node
- {
- self.value = pos as usize;
- }
- },
- sym::precision => {
- self.precision = parse_count(field.expr);
- },
- sym::width => {
- self.width = parse_count(field.expr);
- },
- _ => walk_expr(self, field.expr),
- }
+/// Span covering the format string and values
+///
+/// ```ignore
+/// format("{}.{}", 10, 11)
+/// // ^^^^^^^^^^^^^^^
+/// ```
+pub fn format_args_inputs_span(format_args: &FormatArgs) -> Span {
+ match format_args.arguments.explicit_args() {
+ [] => format_args.span,
+ [.., last] => format_args
+ .span
+ .to(hygiene::walk_chain(last.expr.span, format_args.span.ctxt())),
}
}
-fn parse_count(expr: &Expr<'_>) -> Option<usize> {
- // <::core::fmt::rt::v1::Count>::Param(1usize),
- if let ExprKind::Call(ctor, [val]) = expr.kind
- && let ExprKind::Path(QPath::TypeRelative(_, path)) = ctor.kind
- && path.ident.name == sym::Param
- && let ExprKind::Lit(lit) = &val.kind
- && let LitKind::Int(pos, _) = lit.node
- {
- Some(pos as usize)
- } else {
- None
- }
-}
+/// Returns the [`Span`] of the value at `index` extended to the previous comma, e.g. for the value
+/// `10`
+///
+/// ```ignore
+/// format("{}.{}", 10, 11)
+/// // ^^^^
+/// ```
+pub fn format_arg_removal_span(format_args: &FormatArgs, index: usize) -> Option<Span> {
+ let ctxt = format_args.span.ctxt();
-/// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)`
-fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option<impl Iterator<Item = ParamPosition> + 'tcx> {
- if let ExprKind::AddrOf(.., array) = fmt_arg.kind
- && let ExprKind::Array(specs) = array.kind
- {
- Some(specs.iter().map(|spec| {
- if let ExprKind::Call(f, args) = spec.kind
- && let ExprKind::Path(QPath::TypeRelative(ty, f)) = f.kind
- && let TyKind::Path(QPath::LangItem(LangItem::FormatPlaceholder, _, _)) = ty.kind
- && f.ident.name == sym::new
- && let [position, _fill, _align, _flags, precision, width] = args
- && let ExprKind::Lit(position) = &position.kind
- && let LitKind::Int(position, _) = position.node {
- ParamPosition {
- value: position as usize,
- width: parse_count(width),
- precision: parse_count(precision),
- }
- } else {
- ParamPosition::default()
- }
- }))
- } else {
- None
- }
-}
+ let current = hygiene::walk_chain(format_args.arguments.by_index(index)?.expr.span, ctxt);
-/// `Span::from_inner`, but for `rustc_parse_format`'s `InnerSpan`
-fn span_from_inner(base: SpanData, inner: rpf::InnerSpan) -> Span {
- Span::new(
- base.lo + BytePos::from_usize(inner.start),
- base.lo + BytePos::from_usize(inner.end),
- base.ctxt,
- base.parent,
- )
-}
+ let prev = if index == 0 {
+ format_args.span
+ } else {
+ hygiene::walk_chain(format_args.arguments.by_index(index - 1)?.expr.span, ctxt)
+ };
-/// How a format parameter is used in the format string
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-pub enum FormatParamKind {
- /// An implicit parameter , such as `{}` or `{:?}`.
- Implicit,
- /// A parameter with an explicit number, e.g. `{1}`, `{0:?}`, or `{:.0$}`
- Numbered,
- /// A parameter with an asterisk precision. e.g. `{:.*}`.
- Starred,
- /// A named parameter with a named `value_arg`, such as the `x` in `format!("{x}", x = 1)`.
- Named(Symbol),
- /// An implicit named parameter, such as the `y` in `format!("{y}")`.
- NamedInline(Symbol),
+ Some(current.with_lo(prev.hi()))
}
/// Where a format parameter is being used in the format string
@@ -587,462 +491,6 @@ pub enum FormatParamUsage {
Precision,
}
-/// A `FormatParam` is any place in a `FormatArgument` that refers to a supplied value, e.g.
-///
-/// ```
-/// let precision = 2;
-/// format!("{:.precision$}", 0.1234);
-/// ```
-///
-/// has two `FormatParam`s, a [`FormatParamKind::Implicit`] `.kind` with a `.value` of `0.1234`
-/// and a [`FormatParamKind::NamedInline("precision")`] `.kind` with a `.value` of `2`
-#[derive(Debug, Copy, Clone)]
-pub struct FormatParam<'tcx> {
- /// The expression this parameter refers to.
- pub value: &'tcx Expr<'tcx>,
- /// How this parameter refers to its `value`.
- pub kind: FormatParamKind,
- /// Where this format param is being used - argument/width/precision
- pub usage: FormatParamUsage,
- /// Span of the parameter, may be zero width. Includes the whitespace of implicit parameters.
- ///
- /// ```text
- /// format!("{}, { }, {0}, {name}", ...);
- /// ^ ~~ ~ ~~~~
- /// ```
- pub span: Span,
-}
-
-impl<'tcx> FormatParam<'tcx> {
- fn new(
- mut kind: FormatParamKind,
- usage: FormatParamUsage,
- position: usize,
- inner: rpf::InnerSpan,
- values: &FormatArgsValues<'tcx>,
- ) -> Option<Self> {
- let value_index = *values.pos_to_value_index.get(position)?;
- let value = *values.value_args.get(value_index)?;
- let span = span_from_inner(values.format_string_span, inner);
-
- // if a param is declared inline, e.g. `format!("{x}")`, the generated expr's span points
- // into the format string
- if let FormatParamKind::Named(name) = kind && values.format_string_span.contains(value.span.data()) {
- kind = FormatParamKind::NamedInline(name);
- }
-
- Some(Self {
- value,
- kind,
- usage,
- span,
- })
- }
-}
-
-/// Used by [width](https://doc.rust-lang.org/std/fmt/#width) and
-/// [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers.
-#[derive(Debug, Copy, Clone)]
-pub enum Count<'tcx> {
- /// Specified with a literal number, stores the value.
- Is(usize, Span),
- /// Specified using `$` and `*` syntaxes. The `*` format is still considered to be
- /// `FormatParamKind::Numbered`.
- Param(FormatParam<'tcx>),
- /// Not specified.
- Implied(Option<Span>),
-}
-
-impl<'tcx> Count<'tcx> {
- fn new(
- usage: FormatParamUsage,
- count: rpf::Count<'_>,
- position: Option<usize>,
- inner: Option<rpf::InnerSpan>,
- values: &FormatArgsValues<'tcx>,
- ) -> Option<Self> {
- let span = inner.map(|inner| span_from_inner(values.format_string_span, inner));
-
- Some(match count {
- rpf::Count::CountIs(val) => Self::Is(val, span?),
- rpf::Count::CountIsName(name, _) => Self::Param(FormatParam::new(
- FormatParamKind::Named(Symbol::intern(name)),
- usage,
- position?,
- inner?,
- values,
- )?),
- rpf::Count::CountIsParam(_) => Self::Param(FormatParam::new(
- FormatParamKind::Numbered,
- usage,
- position?,
- inner?,
- values,
- )?),
- rpf::Count::CountIsStar(_) => Self::Param(FormatParam::new(
- FormatParamKind::Starred,
- usage,
- position?,
- inner?,
- values,
- )?),
- rpf::Count::CountImplied => Self::Implied(span),
- })
- }
-
- pub fn is_implied(self) -> bool {
- matches!(self, Count::Implied(_))
- }
-
- pub fn param(self) -> Option<FormatParam<'tcx>> {
- match self {
- Count::Param(param) => Some(param),
- _ => None,
- }
- }
-
- pub fn span(self) -> Option<Span> {
- match self {
- Count::Is(_, span) => Some(span),
- Count::Param(param) => Some(param.span),
- Count::Implied(span) => span,
- }
- }
-}
-
-/// Specification for the formatting of an argument in the format string. See
-/// <https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters> for the precise meanings.
-#[derive(Debug)]
-pub struct FormatSpec<'tcx> {
- /// Optionally specified character to fill alignment with.
- pub fill: Option<char>,
- /// Optionally specified alignment.
- pub align: Alignment,
- /// Whether all flag options are set to default (no flags specified).
- pub no_flags: bool,
- /// Represents either the maximum width or the integer precision.
- pub precision: Count<'tcx>,
- /// The minimum width, will be padded according to `width`/`align`
- pub width: Count<'tcx>,
- /// The formatting trait used by the argument, e.g. `sym::Display` for `{}`, `sym::Debug` for
- /// `{:?}`.
- pub r#trait: Symbol,
- pub trait_span: Option<Span>,
-}
-
-impl<'tcx> FormatSpec<'tcx> {
- fn new(spec: rpf::FormatSpec<'_>, positions: ParamPosition, values: &FormatArgsValues<'tcx>) -> Option<Self> {
- Some(Self {
- fill: spec.fill,
- align: spec.align,
- no_flags: spec.sign.is_none() && !spec.alternate && !spec.zero_pad && spec.debug_hex.is_none(),
- precision: Count::new(
- FormatParamUsage::Precision,
- spec.precision,
- positions.precision,
- spec.precision_span,
- values,
- )?,
- width: Count::new(
- FormatParamUsage::Width,
- spec.width,
- positions.width,
- spec.width_span,
- values,
- )?,
- r#trait: match spec.ty {
- "" => sym::Display,
- "?" => sym::Debug,
- "o" => sym!(Octal),
- "x" => sym!(LowerHex),
- "X" => sym!(UpperHex),
- "p" => sym::Pointer,
- "b" => sym!(Binary),
- "e" => sym!(LowerExp),
- "E" => sym!(UpperExp),
- _ => return None,
- },
- trait_span: spec
- .ty_span
- .map(|span| span_from_inner(values.format_string_span, span)),
- })
- }
-
- /// Returns true if this format spec is unchanged from the default. e.g. returns true for `{}`,
- /// `{foo}` and `{2}`, but false for `{:?}`, `{foo:5}` and `{3:.5}`
- pub fn is_default(&self) -> bool {
- self.r#trait == sym::Display && self.is_default_for_trait()
- }
-
- /// Has no other formatting specifiers than setting the format trait. returns true for `{}`,
- /// `{foo}`, `{:?}`, but false for `{foo:5}`, `{3:.5?}`
- pub fn is_default_for_trait(&self) -> bool {
- self.width.is_implied() && self.precision.is_implied() && self.align == Alignment::AlignUnknown && self.no_flags
- }
-}
-
-/// A format argument, such as `{}`, `{foo:?}`.
-#[derive(Debug)]
-pub struct FormatArg<'tcx> {
- /// The parameter the argument refers to.
- pub param: FormatParam<'tcx>,
- /// How to format `param`.
- pub format: FormatSpec<'tcx>,
- /// span of the whole argument, `{..}`.
- pub span: Span,
-}
-
-impl<'tcx> FormatArg<'tcx> {
- /// Span of the `:` and format specifiers
- ///
- /// ```ignore
- /// format!("{:.}"), format!("{foo:.}")
- /// ^^ ^^
- /// ```
- pub fn format_span(&self) -> Span {
- let base = self.span.data();
-
- // `base.hi` is `{...}|`, subtract 1 byte (the length of '}') so that it points before the closing
- // brace `{...|}`
- Span::new(self.param.span.hi(), base.hi - BytePos(1), base.ctxt, base.parent)
- }
-}
-
-/// A parsed `format_args!` expansion.
-#[derive(Debug)]
-pub struct FormatArgsExpn<'tcx> {
- /// The format string literal.
- pub format_string: FormatString,
- /// The format arguments, such as `{:?}`.
- pub args: Vec<FormatArg<'tcx>>,
- /// Has an added newline due to `println!()`/`writeln!()`/etc. The last format string part will
- /// include this added newline.
- pub newline: bool,
- /// Spans of the commas between the format string and explicit values, excluding any trailing
- /// comma
- ///
- /// ```ignore
- /// format!("..", 1, 2, 3,)
- /// // ^ ^ ^
- /// ```
- comma_spans: Vec<Span>,
- /// Explicit values passed after the format string, ignoring implicit captures. `[1, z + 2]` for
- /// `format!("{x} {} {y}", 1, z + 2)`.
- explicit_values: Vec<&'tcx Expr<'tcx>>,
-}
-
-impl<'tcx> FormatArgsExpn<'tcx> {
- /// Gets the spans of the commas inbetween the format string and explicit args, not including
- /// any trailing comma
- ///
- /// ```ignore
- /// format!("{} {}", a, b)
- /// // ^ ^
- /// ```
- ///
- /// Ensures that the format string and values aren't coming from a proc macro that sets the
- /// output span to that of its input
- fn comma_spans(cx: &LateContext<'_>, explicit_values: &[&Expr<'_>], fmt_span: Span) -> Option<Vec<Span>> {
- // `format!("{} {} {c}", "one", "two", c = "three")`
- // ^^^^^ ^^^^^ ^^^^^^^
- let value_spans = explicit_values
- .iter()
- .map(|val| hygiene::walk_chain(val.span, fmt_span.ctxt()));
-
- // `format!("{} {} {c}", "one", "two", c = "three")`
- // ^^ ^^ ^^^^^^
- let between_spans = once(fmt_span)
- .chain(value_spans)
- .tuple_windows()
- .map(|(start, end)| start.between(end));
-
- let mut comma_spans = Vec::new();
- for between_span in between_spans {
- let mut offset = 0;
- let mut seen_comma = false;
-
- for token in tokenize(&snippet_opt(cx, between_span)?) {
- match token.kind {
- TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace => {},
- TokenKind::Comma if !seen_comma => {
- seen_comma = true;
-
- let base = between_span.data();
- comma_spans.push(Span::new(
- base.lo + BytePos(offset),
- base.lo + BytePos(offset + 1),
- base.ctxt,
- base.parent,
- ));
- },
- // named arguments, `start_val, name = end_val`
- // ^^^^^^^^^ between_span
- TokenKind::Ident | TokenKind::Eq if seen_comma => {},
- // An unexpected token usually indicates the format string or a value came from a proc macro output
- // that sets the span of its output to an input, e.g. `println!(some_proc_macro!("input"), ..)` that
- // emits a string literal with the span set to that of `"input"`
- _ => return None,
- }
- offset += token.len;
- }
-
- if !seen_comma {
- return None;
- }
- }
-
- Some(comma_spans)
- }
-
- pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
- let macro_name = macro_backtrace(expr.span)
- .map(|macro_call| cx.tcx.item_name(macro_call.def_id))
- .find(|&name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))?;
- let newline = macro_name == sym::format_args_nl;
-
- // ::core::fmt::Arguments::new_v1(pieces, args)
- // ::core::fmt::Arguments::new_v1_formatted(pieces, args, fmt, _unsafe_arg)
- if let ExprKind::Call(callee, [pieces, args, rest @ ..]) = expr.kind
- && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind
- && let TyKind::Path(QPath::LangItem(LangItem::FormatArguments, _, _)) = ty.kind
- && matches!(seg.ident.as_str(), "new_v1" | "new_v1_formatted")
- {
- let format_string = FormatString::new(cx, pieces)?;
-
- let mut parser = rpf::Parser::new(
- &format_string.unescaped,
- format_string.style,
- Some(format_string.snippet.clone()),
- // `format_string.unescaped` does not contain the appended newline
- false,
- rpf::ParseMode::Format,
- );
-
- let parsed_args = parser
- .by_ref()
- .filter_map(|piece| match piece {
- rpf::Piece::NextArgument(a) => Some(a),
- rpf::Piece::String(_) => None,
- })
- .collect_vec();
- if !parser.errors.is_empty() {
- return None;
- }
-
- let positions = if let Some(fmt_arg) = rest.first() {
- // If the argument contains format specs, `new_v1_formatted(_, _, fmt, _)`, parse
- // them.
-
- Either::Left(parse_rt_fmt(fmt_arg)?)
- } else {
- // If no format specs are given, the positions are in the given order and there are
- // no `precision`/`width`s to consider.
-
- Either::Right((0..).map(|n| ParamPosition {
- value: n,
- width: None,
- precision: None,
- }))
- };
-
- let values = FormatArgsValues::new(args, format_string.span.data());
-
- let args = izip!(positions, parsed_args, parser.arg_places)
- .map(|(position, parsed_arg, arg_span)| {
- Some(FormatArg {
- param: FormatParam::new(
- match parsed_arg.position {
- rpf::Position::ArgumentImplicitlyIs(_) => FormatParamKind::Implicit,
- rpf::Position::ArgumentIs(_) => FormatParamKind::Numbered,
- // NamedInline is handled by `FormatParam::new()`
- rpf::Position::ArgumentNamed(name) => FormatParamKind::Named(Symbol::intern(name)),
- },
- FormatParamUsage::Argument,
- position.value,
- parsed_arg.position_span,
- &values,
- )?,
- format: FormatSpec::new(parsed_arg.format, position, &values)?,
- span: span_from_inner(values.format_string_span, arg_span),
- })
- })
- .collect::<Option<Vec<_>>>()?;
-
- let mut explicit_values = values.value_args;
- // remove values generated for implicitly captured vars
- let len = explicit_values
- .iter()
- .take_while(|val| !format_string.span.contains(val.span))
- .count();
- explicit_values.truncate(len);
-
- let comma_spans = Self::comma_spans(cx, &explicit_values, format_string.span)?;
-
- Some(Self {
- format_string,
- args,
- newline,
- comma_spans,
- explicit_values,
- })
- } else {
- None
- }
- }
-
- pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> {
- for_each_expr(expr, |e| {
- let e_ctxt = e.span.ctxt();
- if e_ctxt == expr.span.ctxt() {
- ControlFlow::Continue(Descend::Yes)
- } else if e_ctxt.outer_expn().is_descendant_of(expn_id) {
- if let Some(args) = FormatArgsExpn::parse(cx, e) {
- ControlFlow::Break(args)
- } else {
- ControlFlow::Continue(Descend::No)
- }
- } else {
- ControlFlow::Continue(Descend::No)
- }
- })
- }
-
- /// Source callsite span of all inputs
- pub fn inputs_span(&self) -> Span {
- match *self.explicit_values {
- [] => self.format_string.span,
- [.., last] => self
- .format_string
- .span
- .to(hygiene::walk_chain(last.span, self.format_string.span.ctxt())),
- }
- }
-
- /// Get the span of a value expanded to the previous comma, e.g. for the value `10`
- ///
- /// ```ignore
- /// format("{}.{}", 10, 11)
- /// // ^^^^
- /// ```
- pub fn value_with_prev_comma_span(&self, value_id: HirId) -> Option<Span> {
- for (comma_span, value) in zip(&self.comma_spans, &self.explicit_values) {
- if value.hir_id == value_id {
- return Some(comma_span.to(hygiene::walk_chain(value.span, comma_span.ctxt())));
- }
- }
-
- None
- }
-
- /// Iterator of all format params, both values and those referenced by `width`/`precision`s.
- pub fn params(&'tcx self) -> impl Iterator<Item = FormatParam<'tcx>> {
- self.args
- .iter()
- .flat_map(|arg| [Some(arg.param), arg.format.precision.param(), arg.format.width.param()])
- .flatten()
- }
-}
-
/// A node with a `HirId` and a `Span`
pub trait HirNode {
fn hir_id(&self) -> HirId;
diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs
index dbf9f3b62..e05de2dc9 100644
--- a/src/tools/clippy/clippy_utils/src/msrvs.rs
+++ b/src/tools/clippy/clippy_utils/src/msrvs.rs
@@ -19,6 +19,7 @@ macro_rules! msrv_aliases {
// names may refer to stabilized feature flags or library items
msrv_aliases! {
+ 1,68,0 { PATH_MAIN_SEPARATOR_STR }
1,65,0 { LET_ELSE }
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY }
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index 4aae0f728..9be2d0eae 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -23,6 +23,7 @@ pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"];
pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"];
pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"];
+pub const CORE_RESULT_OK_METHOD: [&str; 4] = ["core", "result", "Result", "ok"];
pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"];
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"];
@@ -67,6 +68,7 @@ pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard
pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"];
pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"];
pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
+pub const PATH_MAIN_SEPARATOR: [&str; 3] = ["std", "path", "MAIN_SEPARATOR"];
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekable"];
pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
@@ -112,6 +114,7 @@ pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
+pub const STD_IO_LINES: [&str; 3] = ["std", "io", "Lines"];
pub const STD_IO_SEEK: [&str; 3] = ["std", "io", "Seek"];
pub const STD_IO_SEEK_FROM_CURRENT: [&str; 4] = ["std", "io", "SeekFrom", "Current"];
pub const STD_IO_SEEKFROM_START: [&str; 4] = ["std", "io", "SeekFrom", "Start"];
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index 1a35fe050..354b6d71a 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -37,7 +37,7 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
- ty::PredicateKind::AliasEq(..) => panic!("alias eq predicate on function: {predicate:#?}"),
+ ty::PredicateKind::AliasRelate(..) => panic!("alias relate predicate on function: {predicate:#?}"),
ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"),
ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"),
ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {predicate:#?}"),
@@ -176,6 +176,10 @@ fn check_rvalue<'tcx>(
// FIXME(dyn-star)
unimplemented!()
},
+ Rvalue::Cast(CastKind::Transmute, _, _) => Err((
+ span,
+ "transmute can attempt to turn pointers into integers, so is unstable in const fn".into(),
+ )),
// binops are fine on integers
Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
check_operand(tcx, lhs, span, body)?;
@@ -241,6 +245,7 @@ fn check_statement<'tcx>(
| StatementKind::StorageDead(_)
| StatementKind::Retag { .. }
| StatementKind::AscribeUserType(..)
+ | StatementKind::PlaceMention(..)
| StatementKind::Coverage(..)
| StatementKind::ConstEvalCounter
| StatementKind::Nop => Ok(()),
@@ -296,17 +301,13 @@ fn check_terminator<'tcx>(
| TerminatorKind::Goto { .. }
| TerminatorKind::Return
| TerminatorKind::Resume
+ | TerminatorKind::Terminate
| TerminatorKind::Unreachable => Ok(()),
TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
- TerminatorKind::DropAndReplace { place, value, .. } => {
- check_place(tcx, *place, span, body)?;
- check_operand(tcx, value, span, body)
- },
TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body),
- TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
Err((span, "const fn generators are unstable".into()))
},
@@ -317,7 +318,7 @@ fn check_terminator<'tcx>(
from_hir_call: _,
destination: _,
target: _,
- cleanup: _,
+ unwind: _,
fn_span: _,
} => {
let fn_ty = func.ty(body, tcx);
@@ -360,7 +361,7 @@ fn check_terminator<'tcx>(
expected: _,
msg: _,
target: _,
- cleanup: _,
+ unwind: _,
} => check_operand(tcx, cond, span, body),
TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs
index cd5dcfdac..62fa37660 100644
--- a/src/tools/clippy/clippy_utils/src/source.rs
+++ b/src/tools/clippy/clippy_utils/src/source.rs
@@ -12,24 +12,21 @@ use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP};
use std::borrow::Cow;
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
-/// Also takes an `Option<String>` which can be put inside the braces.
-pub fn expr_block<'a, T: LintContext>(
+pub fn expr_block<T: LintContext>(
cx: &T,
expr: &Expr<'_>,
- option: Option<String>,
- default: &'a str,
+ outer: SyntaxContext,
+ default: &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)))
+ app: &mut Applicability,
+) -> String {
+ let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app);
+ if from_macro {
+ format!("{{ {code} }}")
} else if let ExprKind::Block(_, _) = expr.kind {
- Cow::Owned(format!("{code}{string}"))
- } else if string.is_empty() {
- Cow::Owned(format!("{{ {code} }}"))
+ format!("{code}")
} else {
- Cow::Owned(format!("{{\n{code};\n{string}\n}}"))
+ format!("{{ {code} }}")
}
}
@@ -229,12 +226,6 @@ fn snippet_with_applicability_sess<'a>(
)
}
-/// 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(cx: &impl LintContext, span: Span) -> Option<String> {
snippet_opt_sess(cx.sess(), span)
@@ -303,6 +294,19 @@ pub fn snippet_block_with_applicability<'a>(
reindent_multiline(snip, true, indent)
}
+pub fn snippet_block_with_context<'a>(
+ cx: &impl LintContext,
+ span: Span,
+ outer: SyntaxContext,
+ default: &'a str,
+ indent_relative_to: Option<Span>,
+ app: &mut Applicability,
+) -> (Cow<'a, str>, bool) {
+ let (snip, from_macro) = snippet_with_context(cx, span, outer, default, app);
+ let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
+ (reindent_multiline(snip, true, indent), from_macro)
+}
+
/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This
/// will result in the macro call, rather then the expansion, if the span is from a child context.
/// If the span is not from a child context, it will be used directly instead.
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index 07feadca2..a5a4a921d 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -1,9 +1,7 @@
//! Contains utility functions to generate suggestions.
#![deny(clippy::missing_docs_in_private_items)]
-use crate::source::{
- snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite,
-};
+use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_context};
use crate::ty::expr_sig;
use crate::{get_parent_expr_for_hir, higher};
use rustc_ast::util::parser::AssocOp;
@@ -89,12 +87,6 @@ impl<'a> Sugg<'a> {
})
}
- /// 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 get_snippet = |span| snippet_with_macro_callsite(cx, span, default);
- Self::hir_from_snippet(expr, get_snippet)
- }
-
/// Same as `hir`, but first walks the span up to the given context. This will result in the
/// macro call, rather then the expansion, if the span is from a child context. If the span is
/// not from a child context, it will be used directly instead.
@@ -133,7 +125,6 @@ impl<'a> Sugg<'a> {
match expr.kind {
hir::ExprKind::AddrOf(..)
- | hir::ExprKind::Box(..)
| hir::ExprKind::If(..)
| hir::ExprKind::Let(..)
| hir::ExprKind::Closure { .. }
@@ -188,7 +179,6 @@ impl<'a> Sugg<'a> {
match expr.kind {
_ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
ast::ExprKind::AddrOf(..)
- | ast::ExprKind::Box(..)
| ast::ExprKind::Closure { .. }
| ast::ExprKind::If(..)
| ast::ExprKind::Let(..)
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 25654e695..9449f0b55 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -16,9 +16,9 @@ use rustc_infer::infer::{
use rustc_lint::LateContext;
use rustc_middle::mir::interpret::{ConstValue, Scalar};
use rustc_middle::ty::{
- self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, IntTy, List, ParamEnv, Predicate,
- PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
- TypeVisitor, UintTy, VariantDef, VariantDiscr,
+ self, layout::ValidityRequirement, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv,
+ Predicate, PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
+ TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr,
};
use rustc_middle::ty::{GenericArg, GenericArgKind};
use rustc_span::symbol::Ident;
@@ -538,11 +538,26 @@ pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
}
/// Checks if a given type looks safe to be uninitialized.
-pub fn is_uninit_value_valid_for_ty(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
+pub fn is_uninit_value_valid_for_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ cx.tcx
+ .check_validity_requirement((ValidityRequirement::Uninit, cx.param_env.and(ty)))
+ .unwrap_or_else(|_| is_uninit_value_valid_for_ty_fallback(cx, ty))
+}
+
+/// A fallback for polymorphic types, which are not supported by `check_validity_requirement`.
+fn is_uninit_value_valid_for_ty_fallback<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
match *ty.kind() {
+ // The array length may be polymorphic, let's try the inner type.
ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component),
+ // Peek through tuples and try their fallbacks.
ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)),
- ty::Adt(adt, _) => cx.tcx.lang_items().maybe_uninit() == Some(adt.did()),
+ // Unions are always fine right now.
+ // This includes MaybeUninit, the main way people use uninitialized memory.
+ // For ADTs, we could look at all fields just like for tuples, but that's potentially
+ // exponential, so let's avoid doing that for now. Code doing that is sketchy enough to
+ // just use an `#[allow()]`.
+ ty::Adt(adt, _) => adt.is_union(),
+ // For the rest, conservatively assume that they cannot be uninit.
_ => false,
}
}
@@ -1121,3 +1136,47 @@ pub fn make_normalized_projection<'tcx>(
}
helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, substs)?)
}
+
+/// Check if given type has inner mutability such as [`std::cell::Cell`] or [`std::cell::RefCell`]
+/// etc.
+pub fn is_interior_mut_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ match *ty.kind() {
+ ty::Ref(_, inner_ty, mutbl) => mutbl == Mutability::Mut || is_interior_mut_ty(cx, inner_ty),
+ ty::Slice(inner_ty) => is_interior_mut_ty(cx, inner_ty),
+ ty::Array(inner_ty, size) => {
+ size.try_eval_target_usize(cx.tcx, cx.param_env)
+ .map_or(true, |u| u != 0)
+ && is_interior_mut_ty(cx, inner_ty)
+ },
+ ty::Tuple(fields) => fields.iter().any(|ty| is_interior_mut_ty(cx, ty)),
+ ty::Adt(def, substs) => {
+ // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to
+ // that of their type parameters. Note: we don't include `HashSet` and `HashMap`
+ // because they have no impl for `Hash` or `Ord`.
+ let def_id = def.did();
+ let is_std_collection = [
+ sym::Option,
+ sym::Result,
+ sym::LinkedList,
+ sym::Vec,
+ sym::VecDeque,
+ sym::BTreeMap,
+ sym::BTreeSet,
+ sym::Rc,
+ sym::Arc,
+ ]
+ .iter()
+ .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def_id));
+ let is_box = Some(def_id) == cx.tcx.lang_items().owned_box();
+ if is_std_collection || is_box {
+ // The type is mutable if any of its type parameters are
+ substs.types().any(|ty| is_interior_mut_ty(cx, ty))
+ } else {
+ !ty.has_escaping_bound_vars()
+ && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
+ && !ty.is_freeze(cx.tcx, cx.param_env)
+ }
+ },
+ _ => false,
+ }
+}
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index d27a20bd4..1dc19bac9 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -599,10 +599,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>(
| ExprKind::Let(&Let { init: e, .. }) => {
helper(typeck, false, e, f)?;
},
- ExprKind::Block(&Block { expr: Some(e), .. }, _)
- | ExprKind::Box(e)
- | ExprKind::Cast(e, _)
- | ExprKind::Unary(_, e) => {
+ ExprKind::Block(&Block { expr: Some(e), .. }, _) | ExprKind::Cast(e, _) | ExprKind::Unary(_, e) => {
helper(typeck, true, e, f)?;
},
ExprKind::Call(callee, args) => {
diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml
index 80eee3681..5c9f76dbb 100644
--- a/src/tools/clippy/declare_clippy_lint/Cargo.toml
+++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "declare_clippy_lint"
-version = "0.1.69"
+version = "0.1.70"
edition = "2021"
publish = false
diff --git a/src/tools/clippy/etc/relicense/RELICENSE_DOCUMENTATION.md b/src/tools/clippy/etc/relicense/RELICENSE_DOCUMENTATION.md
index fcd7abbf3..ffb99cde4 100644
--- a/src/tools/clippy/etc/relicense/RELICENSE_DOCUMENTATION.md
+++ b/src/tools/clippy/etc/relicense/RELICENSE_DOCUMENTATION.md
@@ -35,7 +35,7 @@ relicensing are archived on GitHub. We also have saved Wayback Machine copies of
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:
+There are a few 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
@@ -55,7 +55,7 @@ There are a couple people in relicense_comments.txt who are not found in contrib
we rewrote (see below)
-Two of these contributors had nonminor contributions (#2184, #427) requiring a rewrite, carried out in #3251
+Two of these contributors had non-minor 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))
diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml
index 653121af5..27d32f390 100644
--- a/src/tools/clippy/lintcheck/Cargo.toml
+++ b/src/tools/clippy/lintcheck/Cargo.toml
@@ -8,12 +8,16 @@ repository = "https://github.com/rust-lang/rust-clippy"
categories = ["development-tools"]
edition = "2021"
publish = false
+default-run = "lintcheck"
[dependencies]
+anyhow = "1.0.69"
cargo_metadata = "0.15.3"
-clap = "4.1.4"
+clap = { version = "4.1.8", features = ["derive", "env"] }
+crates_io_api = "0.8.1"
crossbeam-channel = "0.5.6"
flate2 = "1.0"
+indicatif = "0.17.3"
rayon = "1.5.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.85"
@@ -24,3 +28,11 @@ walkdir = "2.3"
[features]
deny-warnings = []
+
+[[bin]]
+name = "lintcheck"
+path = "src/main.rs"
+
+[[bin]]
+name = "popular-crates"
+path = "src/popular-crates.rs"
diff --git a/src/tools/clippy/lintcheck/README.md b/src/tools/clippy/lintcheck/README.md
index 6142de5e3..faf3ce909 100644
--- a/src/tools/clippy/lintcheck/README.md
+++ b/src/tools/clippy/lintcheck/README.md
@@ -16,7 +16,7 @@ or
cargo lintcheck
```
-By default the logs will be saved into
+By default, the logs will be saved into
`lintcheck-logs/lintcheck_crates_logs.txt`.
You can set a custom sources.toml by adding `--crates-toml custom.toml` or using
@@ -25,6 +25,15 @@ the repo root.
The results will then be saved to `lintcheck-logs/custom_logs.toml`.
+The `custom.toml` file may be built using <https://crates.io> recently most
+downloaded crates by using the `popular-crates` binary from the `lintcheck`
+directory. For example, to retrieve the 100 recently most downloaded crates:
+
+```
+cargo run --release --bin popular-crates -- -n 100 custom.toml
+```
+
+
### Configuring the Crate Sources
The sources to check are saved in a `toml` file. There are three types of
diff --git a/src/tools/clippy/lintcheck/src/config.rs b/src/tools/clippy/lintcheck/src/config.rs
index e0244ddce..3f01e9bb0 100644
--- a/src/tools/clippy/lintcheck/src/config.rs
+++ b/src/tools/clippy/lintcheck/src/config.rs
@@ -1,131 +1,79 @@
-use clap::{Arg, ArgAction, ArgMatches, Command};
-use std::env;
-use std::path::PathBuf;
+use clap::Parser;
+use std::{num::NonZeroUsize, path::PathBuf};
-fn get_clap_config() -> ArgMatches {
- Command::new("lintcheck")
- .about("run clippy on a set of crates and check output")
- .args([
- Arg::new("only")
- .action(ArgAction::Set)
- .value_name("CRATE")
- .long("only")
- .help("Only process a single crate of the list"),
- Arg::new("crates-toml")
- .action(ArgAction::Set)
- .value_name("CRATES-SOURCES-TOML-PATH")
- .long("crates-toml")
- .help("Set the path for a crates.toml where lintcheck should read the sources from"),
- Arg::new("threads")
- .action(ArgAction::Set)
- .value_name("N")
- .value_parser(clap::value_parser!(usize))
- .short('j')
- .long("jobs")
- .help("Number of threads to use, 0 automatic choice"),
- Arg::new("fix")
- .long("fix")
- .help("Runs cargo clippy --fix and checks if all suggestions apply"),
- Arg::new("filter")
- .long("filter")
- .action(ArgAction::Append)
- .value_name("clippy_lint_name")
- .help("Apply a filter to only collect specified lints, this also overrides `allow` attributes"),
- Arg::new("markdown")
- .long("markdown")
- .help("Change the reports table to use markdown links"),
- Arg::new("recursive")
- .long("recursive")
- .help("Run clippy on the dependencies of crates specified in crates-toml")
- .conflicts_with("threads")
- .conflicts_with("fix"),
- ])
- .get_matches()
-}
-
-#[derive(Debug, Clone)]
+#[derive(Clone, Debug, Parser)]
pub(crate) struct LintcheckConfig {
- /// max number of jobs to spawn (default 1)
+ /// Number of threads to use (default: all unless --fix or --recursive)
+ #[clap(
+ long = "jobs",
+ short = 'j',
+ value_name = "N",
+ default_value_t = 0,
+ hide_default_value = true
+ )]
pub max_jobs: usize,
- /// we read the sources to check from here
+ /// Set the path for a crates.toml where lintcheck should read the sources from
+ #[clap(
+ long = "crates-toml",
+ value_name = "CRATES-SOURCES-TOML-PATH",
+ default_value = "lintcheck/lintcheck_crates.toml",
+ hide_default_value = true,
+ env = "LINTCHECK_TOML",
+ hide_env = true
+ )]
pub sources_toml_path: PathBuf,
- /// we save the clippy lint results here
- pub lintcheck_results_path: PathBuf,
- /// Check only a specified package
+ /// File to save the clippy lint results here
+ #[clap(skip = "")]
+ pub lintcheck_results_path: PathBuf, // Overridden in new()
+ /// Only process a single crate on the list
+ #[clap(long, value_name = "CRATE")]
pub only: Option<String>,
- /// whether to just run --fix and not collect all the warnings
+ /// Runs cargo clippy --fix and checks if all suggestions apply
+ #[clap(long, conflicts_with("max_jobs"))]
pub fix: bool,
- /// A list of lints that this lintcheck run should focus on
+ /// Apply a filter to only collect specified lints, this also overrides `allow` attributes
+ #[clap(long = "filter", value_name = "clippy_lint_name", use_value_delimiter = true)]
pub lint_filter: Vec<String>,
- /// Indicate if the output should support markdown syntax
+ /// Change the reports table to use markdown links
+ #[clap(long)]
pub markdown: bool,
- /// Run clippy on the dependencies of crates
+ /// Run clippy on the dependencies of crates specified in crates-toml
+ #[clap(long, conflicts_with("max_jobs"))]
pub recursive: bool,
}
impl LintcheckConfig {
pub fn new() -> Self {
- let clap_config = get_clap_config();
-
- // first, check if we got anything passed via the LINTCHECK_TOML env var,
- // if not, ask clap if we got any value for --crates-toml <foo>
- // if not, use the default "lintcheck/lintcheck_crates.toml"
- let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| {
- clap_config
- .get_one::<String>("crates-toml")
- .map_or("lintcheck/lintcheck_crates.toml", |s| &**s)
- .into()
- });
-
- let markdown = clap_config.contains_id("markdown");
- let sources_toml_path = PathBuf::from(sources_toml);
+ let mut config = LintcheckConfig::parse();
// for the path where we save the lint results, get the filename without extension (so for
// wasd.toml, use "wasd"...)
- let filename: PathBuf = sources_toml_path.file_stem().unwrap().into();
- let lintcheck_results_path = PathBuf::from(format!(
+ let filename: PathBuf = config.sources_toml_path.file_stem().unwrap().into();
+ config.lintcheck_results_path = PathBuf::from(format!(
"lintcheck-logs/{}_logs.{}",
filename.display(),
- if markdown { "md" } else { "txt" }
+ if config.markdown { "md" } else { "txt" }
));
- // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and
- // use half of that for the physical core count
- // by default use a single thread
- let max_jobs = match clap_config.get_one::<usize>("threads") {
- Some(&0) => {
- // automatic choice
- // Rayon seems to return thread count so half that for core count
- rayon::current_num_threads() / 2
- },
- Some(&threads) => threads,
- // no -j passed, use a single thread
- None => 1,
+ // look at the --threads arg, if 0 is passed, use the threads count
+ if config.max_jobs == 0 {
+ config.max_jobs = if config.fix || config.recursive {
+ 1
+ } else {
+ std::thread::available_parallelism().map_or(1, NonZeroUsize::get)
+ };
};
- let lint_filter: Vec<String> = clap_config
- .get_many::<String>("filter")
- .map(|iter| {
- iter.map(|lint_name| {
- let mut filter = lint_name.replace('_', "-");
- if !filter.starts_with("clippy::") {
- filter.insert_str(0, "clippy::");
- }
- filter
- })
- .collect()
- })
- .unwrap_or_default();
-
- LintcheckConfig {
- max_jobs,
- sources_toml_path,
- lintcheck_results_path,
- only: clap_config.get_one::<String>("only").map(String::from),
- fix: clap_config.contains_id("fix"),
- lint_filter,
- markdown,
- recursive: clap_config.contains_id("recursive"),
+ for lint_name in &mut config.lint_filter {
+ *lint_name = format!(
+ "clippy::{}",
+ lint_name
+ .strip_prefix("clippy::")
+ .unwrap_or(lint_name)
+ .replace('_', "-")
+ );
}
+
+ config
}
}
diff --git a/src/tools/clippy/lintcheck/src/popular-crates.rs b/src/tools/clippy/lintcheck/src/popular-crates.rs
new file mode 100644
index 000000000..fdab984ad
--- /dev/null
+++ b/src/tools/clippy/lintcheck/src/popular-crates.rs
@@ -0,0 +1,65 @@
+#![deny(clippy::pedantic)]
+
+use clap::Parser;
+use crates_io_api::{CratesQueryBuilder, Sort, SyncClient};
+use indicatif::ProgressBar;
+use std::collections::HashSet;
+use std::fs::File;
+use std::io::{BufWriter, Write};
+use std::path::PathBuf;
+use std::time::Duration;
+
+#[derive(Parser)]
+struct Opts {
+ /// Output TOML file name
+ output: PathBuf,
+ /// Number of crate names to download
+ #[clap(short, long, default_value_t = 100)]
+ number: usize,
+ /// Do not output progress
+ #[clap(short, long)]
+ quiet: bool,
+}
+
+fn main() -> anyhow::Result<()> {
+ let opts = Opts::parse();
+ let mut output = BufWriter::new(File::create(opts.output)?);
+ output.write_all(b"[crates]\n")?;
+ let client = SyncClient::new(
+ "clippy/lintcheck (github.com/rust-lang/rust-clippy/)",
+ Duration::from_secs(1),
+ )?;
+ let mut seen_crates = HashSet::new();
+ let pb = if opts.quiet {
+ None
+ } else {
+ Some(ProgressBar::new(opts.number as u64))
+ };
+ let mut query = CratesQueryBuilder::new()
+ .sort(Sort::RecentDownloads)
+ .page_size(100)
+ .build();
+ while seen_crates.len() < opts.number {
+ let retrieved = client.crates(query.clone())?.crates;
+ if retrieved.is_empty() {
+ eprintln!("No more than {} crates available from API", seen_crates.len());
+ break;
+ }
+ for c in retrieved {
+ if seen_crates.insert(c.name.clone()) {
+ output.write_all(
+ format!(
+ "{} = {{ name = '{}', versions = ['{}'] }}\n",
+ c.name, c.name, c.max_version
+ )
+ .as_bytes(),
+ )?;
+ if let Some(pb) = &pb {
+ pb.inc(1);
+ }
+ }
+ }
+ query.set_page(query.page() + 1);
+ }
+ Ok(())
+}
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index cfe845ec7..91e8ccea1 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
[toolchain]
-channel = "nightly-2023-02-25"
+channel = "nightly-2023-04-06"
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/src/tools/clippy/rustc_tools_util/README.md b/src/tools/clippy/rustc_tools_util/README.md
index eefc661f9..e197ea048 100644
--- a/src/tools/clippy/rustc_tools_util/README.md
+++ b/src/tools/clippy/rustc_tools_util/README.md
@@ -49,6 +49,8 @@ The changelog for `rustc_tools_util` is available under:
## License
+<!-- REUSE-IgnoreStart -->
+
Copyright 2014-2022 The Rust Project Developers
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
@@ -56,3 +58,5 @@ 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.
+
+<!-- REUSE-IgnoreEnd -->
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index dd183362f..718bc41fb 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -1,6 +1,6 @@
#![feature(rustc_private)]
#![feature(let_chains)]
-#![feature(once_cell)]
+#![feature(lazy_cell)]
#![feature(lint_reasons)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
// warn on lints, that are included in `rust-lang/rust`s bootstrap
@@ -130,7 +130,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
#[allow(rustc::bad_opt_access)]
fn config(&mut self, config: &mut interface::Config) {
let conf_path = clippy_lints::lookup_conf_file();
- let conf_path_string = if let Ok(Some(path)) = &conf_path {
+ let conf_path_string = if let Ok((Some(path), _)) = &conf_path {
path.to_str().map(String::from)
} else {
None
@@ -176,7 +176,7 @@ Common options:
--rustc Pass all args to rustc
-V, --version Print version info and exit
-Other options are the same as `cargo check`.
+For the other options see `cargo check --help`.
To allow or deny a lint from the command line you can use `cargo clippy --`
with:
diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs
index 82147eba3..c5e9b96cf 100644
--- a/src/tools/clippy/src/main.rs
+++ b/src/tools/clippy/src/main.rs
@@ -18,7 +18,7 @@ Common options:
-V, --version Print version info and exit
--explain LINT Print the documentation for a given lint
-Other options are the same as `cargo check`.
+For the other options see `cargo check --help`.
To allow or deny a lint from the command line you can use `cargo clippy --`
with:
diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs
index c10ee969c..57890ff31 100644
--- a/src/tools/clippy/tests/compile-test.rs
+++ b/src/tools/clippy/tests/compile-test.rs
@@ -1,5 +1,5 @@
#![feature(test)] // compiletest_rs requires this attribute
-#![feature(once_cell)]
+#![feature(lazy_cell)]
#![feature(is_sorted)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(rust_2018_idioms, unused_lifetimes)]
diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs
index 6d0022f7a..68a878e9a 100644
--- a/src/tools/clippy/tests/dogfood.rs
+++ b/src/tools/clippy/tests/dogfood.rs
@@ -3,10 +3,11 @@
//!
//! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context
-#![feature(once_cell)]
+#![feature(lazy_cell)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(rust_2018_idioms, unused_lifetimes)]
+use itertools::Itertools;
use std::path::PathBuf;
use std::process::Command;
use test_utils::IS_RUSTC_TEST_SUITE;
@@ -19,8 +20,10 @@ fn dogfood_clippy() {
return;
}
+ let mut failed_packages = Vec::new();
+
// "" is the root package
- for package in &[
+ for package in [
"",
"clippy_dev",
"clippy_lints",
@@ -28,8 +31,16 @@ fn dogfood_clippy() {
"lintcheck",
"rustc_tools_util",
] {
- run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]);
+ if !run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]) {
+ failed_packages.push(if package.is_empty() { "root" } else { package });
+ }
}
+
+ assert!(
+ failed_packages.is_empty(),
+ "Dogfood failed for packages `{}`",
+ failed_packages.iter().format(", "),
+ );
}
#[test]
@@ -71,7 +82,7 @@ fn run_metadata_collection_lint() {
run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]);
}
-fn run_clippy_for_package(project: &str, args: &[&str]) {
+fn run_clippy_for_package(project: &str, args: &[&str]) -> bool {
let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH);
@@ -107,5 +118,5 @@ fn run_clippy_for_package(project: &str, args: &[&str]) {
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
- assert!(output.status.success());
+ output.status.success()
}
diff --git a/src/tools/clippy/tests/lint_message_convention.rs b/src/tools/clippy/tests/lint_message_convention.rs
index abd0d1bc5..8feea800f 100644
--- a/src/tools/clippy/tests/lint_message_convention.rs
+++ b/src/tools/clippy/tests/lint_message_convention.rs
@@ -1,4 +1,4 @@
-#![feature(once_cell)]
+#![feature(lazy_cell)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(rust_2018_idioms, unused_lifetimes)]
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr
index 98697e001..aa1b3c638 100644
--- a/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr
+++ b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr
@@ -1,2 +1,4 @@
-Using config file `$SRC_DIR/.clippy.toml`
-Warning: `$SRC_DIR/clippy.toml` will be ignored.
+warning: using config file `$SRC_DIR/.clippy.toml`, `$SRC_DIR/clippy.toml` will be ignored
+
+warning: 1 warning emitted
+
diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr
index 7ed0ef027..b4619e980 100644
--- a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr
+++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr
@@ -9,3 +9,4 @@ note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy
note: Clippy version: foo
+thread panicked while panicking. aborting.
diff --git a/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr
index ee9417621..1be0cda12 100644
--- a/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr
+++ b/src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr
@@ -11,29 +11,29 @@ LL - println!("val='{}'", local_i32);
LL + println!("val='{local_i32}'");
|
-error: literal with an empty format string
- --> $DIR/uninlined_format_args.rs:10:35
+error: variables can be used directly in the `format!` string
+ --> $DIR/uninlined_format_args.rs:10:5
|
LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64);
- | ^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: `-D clippy::print-literal` implied by `-D warnings`
-help: try this
+help: change this to
|
LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64);
-LL + println!("Hello x is {:.*}", local_i32, local_f64);
+LL + println!("Hello {} is {local_f64:.local_i32$}", "x");
|
-error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:10:5
+error: literal with an empty format string
+ --> $DIR/uninlined_format_args.rs:10:35
|
LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^
|
-help: change this to
+ = note: `-D clippy::print-literal` implied by `-D warnings`
+help: try this
|
LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64);
-LL + println!("Hello {} is {local_f64:.local_i32$}", "x");
+LL + println!("Hello x is {:.*}", local_i32, local_f64);
|
error: variables can be used directly in the `format!` string
diff --git a/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.rs b/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.rs
new file mode 100644
index 000000000..7f623c7a9
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.rs
@@ -0,0 +1,10 @@
+#![allow(unused)]
+#![warn(clippy::large_const_arrays, clippy::large_stack_arrays)]
+
+const ABOVE: [u8; 11] = [0; 11];
+const BELOW: [u8; 10] = [0; 10];
+
+fn main() {
+ let above = [0u8; 11];
+ let below = [0u8; 10];
+}
diff --git a/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.stderr b/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.stderr
new file mode 100644
index 000000000..ac017b209
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.stderr
@@ -0,0 +1,29 @@
+error: large array defined as const
+ --> $DIR/array_size_threshold.rs:4:1
+ |
+LL | const ABOVE: [u8; 11] = [0; 11];
+ | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: make this a static item: `static`
+ |
+ = note: `-D clippy::large-const-arrays` implied by `-D warnings`
+
+error: allocating a local array larger than 10 bytes
+ --> $DIR/array_size_threshold.rs:4:25
+ |
+LL | const ABOVE: [u8; 11] = [0; 11];
+ | ^^^^^^^
+ |
+ = help: consider allocating on the heap with `vec![0; 11].into_boxed_slice()`
+ = note: `-D clippy::large-stack-arrays` implied by `-D warnings`
+
+error: allocating a local array larger than 10 bytes
+ --> $DIR/array_size_threshold.rs:8:17
+ |
+LL | let above = [0u8; 11];
+ | ^^^^^^^^^
+ |
+ = help: consider allocating on the heap with `vec![0u8; 11].into_boxed_slice()`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/array_size_threshold/clippy.toml b/src/tools/clippy/tests/ui-toml/array_size_threshold/clippy.toml
new file mode 100644
index 000000000..3f1fe9a12
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/array_size_threshold/clippy.toml
@@ -0,0 +1 @@
+array-size-threshold = 10
diff --git a/src/tools/clippy/tests/ui-toml/extra_unused_type_parameters/clippy.toml b/src/tools/clippy/tests/ui-toml/extra_unused_type_parameters/clippy.toml
new file mode 100644
index 000000000..5f304987a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/extra_unused_type_parameters/clippy.toml
@@ -0,0 +1 @@
+avoid-breaking-exported-api = true
diff --git a/src/tools/clippy/tests/ui-toml/extra_unused_type_parameters/extra_unused_type_parameters.rs b/src/tools/clippy/tests/ui-toml/extra_unused_type_parameters/extra_unused_type_parameters.rs
new file mode 100644
index 000000000..565523245
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/extra_unused_type_parameters/extra_unused_type_parameters.rs
@@ -0,0 +1,9 @@
+pub struct S;
+
+impl S {
+ pub fn exported_fn<T>() {
+ unimplemented!();
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/ifs_same_cond/clippy.toml b/src/tools/clippy/tests/ui-toml/ifs_same_cond/clippy.toml
new file mode 100644
index 000000000..90a36ecd9
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/ifs_same_cond/clippy.toml
@@ -0,0 +1 @@
+ignore-interior-mutability = ["std::cell::Cell"]
diff --git a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs
new file mode 100644
index 000000000..d623ac7e0
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs
@@ -0,0 +1,18 @@
+#![warn(clippy::ifs_same_cond)]
+#![allow(clippy::if_same_then_else, clippy::comparison_chain)]
+
+fn main() {}
+
+fn issue10272() {
+ use std::cell::Cell;
+
+ // Because the `ignore-interior-mutability` configuration
+ // is set to ignore for `std::cell::Cell`, the following `get()` calls
+ // should trigger warning
+ let x = Cell::new(true);
+ if x.get() {
+ } else if !x.take() {
+ } else if x.get() {
+ } else {
+ }
+}
diff --git a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr
new file mode 100644
index 000000000..2841f62bc
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.stderr
@@ -0,0 +1,15 @@
+error: this `if` has the same condition as a previous `if`
+ --> $DIR/ifs_same_cond.rs:15:15
+ |
+LL | } else if x.get() {
+ | ^^^^^^^
+ |
+note: same as this
+ --> $DIR/ifs_same_cond.rs:13:8
+ |
+LL | if x.get() {
+ | ^^^^^^^
+ = note: `-D clippy::ifs-same-cond` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/large_futures/clippy.toml b/src/tools/clippy/tests/ui-toml/large_futures/clippy.toml
new file mode 100644
index 000000000..61bb17fdf
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/large_futures/clippy.toml
@@ -0,0 +1 @@
+future-size-threshold = 1024
diff --git a/src/tools/clippy/tests/ui-toml/large_futures/large_futures.rs b/src/tools/clippy/tests/ui-toml/large_futures/large_futures.rs
new file mode 100644
index 000000000..4158df8b5
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/large_futures/large_futures.rs
@@ -0,0 +1,27 @@
+#![warn(clippy::large_futures)]
+
+fn main() {}
+
+pub async fn should_warn() {
+ let x = [0u8; 1024];
+ async {}.await;
+ dbg!(x);
+}
+
+pub async fn should_not_warn() {
+ let x = [0u8; 1020];
+ async {}.await;
+ dbg!(x);
+}
+
+pub async fn bar() {
+ should_warn().await;
+
+ async {
+ let x = [0u8; 1024];
+ dbg!(x);
+ }
+ .await;
+
+ should_not_warn().await;
+}
diff --git a/src/tools/clippy/tests/ui-toml/large_futures/large_futures.stderr b/src/tools/clippy/tests/ui-toml/large_futures/large_futures.stderr
new file mode 100644
index 000000000..b92734de2
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/large_futures/large_futures.stderr
@@ -0,0 +1,10 @@
+error: large future with a size of 1026 bytes
+ --> $DIR/large_futures.rs:18:5
+ |
+LL | should_warn().await;
+ | ^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(should_warn())`
+ |
+ = note: `-D clippy::large-futures` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
index 6a246afac..8447c3172 100644
--- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
+++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
@@ -24,6 +24,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie
enforced-import-renames
enum-variant-name-threshold
enum-variant-size-threshold
+ future-size-threshold
ignore-interior-mutability
large-error-threshold
literal-representation-threshold
diff --git a/src/tools/clippy/tests/ui/allow_attributes.fixed b/src/tools/clippy/tests/ui/allow_attributes.fixed
new file mode 100644
index 000000000..b8dd0619e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/allow_attributes.fixed
@@ -0,0 +1,25 @@
+// run-rustfix
+#![allow(unused)]
+#![warn(clippy::allow_attributes)]
+#![feature(lint_reasons)]
+
+fn main() {}
+
+// Using clippy::needless_borrow just as a placeholder, it isn't relevant.
+
+// Should lint
+#[expect(dead_code)]
+struct T1;
+
+struct T2; // Should not lint
+#[deny(clippy::needless_borrow)] // Should not lint
+struct T3;
+#[warn(clippy::needless_borrow)] // Should not lint
+struct T4;
+// `panic = "unwind"` should always be true
+#[cfg_attr(panic = "unwind", expect(dead_code))]
+struct CfgT;
+
+fn ignore_inner_attr() {
+ #![allow(unused)] // Should not lint
+}
diff --git a/src/tools/clippy/tests/ui/allow_attributes.rs b/src/tools/clippy/tests/ui/allow_attributes.rs
new file mode 100644
index 000000000..295f56090
--- /dev/null
+++ b/src/tools/clippy/tests/ui/allow_attributes.rs
@@ -0,0 +1,25 @@
+// run-rustfix
+#![allow(unused)]
+#![warn(clippy::allow_attributes)]
+#![feature(lint_reasons)]
+
+fn main() {}
+
+// Using clippy::needless_borrow just as a placeholder, it isn't relevant.
+
+// Should lint
+#[allow(dead_code)]
+struct T1;
+
+struct T2; // Should not lint
+#[deny(clippy::needless_borrow)] // Should not lint
+struct T3;
+#[warn(clippy::needless_borrow)] // Should not lint
+struct T4;
+// `panic = "unwind"` should always be true
+#[cfg_attr(panic = "unwind", allow(dead_code))]
+struct CfgT;
+
+fn ignore_inner_attr() {
+ #![allow(unused)] // Should not lint
+}
diff --git a/src/tools/clippy/tests/ui/allow_attributes.stderr b/src/tools/clippy/tests/ui/allow_attributes.stderr
new file mode 100644
index 000000000..681837e9e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/allow_attributes.stderr
@@ -0,0 +1,16 @@
+error: #[allow] attribute found
+ --> $DIR/allow_attributes.rs:11:3
+ |
+LL | #[allow(dead_code)]
+ | ^^^^^ help: replace it with: `expect`
+ |
+ = note: `-D clippy::allow-attributes` implied by `-D warnings`
+
+error: #[allow] attribute found
+ --> $DIR/allow_attributes.rs:20:30
+ |
+LL | #[cfg_attr(panic = "unwind", allow(dead_code))]
+ | ^^^^^ help: replace it with: `expect`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/almost_complete_range.fixed b/src/tools/clippy/tests/ui/almost_complete_range.fixed
index 6046addf7..a4bf7fe18 100644
--- a/src/tools/clippy/tests/ui/almost_complete_range.fixed
+++ b/src/tools/clippy/tests/ui/almost_complete_range.fixed
@@ -1,6 +1,6 @@
// run-rustfix
// edition:2018
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![feature(exclusive_range_pattern)]
#![feature(stmt_expr_attributes)]
@@ -9,33 +9,10 @@
#![allow(clippy::needless_parens_on_range_literals)]
#![allow(clippy::double_parens)]
-#[macro_use]
-extern crate macro_rules;
-
-macro_rules! a {
- () => {
- 'a'
- };
-}
-macro_rules! A {
- () => {
- 'A'
- };
-}
-macro_rules! zero {
- () => {
- '0'
- };
-}
-
-macro_rules! b {
- () => {
- let _ = 'a'..='z';
- let _ = 'A'..='Z';
- let _ = '0'..='9';
- };
-}
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
+#[inline_macros]
fn main() {
#[rustfmt::skip]
{
@@ -56,9 +33,9 @@ fn main() {
let _ = b'B'..b'Z';
let _ = b'1'..b'9';
- let _ = a!()..='z';
- let _ = A!()..='Z';
- let _ = zero!()..='9';
+ let _ = inline!('a')..='z';
+ let _ = inline!('A')..='Z';
+ let _ = inline!('0')..='9';
let _ = match 0u8 {
b'a'..=b'z' if true => 1,
@@ -80,8 +57,16 @@ fn main() {
_ => 7,
};
- almost_complete_range!();
- b!();
+ external!(
+ let _ = 'a'..'z';
+ let _ = 'A'..'Z';
+ let _ = '0'..'9';
+ );
+ inline!(
+ let _ = 'a'..='z';
+ let _ = 'A'..='Z';
+ let _ = '0'..='9';
+ );
}
#[clippy::msrv = "1.25"]
diff --git a/src/tools/clippy/tests/ui/almost_complete_range.rs b/src/tools/clippy/tests/ui/almost_complete_range.rs
index ae7e07ab8..8237c3a13 100644
--- a/src/tools/clippy/tests/ui/almost_complete_range.rs
+++ b/src/tools/clippy/tests/ui/almost_complete_range.rs
@@ -1,6 +1,6 @@
// run-rustfix
// edition:2018
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![feature(exclusive_range_pattern)]
#![feature(stmt_expr_attributes)]
@@ -9,33 +9,10 @@
#![allow(clippy::needless_parens_on_range_literals)]
#![allow(clippy::double_parens)]
-#[macro_use]
-extern crate macro_rules;
-
-macro_rules! a {
- () => {
- 'a'
- };
-}
-macro_rules! A {
- () => {
- 'A'
- };
-}
-macro_rules! zero {
- () => {
- '0'
- };
-}
-
-macro_rules! b {
- () => {
- let _ = 'a'..'z';
- let _ = 'A'..'Z';
- let _ = '0'..'9';
- };
-}
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
+#[inline_macros]
fn main() {
#[rustfmt::skip]
{
@@ -56,9 +33,9 @@ fn main() {
let _ = b'B'..b'Z';
let _ = b'1'..b'9';
- let _ = a!()..'z';
- let _ = A!()..'Z';
- let _ = zero!()..'9';
+ let _ = inline!('a')..'z';
+ let _ = inline!('A')..'Z';
+ let _ = inline!('0')..'9';
let _ = match 0u8 {
b'a'..b'z' if true => 1,
@@ -80,8 +57,16 @@ fn main() {
_ => 7,
};
- almost_complete_range!();
- b!();
+ external!(
+ let _ = 'a'..'z';
+ let _ = 'A'..'Z';
+ let _ = '0'..'9';
+ );
+ inline!(
+ let _ = 'a'..'z';
+ let _ = 'A'..'Z';
+ let _ = '0'..'9';
+ );
}
#[clippy::msrv = "1.25"]
diff --git a/src/tools/clippy/tests/ui/almost_complete_range.stderr b/src/tools/clippy/tests/ui/almost_complete_range.stderr
index a7a532878..34521c13a 100644
--- a/src/tools/clippy/tests/ui/almost_complete_range.stderr
+++ b/src/tools/clippy/tests/ui/almost_complete_range.stderr
@@ -1,5 +1,5 @@
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:42:17
+ --> $DIR/almost_complete_range.rs:19:17
|
LL | let _ = ('a') ..'z';
| ^^^^^^--^^^
@@ -9,7 +9,7 @@ LL | let _ = ('a') ..'z';
= note: `-D clippy::almost-complete-range` implied by `-D warnings`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:43:17
+ --> $DIR/almost_complete_range.rs:20:17
|
LL | let _ = 'A' .. ('Z');
| ^^^^--^^^^^^
@@ -17,7 +17,7 @@ LL | let _ = 'A' .. ('Z');
| help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:44:17
+ --> $DIR/almost_complete_range.rs:21:17
|
LL | let _ = ((('0'))) .. ('9');
| ^^^^^^^^^^--^^^^^^
@@ -25,7 +25,7 @@ LL | let _ = ((('0'))) .. ('9');
| help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:51:13
+ --> $DIR/almost_complete_range.rs:28:13
|
LL | let _ = (b'a')..(b'z');
| ^^^^^^--^^^^^^
@@ -33,7 +33,7 @@ LL | let _ = (b'a')..(b'z');
| help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:52:13
+ --> $DIR/almost_complete_range.rs:29:13
|
LL | let _ = b'A'..b'Z';
| ^^^^--^^^^
@@ -41,7 +41,7 @@ LL | let _ = b'A'..b'Z';
| help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:53:13
+ --> $DIR/almost_complete_range.rs:30:13
|
LL | let _ = b'0'..b'9';
| ^^^^--^^^^
@@ -49,31 +49,31 @@ LL | let _ = b'0'..b'9';
| help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:59:13
+ --> $DIR/almost_complete_range.rs:36:13
|
-LL | let _ = a!()..'z';
- | ^^^^--^^^
- | |
- | help: use an inclusive range: `..=`
+LL | let _ = inline!('a')..'z';
+ | ^^^^^^^^^^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:60:13
+ --> $DIR/almost_complete_range.rs:37:13
|
-LL | let _ = A!()..'Z';
- | ^^^^--^^^
- | |
- | help: use an inclusive range: `..=`
+LL | let _ = inline!('A')..'Z';
+ | ^^^^^^^^^^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:61:13
+ --> $DIR/almost_complete_range.rs:38:13
|
-LL | let _ = zero!()..'9';
- | ^^^^^^^--^^^
- | |
- | help: use an inclusive range: `..=`
+LL | let _ = inline!('0')..'9';
+ | ^^^^^^^^^^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:64:9
+ --> $DIR/almost_complete_range.rs:41:9
|
LL | b'a'..b'z' if true => 1,
| ^^^^--^^^^
@@ -81,7 +81,7 @@ LL | b'a'..b'z' if true => 1,
| help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:65:9
+ --> $DIR/almost_complete_range.rs:42:9
|
LL | b'A'..b'Z' if true => 2,
| ^^^^--^^^^
@@ -89,7 +89,7 @@ LL | b'A'..b'Z' if true => 2,
| help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:66:9
+ --> $DIR/almost_complete_range.rs:43:9
|
LL | b'0'..b'9' if true => 3,
| ^^^^--^^^^
@@ -97,7 +97,7 @@ LL | b'0'..b'9' if true => 3,
| help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:74:9
+ --> $DIR/almost_complete_range.rs:51:9
|
LL | 'a'..'z' if true => 1,
| ^^^--^^^
@@ -105,7 +105,7 @@ LL | 'a'..'z' if true => 1,
| help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:75:9
+ --> $DIR/almost_complete_range.rs:52:9
|
LL | 'A'..'Z' if true => 2,
| ^^^--^^^
@@ -113,7 +113,7 @@ LL | 'A'..'Z' if true => 2,
| help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:76:9
+ --> $DIR/almost_complete_range.rs:53:9
|
LL | '0'..'9' if true => 3,
| ^^^--^^^
@@ -121,46 +121,37 @@ LL | '0'..'9' if true => 3,
| help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:33:17
+ --> $DIR/almost_complete_range.rs:66:17
|
LL | let _ = 'a'..'z';
| ^^^--^^^
| |
| help: use an inclusive range: `..=`
-...
-LL | b!();
- | ---- in this macro invocation
|
- = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info)
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:34:17
+ --> $DIR/almost_complete_range.rs:67:17
|
LL | let _ = 'A'..'Z';
| ^^^--^^^
| |
| help: use an inclusive range: `..=`
-...
-LL | b!();
- | ---- in this macro invocation
|
- = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info)
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:35:17
+ --> $DIR/almost_complete_range.rs:68:17
|
LL | let _ = '0'..'9';
| ^^^--^^^
| |
| help: use an inclusive range: `..=`
-...
-LL | b!();
- | ---- in this macro invocation
|
- = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info)
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:90:9
+ --> $DIR/almost_complete_range.rs:75:9
|
LL | 'a'..'z' => 1,
| ^^^--^^^
@@ -168,7 +159,7 @@ LL | 'a'..'z' => 1,
| help: use an inclusive range: `...`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:91:9
+ --> $DIR/almost_complete_range.rs:76:9
|
LL | 'A'..'Z' => 2,
| ^^^--^^^
@@ -176,7 +167,7 @@ LL | 'A'..'Z' => 2,
| help: use an inclusive range: `...`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:92:9
+ --> $DIR/almost_complete_range.rs:77:9
|
LL | '0'..'9' => 3,
| ^^^--^^^
@@ -184,7 +175,7 @@ LL | '0'..'9' => 3,
| help: use an inclusive range: `...`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:99:13
+ --> $DIR/almost_complete_range.rs:84:13
|
LL | let _ = 'a'..'z';
| ^^^--^^^
@@ -192,7 +183,7 @@ LL | let _ = 'a'..'z';
| help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:100:13
+ --> $DIR/almost_complete_range.rs:85:13
|
LL | let _ = 'A'..'Z';
| ^^^--^^^
@@ -200,7 +191,7 @@ LL | let _ = 'A'..'Z';
| help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:101:13
+ --> $DIR/almost_complete_range.rs:86:13
|
LL | let _ = '0'..'9';
| ^^^--^^^
@@ -208,7 +199,7 @@ LL | let _ = '0'..'9';
| help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:103:9
+ --> $DIR/almost_complete_range.rs:88:9
|
LL | 'a'..'z' => 1,
| ^^^--^^^
@@ -216,7 +207,7 @@ LL | 'a'..'z' => 1,
| help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:104:9
+ --> $DIR/almost_complete_range.rs:89:9
|
LL | 'A'..'Z' => 1,
| ^^^--^^^
@@ -224,7 +215,7 @@ LL | 'A'..'Z' => 1,
| help: use an inclusive range: `..=`
error: almost complete ascii range
- --> $DIR/almost_complete_range.rs:105:9
+ --> $DIR/almost_complete_range.rs:90:9
|
LL | '0'..'9' => 3,
| ^^^--^^^
diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
index 2611e3a78..3c06676d7 100644
--- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
+++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
@@ -45,24 +45,32 @@ impl_arith!(
Div, Custom, Custom, div;
Mul, Custom, Custom, mul;
Rem, Custom, Custom, rem;
+ Shl, Custom, Custom, shl;
+ Shr, Custom, Custom, shr;
Sub, Custom, Custom, sub;
Add, Custom, &Custom, add;
Div, Custom, &Custom, div;
Mul, Custom, &Custom, mul;
Rem, Custom, &Custom, rem;
+ Shl, Custom, &Custom, shl;
+ Shr, Custom, &Custom, shr;
Sub, Custom, &Custom, sub;
Add, &Custom, Custom, add;
Div, &Custom, Custom, div;
Mul, &Custom, Custom, mul;
Rem, &Custom, Custom, rem;
+ Shl, &Custom, Custom, shl;
+ Shr, &Custom, Custom, shr;
Sub, &Custom, Custom, sub;
Add, &Custom, &Custom, add;
Div, &Custom, &Custom, div;
Mul, &Custom, &Custom, mul;
Rem, &Custom, &Custom, rem;
+ Shl, &Custom, &Custom, shl;
+ Shr, &Custom, &Custom, shr;
Sub, &Custom, &Custom, sub;
);
@@ -71,24 +79,32 @@ impl_assign_arith!(
DivAssign, Custom, Custom, div_assign;
MulAssign, Custom, Custom, mul_assign;
RemAssign, Custom, Custom, rem_assign;
+ ShlAssign, Custom, Custom, shl_assign;
+ ShrAssign, Custom, Custom, shr_assign;
SubAssign, Custom, Custom, sub_assign;
AddAssign, Custom, &Custom, add_assign;
DivAssign, Custom, &Custom, div_assign;
MulAssign, Custom, &Custom, mul_assign;
RemAssign, Custom, &Custom, rem_assign;
+ ShlAssign, Custom, &Custom, shl_assign;
+ ShrAssign, Custom, &Custom, shr_assign;
SubAssign, Custom, &Custom, sub_assign;
AddAssign, &Custom, Custom, add_assign;
DivAssign, &Custom, Custom, div_assign;
MulAssign, &Custom, Custom, mul_assign;
RemAssign, &Custom, Custom, rem_assign;
+ ShlAssign, &Custom, Custom, shl_assign;
+ ShrAssign, &Custom, Custom, shr_assign;
SubAssign, &Custom, Custom, sub_assign;
AddAssign, &Custom, &Custom, add_assign;
DivAssign, &Custom, &Custom, div_assign;
MulAssign, &Custom, &Custom, mul_assign;
RemAssign, &Custom, &Custom, rem_assign;
+ ShlAssign, &Custom, &Custom, shl_assign;
+ ShrAssign, &Custom, &Custom, shr_assign;
SubAssign, &Custom, &Custom, sub_assign;
);
@@ -297,6 +313,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() {
_custom %= &Custom;
_custom *= Custom;
_custom *= &Custom;
+ _custom >>= Custom;
+ _custom >>= &Custom;
+ _custom <<= Custom;
+ _custom <<= &Custom;
_custom += -Custom;
_custom += &-Custom;
_custom -= -Custom;
@@ -307,6 +327,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() {
_custom %= &-Custom;
_custom *= -Custom;
_custom *= &-Custom;
+ _custom >>= -Custom;
+ _custom >>= &-Custom;
+ _custom <<= -Custom;
+ _custom <<= &-Custom;
// Binary
_n = _n + 1;
@@ -347,6 +371,10 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() {
_custom = Custom + &Custom;
_custom = &Custom + Custom;
_custom = &Custom + &Custom;
+ _custom = _custom >> _custom;
+ _custom = _custom >> &_custom;
+ _custom = Custom << _custom;
+ _custom = &Custom << _custom;
// Unary
_n = -_n;
@@ -397,4 +425,8 @@ pub fn integer_arithmetic() {
i ^= i;
}
+pub fn issue_10583(a: u16) -> u16 {
+ 10 / a
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
index 17a2448fb..2c8ee2884 100644
--- a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
+++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
@@ -1,5 +1,5 @@
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:270:5
+ --> $DIR/arithmetic_side_effects.rs:286:5
|
LL | _n += 1;
| ^^^^^^^
@@ -7,592 +7,652 @@ LL | _n += 1;
= note: `-D clippy::arithmetic-side-effects` implied by `-D warnings`
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:271:5
+ --> $DIR/arithmetic_side_effects.rs:287:5
|
LL | _n += &1;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:272:5
+ --> $DIR/arithmetic_side_effects.rs:288:5
|
LL | _n -= 1;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:273:5
+ --> $DIR/arithmetic_side_effects.rs:289:5
|
LL | _n -= &1;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:274:5
+ --> $DIR/arithmetic_side_effects.rs:290:5
|
LL | _n /= 0;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:275:5
+ --> $DIR/arithmetic_side_effects.rs:291:5
|
LL | _n /= &0;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:276:5
+ --> $DIR/arithmetic_side_effects.rs:292:5
|
LL | _n %= 0;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:277:5
+ --> $DIR/arithmetic_side_effects.rs:293:5
|
LL | _n %= &0;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:278:5
+ --> $DIR/arithmetic_side_effects.rs:294:5
|
LL | _n *= 2;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:279:5
+ --> $DIR/arithmetic_side_effects.rs:295:5
|
LL | _n *= &2;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:280:5
+ --> $DIR/arithmetic_side_effects.rs:296:5
|
LL | _n += -1;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:281:5
+ --> $DIR/arithmetic_side_effects.rs:297:5
|
LL | _n += &-1;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:282:5
+ --> $DIR/arithmetic_side_effects.rs:298:5
|
LL | _n -= -1;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:283:5
+ --> $DIR/arithmetic_side_effects.rs:299:5
|
LL | _n -= &-1;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:284:5
+ --> $DIR/arithmetic_side_effects.rs:300:5
|
LL | _n /= -0;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:285:5
+ --> $DIR/arithmetic_side_effects.rs:301:5
|
LL | _n /= &-0;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:286:5
+ --> $DIR/arithmetic_side_effects.rs:302:5
|
LL | _n %= -0;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:287:5
+ --> $DIR/arithmetic_side_effects.rs:303:5
|
LL | _n %= &-0;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:288:5
+ --> $DIR/arithmetic_side_effects.rs:304:5
|
LL | _n *= -2;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:289:5
+ --> $DIR/arithmetic_side_effects.rs:305:5
|
LL | _n *= &-2;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:290:5
+ --> $DIR/arithmetic_side_effects.rs:306:5
|
LL | _custom += Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:291:5
+ --> $DIR/arithmetic_side_effects.rs:307:5
|
LL | _custom += &Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:292:5
+ --> $DIR/arithmetic_side_effects.rs:308:5
|
LL | _custom -= Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:293:5
+ --> $DIR/arithmetic_side_effects.rs:309:5
|
LL | _custom -= &Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:294:5
+ --> $DIR/arithmetic_side_effects.rs:310:5
|
LL | _custom /= Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:295:5
+ --> $DIR/arithmetic_side_effects.rs:311:5
|
LL | _custom /= &Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:296:5
+ --> $DIR/arithmetic_side_effects.rs:312:5
|
LL | _custom %= Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:297:5
+ --> $DIR/arithmetic_side_effects.rs:313:5
|
LL | _custom %= &Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:298:5
+ --> $DIR/arithmetic_side_effects.rs:314:5
|
LL | _custom *= Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:299:5
+ --> $DIR/arithmetic_side_effects.rs:315:5
|
LL | _custom *= &Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:300:5
+ --> $DIR/arithmetic_side_effects.rs:316:5
+ |
+LL | _custom >>= Custom;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:317:5
+ |
+LL | _custom >>= &Custom;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:318:5
+ |
+LL | _custom <<= Custom;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:319:5
+ |
+LL | _custom <<= &Custom;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:320:5
|
LL | _custom += -Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:301:5
+ --> $DIR/arithmetic_side_effects.rs:321:5
|
LL | _custom += &-Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:302:5
+ --> $DIR/arithmetic_side_effects.rs:322:5
|
LL | _custom -= -Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:303:5
+ --> $DIR/arithmetic_side_effects.rs:323:5
|
LL | _custom -= &-Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:304:5
+ --> $DIR/arithmetic_side_effects.rs:324:5
|
LL | _custom /= -Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:305:5
+ --> $DIR/arithmetic_side_effects.rs:325:5
|
LL | _custom /= &-Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:306:5
+ --> $DIR/arithmetic_side_effects.rs:326:5
|
LL | _custom %= -Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:307:5
+ --> $DIR/arithmetic_side_effects.rs:327:5
|
LL | _custom %= &-Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:308:5
+ --> $DIR/arithmetic_side_effects.rs:328:5
|
LL | _custom *= -Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:309:5
+ --> $DIR/arithmetic_side_effects.rs:329:5
|
LL | _custom *= &-Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:312:10
+ --> $DIR/arithmetic_side_effects.rs:330:5
+ |
+LL | _custom >>= -Custom;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:331:5
+ |
+LL | _custom >>= &-Custom;
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:332:5
+ |
+LL | _custom <<= -Custom;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:333:5
+ |
+LL | _custom <<= &-Custom;
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:336:10
|
LL | _n = _n + 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:313:10
+ --> $DIR/arithmetic_side_effects.rs:337:10
|
LL | _n = _n + &1;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:314:10
+ --> $DIR/arithmetic_side_effects.rs:338:10
|
LL | _n = 1 + _n;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:315:10
+ --> $DIR/arithmetic_side_effects.rs:339:10
|
LL | _n = &1 + _n;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:316:10
+ --> $DIR/arithmetic_side_effects.rs:340:10
|
LL | _n = _n - 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:317:10
+ --> $DIR/arithmetic_side_effects.rs:341:10
|
LL | _n = _n - &1;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:318:10
+ --> $DIR/arithmetic_side_effects.rs:342:10
|
LL | _n = 1 - _n;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:319:10
+ --> $DIR/arithmetic_side_effects.rs:343:10
|
LL | _n = &1 - _n;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:320:10
+ --> $DIR/arithmetic_side_effects.rs:344:10
|
LL | _n = _n / 0;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:321:10
+ --> $DIR/arithmetic_side_effects.rs:345:10
|
LL | _n = _n / &0;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:322:10
+ --> $DIR/arithmetic_side_effects.rs:346:10
|
LL | _n = _n % 0;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:323:10
+ --> $DIR/arithmetic_side_effects.rs:347:10
|
LL | _n = _n % &0;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:324:10
+ --> $DIR/arithmetic_side_effects.rs:348:10
|
LL | _n = _n * 2;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:325:10
+ --> $DIR/arithmetic_side_effects.rs:349:10
|
LL | _n = _n * &2;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:326:10
+ --> $DIR/arithmetic_side_effects.rs:350:10
|
LL | _n = 2 * _n;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:327:10
+ --> $DIR/arithmetic_side_effects.rs:351:10
|
LL | _n = &2 * _n;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:328:10
+ --> $DIR/arithmetic_side_effects.rs:352:10
|
LL | _n = 23 + &85;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:329:10
+ --> $DIR/arithmetic_side_effects.rs:353:10
|
LL | _n = &23 + 85;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:330:10
+ --> $DIR/arithmetic_side_effects.rs:354:10
|
LL | _n = &23 + &85;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:331:15
+ --> $DIR/arithmetic_side_effects.rs:355:15
|
LL | _custom = _custom + _custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:332:15
+ --> $DIR/arithmetic_side_effects.rs:356:15
|
LL | _custom = _custom + &_custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:333:15
+ --> $DIR/arithmetic_side_effects.rs:357:15
|
LL | _custom = Custom + _custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:334:15
+ --> $DIR/arithmetic_side_effects.rs:358:15
|
LL | _custom = &Custom + _custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:335:15
+ --> $DIR/arithmetic_side_effects.rs:359:15
|
LL | _custom = _custom - Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:336:15
+ --> $DIR/arithmetic_side_effects.rs:360:15
|
LL | _custom = _custom - &Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:337:15
+ --> $DIR/arithmetic_side_effects.rs:361:15
|
LL | _custom = Custom - _custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:338:15
+ --> $DIR/arithmetic_side_effects.rs:362:15
|
LL | _custom = &Custom - _custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:339:15
+ --> $DIR/arithmetic_side_effects.rs:363:15
|
LL | _custom = _custom / Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:340:15
+ --> $DIR/arithmetic_side_effects.rs:364:15
|
LL | _custom = _custom / &Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:341:15
+ --> $DIR/arithmetic_side_effects.rs:365:15
|
LL | _custom = _custom % Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:342:15
+ --> $DIR/arithmetic_side_effects.rs:366:15
|
LL | _custom = _custom % &Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:343:15
+ --> $DIR/arithmetic_side_effects.rs:367:15
|
LL | _custom = _custom * Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:344:15
+ --> $DIR/arithmetic_side_effects.rs:368:15
|
LL | _custom = _custom * &Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:345:15
+ --> $DIR/arithmetic_side_effects.rs:369:15
|
LL | _custom = Custom * _custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:346:15
+ --> $DIR/arithmetic_side_effects.rs:370:15
|
LL | _custom = &Custom * _custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:347:15
+ --> $DIR/arithmetic_side_effects.rs:371:15
|
LL | _custom = Custom + &Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:348:15
+ --> $DIR/arithmetic_side_effects.rs:372:15
|
LL | _custom = &Custom + Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:349:15
+ --> $DIR/arithmetic_side_effects.rs:373:15
|
LL | _custom = &Custom + &Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:352:10
+ --> $DIR/arithmetic_side_effects.rs:374:15
+ |
+LL | _custom = _custom >> _custom;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:375:15
+ |
+LL | _custom = _custom >> &_custom;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:376:15
+ |
+LL | _custom = Custom << _custom;
+ | ^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:377:15
+ |
+LL | _custom = &Custom << _custom;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:380:10
|
LL | _n = -_n;
| ^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:353:10
+ --> $DIR/arithmetic_side_effects.rs:381:10
|
LL | _n = -&_n;
| ^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:354:15
+ --> $DIR/arithmetic_side_effects.rs:382:15
|
LL | _custom = -_custom;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:355:15
+ --> $DIR/arithmetic_side_effects.rs:383:15
|
LL | _custom = -&_custom;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:364:5
+ --> $DIR/arithmetic_side_effects.rs:392:5
|
LL | 1 + i;
| ^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:365:5
+ --> $DIR/arithmetic_side_effects.rs:393:5
|
LL | i * 2;
| ^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:367:5
+ --> $DIR/arithmetic_side_effects.rs:394:5
+ |
+LL | 1 % i / 2;
+ | ^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:395:5
|
LL | i - 2 + 2 - i;
| ^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:368:5
+ --> $DIR/arithmetic_side_effects.rs:396:5
|
LL | -i;
| ^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:369:5
- |
-LL | i >> 1;
- | ^^^^^^
-
-error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:370:5
- |
-LL | i << 1;
- | ^^^^^^
-
-error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:379:5
+ --> $DIR/arithmetic_side_effects.rs:407:5
|
LL | i += 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:380:5
+ --> $DIR/arithmetic_side_effects.rs:408:5
|
LL | i -= 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:381:5
+ --> $DIR/arithmetic_side_effects.rs:409:5
|
LL | i *= 2;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:383:5
+ --> $DIR/arithmetic_side_effects.rs:411:5
|
LL | i /= 0;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:385:5
+ --> $DIR/arithmetic_side_effects.rs:413:5
|
LL | i /= var1;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:386:5
+ --> $DIR/arithmetic_side_effects.rs:414:5
|
LL | i /= var2;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:388:5
+ --> $DIR/arithmetic_side_effects.rs:416:5
|
LL | i %= 0;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:390:5
+ --> $DIR/arithmetic_side_effects.rs:418:5
|
LL | i %= var1;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:391:5
+ --> $DIR/arithmetic_side_effects.rs:419:5
|
LL | i %= var2;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:392:5
+ --> $DIR/arithmetic_side_effects.rs:429:5
|
-LL | i <<= 3;
- | ^^^^^^^
-
-error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:393:5
- |
-LL | i >>= 2;
- | ^^^^^^^
+LL | 10 / a
+ | ^^^^^^
-error: aborting due to 99 previous errors
+error: aborting due to 109 previous errors
diff --git a/src/tools/clippy/tests/ui/as_conversions.rs b/src/tools/clippy/tests/ui/as_conversions.rs
index ba4394def..c50d4088b 100644
--- a/src/tools/clippy/tests/ui/as_conversions.rs
+++ b/src/tools/clippy/tests/ui/as_conversions.rs
@@ -1,20 +1,15 @@
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::as_conversions)]
#![allow(clippy::borrow_as_ptr)]
-#[macro_use]
-extern crate macro_rules;
-
-fn with_external_macro() {
- as_conv_with_arg!(0u32 as u64);
- as_conv!();
-}
+extern crate proc_macros;
+use proc_macros::external;
fn main() {
let i = 0u32 as u64;
let j = &i as *const u64 as *mut u64;
- with_external_macro();
+ external!(0u32 as u64);
}
diff --git a/src/tools/clippy/tests/ui/as_conversions.stderr b/src/tools/clippy/tests/ui/as_conversions.stderr
index f5d59e1e5..54037a649 100644
--- a/src/tools/clippy/tests/ui/as_conversions.stderr
+++ b/src/tools/clippy/tests/ui/as_conversions.stderr
@@ -1,5 +1,5 @@
error: using a potentially dangerous silent `as` conversion
- --> $DIR/as_conversions.rs:15:13
+ --> $DIR/as_conversions.rs:10:13
|
LL | let i = 0u32 as u64;
| ^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | let i = 0u32 as u64;
= note: `-D clippy::as-conversions` implied by `-D warnings`
error: using a potentially dangerous silent `as` conversion
- --> $DIR/as_conversions.rs:17:13
+ --> $DIR/as_conversions.rs:12:13
|
LL | let j = &i as *const u64 as *mut u64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ 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:17:13
+ --> $DIR/as_conversions.rs:12:13
|
LL | let j = &i as *const u64 as *mut u64;
| ^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/async_yields_async.fixed b/src/tools/clippy/tests/ui/async_yields_async.fixed
index 3cf380d2b..579a63ea4 100644
--- a/src/tools/clippy/tests/ui/async_yields_async.fixed
+++ b/src/tools/clippy/tests/ui/async_yields_async.fixed
@@ -2,6 +2,7 @@
#![feature(lint_reasons)]
#![feature(async_closure)]
#![warn(clippy::async_yields_async)]
+#![allow(clippy::redundant_async_block)]
use core::future::Future;
use core::pin::Pin;
diff --git a/src/tools/clippy/tests/ui/async_yields_async.rs b/src/tools/clippy/tests/ui/async_yields_async.rs
index dd4131b60..5aec2fb50 100644
--- a/src/tools/clippy/tests/ui/async_yields_async.rs
+++ b/src/tools/clippy/tests/ui/async_yields_async.rs
@@ -2,6 +2,7 @@
#![feature(lint_reasons)]
#![feature(async_closure)]
#![warn(clippy::async_yields_async)]
+#![allow(clippy::redundant_async_block)]
use core::future::Future;
use core::pin::Pin;
diff --git a/src/tools/clippy/tests/ui/async_yields_async.stderr b/src/tools/clippy/tests/ui/async_yields_async.stderr
index 22ce1c6f6..7f7253483 100644
--- a/src/tools/clippy/tests/ui/async_yields_async.stderr
+++ b/src/tools/clippy/tests/ui/async_yields_async.stderr
@@ -1,5 +1,5 @@
error: an async construct yields a type which is itself awaitable
- --> $DIR/async_yields_async.rs:39:9
+ --> $DIR/async_yields_async.rs:40:9
|
LL | let _h = async {
| _____________________-
@@ -19,7 +19,7 @@ LL + }.await
|
error: an async construct yields a type which is itself awaitable
- --> $DIR/async_yields_async.rs:44:9
+ --> $DIR/async_yields_async.rs:45:9
|
LL | let _i = async {
| ____________________-
@@ -32,7 +32,7 @@ LL | | };
| |_____- outer async construct
error: an async construct yields a type which is itself awaitable
- --> $DIR/async_yields_async.rs:50:9
+ --> $DIR/async_yields_async.rs:51:9
|
LL | let _j = async || {
| ________________________-
@@ -51,7 +51,7 @@ LL + }.await
|
error: an async construct yields a type which is itself awaitable
- --> $DIR/async_yields_async.rs:55:9
+ --> $DIR/async_yields_async.rs:56:9
|
LL | let _k = async || {
| _______________________-
@@ -64,7 +64,7 @@ LL | | };
| |_____- outer async construct
error: an async construct yields a type which is itself awaitable
- --> $DIR/async_yields_async.rs:57:23
+ --> $DIR/async_yields_async.rs:58:23
|
LL | let _l = async || CustomFutureType;
| ^^^^^^^^^^^^^^^^
@@ -74,7 +74,7 @@ LL | let _l = async || CustomFutureType;
| help: consider awaiting this value: `CustomFutureType.await`
error: an async construct yields a type which is itself awaitable
- --> $DIR/async_yields_async.rs:63:9
+ --> $DIR/async_yields_async.rs:64:9
|
LL | let _m = async || {
| _______________________-
diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout
index c6acf24c2..eb3e5189c 100644
--- a/src/tools/clippy/tests/ui/author/blocks.stdout
+++ b/src/tools/clippy/tests/ui/author/blocks.stdout
@@ -43,11 +43,7 @@ if let ExprKind::Block(block, None) = expr.kind
if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind
&& let FnRetTy::DefaultReturn(_) = fn_decl.output
&& expr1 = &cx.tcx.hir().body(body_id).value
- && let ExprKind::Call(func, args) = expr1.kind
- && let ExprKind::Path(ref qpath) = func.kind
- && matches!(qpath, QPath::LangItem(LangItem::IdentityFuture, _))
- && args.len() == 1
- && let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind
+ && let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = expr1.kind
&& let FnRetTy::DefaultReturn(_) = fn_decl1.output
&& expr2 = &cx.tcx.hir().body(body_id1).value
&& let ExprKind::Block(block, None) = expr2.kind
diff --git a/src/tools/clippy/tests/ui/auxiliary/doc_unsafe_macros.rs b/src/tools/clippy/tests/ui/auxiliary/doc_unsafe_macros.rs
deleted file mode 100644
index 3d917e3dc..000000000
--- a/src/tools/clippy/tests/ui/auxiliary/doc_unsafe_macros.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-#[macro_export]
-macro_rules! undocd_unsafe {
- () => {
- pub unsafe fn oy_vey() {
- unimplemented!();
- }
- };
-}
-#[macro_export]
-macro_rules! undocd_safe {
- () => {
- pub fn vey_oy() {
- unimplemented!();
- }
- };
-}
diff --git a/src/tools/clippy/tests/ui/auxiliary/implicit_hasher_macros.rs b/src/tools/clippy/tests/ui/auxiliary/implicit_hasher_macros.rs
deleted file mode 100644
index 1eb77c531..000000000
--- a/src/tools/clippy/tests/ui/auxiliary/implicit_hasher_macros.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-#[macro_export]
-macro_rules! implicit_hasher_fn {
- () => {
- pub fn f(input: &HashMap<u32, u32>) {}
- };
-}
diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
index a13af5652..a9bb61451 100644
--- a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
@@ -3,21 +3,6 @@
//! 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> {
@@ -37,84 +22,6 @@ macro_rules! string_add {
}
#[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)
- };
-}
-
-#[macro_export]
-macro_rules! ref_arg_binding {
- () => {
- let ref _y = 42;
- };
-}
-
-#[macro_export]
-macro_rules! ref_arg_function {
- () => {
- fn fun_example(ref _x: usize) {}
- };
-}
-
-#[macro_export]
-macro_rules! as_conv_with_arg {
- (0u32 as u64) => {
- ()
- };
-}
-
-#[macro_export]
-macro_rules! as_conv {
- () => {
- 0u32 as u64
- };
-}
-
-#[macro_export]
-macro_rules! large_enum_variant {
- () => {
- enum LargeEnumInMacro {
- A(i32),
- B([i32; 8000]),
- }
- };
-}
-
-#[macro_export]
-macro_rules! field_reassign_with_default {
- () => {
- #[derive(Default)]
- struct A {
- pub i: i32,
- pub j: i64,
- }
- fn lint() {
- let mut a: A = Default::default();
- a.i = 42;
- a;
- }
- };
-}
-
-#[macro_export]
-macro_rules! default_numeric_fallback {
- () => {
- let x = 22;
- };
-}
-
-#[macro_export]
macro_rules! mut_mut {
() => {
let mut_mut_ty: &mut &mut u32 = &mut &mut 1u32;
@@ -122,49 +29,11 @@ macro_rules! mut_mut {
}
#[macro_export]
-macro_rules! ptr_as_ptr_cast {
- ($ptr: ident) => {
- $ptr as *const i32
- };
-}
-
-#[macro_export]
-macro_rules! manual_rem_euclid {
+macro_rules! issue_10421 {
() => {
- let value: i32 = 5;
- let _: i32 = ((value % 4) + 4) % 4;
- };
-}
-
-#[macro_export]
-macro_rules! equatable_if_let {
- ($a:ident) => {{ if let 2 = $a {} }};
-}
-
-#[macro_export]
-macro_rules! almost_complete_range {
- () => {
- let _ = 'a'..'z';
- let _ = 'A'..'Z';
- let _ = '0'..'9';
- };
-}
-
-#[macro_export]
-macro_rules! unsafe_macro {
- () => {
- unsafe {
- *core::ptr::null::<()>();
- *core::ptr::null::<()>();
- }
- };
-}
-
-#[macro_export]
-macro_rules! needless_lifetime {
- () => {
- fn needless_lifetime<'a>(x: &'a u8) -> &'a u8 {
- unimplemented!()
- }
+ let mut a = 1;
+ let mut b = 2;
+ a = b;
+ b = a;
};
}
diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_use_helper.rs b/src/tools/clippy/tests/ui/auxiliary/macro_use_helper.rs
index ecb55d8cb..7ed8a28db 100644
--- a/src/tools/clippy/tests/ui/auxiliary/macro_use_helper.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/macro_use_helper.rs
@@ -13,7 +13,7 @@ pub mod inner {
// RE-EXPORT
// this will stick in `inner` module
- pub use macro_rules::foofoo;
+ pub use macro_rules::mut_mut;
pub use macro_rules::try_err;
pub mod nested {
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_with_span.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_with_span.rs
deleted file mode 100644
index 8ea631f2b..000000000
--- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_with_span.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-// compile-flags: --emit=link
-// no-prefer-dynamic
-
-#![crate_type = "proc-macro"]
-
-extern crate proc_macro;
-
-use proc_macro::{token_stream::IntoIter, Group, Span, TokenStream, TokenTree};
-
-#[proc_macro]
-pub fn with_span(input: TokenStream) -> TokenStream {
- let mut iter = input.into_iter();
- let span = iter.next().unwrap().span();
- let mut res = TokenStream::new();
- write_with_span(span, iter, &mut res);
- res
-}
-
-fn write_with_span(s: Span, input: IntoIter, out: &mut TokenStream) {
- for mut tt in input {
- if let TokenTree::Group(g) = tt {
- let mut stream = TokenStream::new();
- write_with_span(s, g.stream().into_iter(), &mut stream);
- let mut group = Group::new(g.delimiter(), stream);
- group.set_span(s);
- out.extend([TokenTree::Group(group)]);
- } else {
- tt.set_span(s);
- out.extend([tt]);
- }
- }
-}
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs
new file mode 100644
index 000000000..3d5beab1e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs
@@ -0,0 +1,474 @@
+// compile-flags: --emit=link
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+#![feature(let_chains)]
+#![feature(proc_macro_span)]
+#![allow(dead_code)]
+
+extern crate proc_macro;
+
+use core::mem;
+use proc_macro::{
+ token_stream::IntoIter,
+ Delimiter::{self, Brace, Parenthesis},
+ Group, Ident, Literal, Punct,
+ Spacing::{self, Alone, Joint},
+ Span, TokenStream, TokenTree as TT,
+};
+
+type Result<T> = core::result::Result<T, TokenStream>;
+
+/// Make a `compile_error!` pointing to the given span.
+fn make_error(msg: &str, span: Span) -> TokenStream {
+ TokenStream::from_iter([
+ TT::Ident(Ident::new("compile_error", span)),
+ TT::Punct(punct_with_span('!', Alone, span)),
+ TT::Group({
+ let mut msg = Literal::string(msg);
+ msg.set_span(span);
+ group_with_span(Parenthesis, TokenStream::from_iter([TT::Literal(msg)]), span)
+ }),
+ ])
+}
+
+fn expect_tt<T>(tt: Option<TT>, f: impl FnOnce(TT) -> Option<T>, expected: &str, span: Span) -> Result<T> {
+ match tt {
+ None => Err(make_error(
+ &format!("unexpected end of input, expected {expected}"),
+ span,
+ )),
+ Some(tt) => {
+ let span = tt.span();
+ match f(tt) {
+ Some(x) => Ok(x),
+ None => Err(make_error(&format!("unexpected token, expected {expected}"), span)),
+ }
+ },
+ }
+}
+
+fn punct_with_span(c: char, spacing: Spacing, span: Span) -> Punct {
+ let mut p = Punct::new(c, spacing);
+ p.set_span(span);
+ p
+}
+
+fn group_with_span(delimiter: Delimiter, stream: TokenStream, span: Span) -> Group {
+ let mut g = Group::new(delimiter, stream);
+ g.set_span(span);
+ g
+}
+
+/// Token used to escape the following token from the macro's span rules.
+const ESCAPE_CHAR: char = '$';
+
+/// Takes a single token followed by a sequence of tokens. Returns the sequence of tokens with their
+/// span set to that of the first token. Tokens may be escaped with either `#ident` or `#(tokens)`.
+#[proc_macro]
+pub fn with_span(input: TokenStream) -> TokenStream {
+ let mut iter = input.into_iter();
+ let span = iter.next().unwrap().span();
+ let mut res = TokenStream::new();
+ if let Err(e) = write_with_span(span, iter, &mut res) {
+ e
+ } else {
+ res
+ }
+}
+
+/// Takes a sequence of tokens and return the tokens with the span set such that they appear to be
+/// from an external macro. Tokens may be escaped with either `#ident` or `#(tokens)`.
+#[proc_macro]
+pub fn external(input: TokenStream) -> TokenStream {
+ let mut res = TokenStream::new();
+ if let Err(e) = write_with_span(Span::mixed_site(), input.into_iter(), &mut res) {
+ e
+ } else {
+ res
+ }
+}
+
+/// Copies all the tokens, replacing all their spans with the given span. Tokens can be escaped
+/// either by `#ident` or `#(tokens)`.
+fn write_with_span(s: Span, mut input: IntoIter, out: &mut TokenStream) -> Result<()> {
+ while let Some(tt) = input.next() {
+ match tt {
+ TT::Punct(p) if p.as_char() == ESCAPE_CHAR => {
+ expect_tt(
+ input.next(),
+ |tt| match tt {
+ tt @ (TT::Ident(_) | TT::Literal(_)) => {
+ out.extend([tt]);
+ Some(())
+ },
+ TT::Punct(mut p) if p.as_char() == ESCAPE_CHAR => {
+ p.set_span(s);
+ out.extend([TT::Punct(p)]);
+ Some(())
+ },
+ TT::Group(g) if g.delimiter() == Parenthesis => {
+ out.extend([TT::Group(group_with_span(Delimiter::None, g.stream(), g.span()))]);
+ Some(())
+ },
+ _ => None,
+ },
+ "an ident, a literal, or parenthesized tokens",
+ p.span(),
+ )?;
+ },
+ TT::Group(g) => {
+ let mut stream = TokenStream::new();
+ write_with_span(s, g.stream().into_iter(), &mut stream)?;
+ out.extend([TT::Group(group_with_span(g.delimiter(), stream, s))]);
+ },
+ mut tt => {
+ tt.set_span(s);
+ out.extend([tt]);
+ },
+ }
+ }
+ Ok(())
+}
+
+/// Within the item this attribute is attached to, an `inline!` macro is available which expands the
+/// contained tokens as though they came from a macro expansion.
+///
+/// Within the `inline!` macro, any token preceded by `$` is passed as though it were an argument
+/// with an automatically chosen fragment specifier. `$ident` will be passed as `ident`, `$1` or
+/// `$"literal"` will be passed as `literal`, `$'lt` will be passed as `lifetime`, and `$(...)` will
+/// pass the contained tokens as a `tt` sequence (the wrapping parenthesis are removed). If another
+/// specifier is required it can be specified within parenthesis like `$(@expr ...)`. This will
+/// expand the remaining tokens as a single argument.
+///
+/// Multiple `inline!` macros may be nested within each other. This will expand as nested macro
+/// calls. However, any arguments will be passed as though they came from the outermost context.
+#[proc_macro_attribute]
+pub fn inline_macros(args: TokenStream, input: TokenStream) -> TokenStream {
+ let mut args = args.into_iter();
+ let mac_name = match args.next() {
+ Some(TT::Ident(name)) => Some(name),
+ Some(tt) => {
+ return make_error(
+ "unexpected argument, expected either an ident or no arguments",
+ tt.span(),
+ );
+ },
+ None => None,
+ };
+ if let Some(tt) = args.next() {
+ return make_error(
+ "unexpected argument, expected either an ident or no arguments",
+ tt.span(),
+ );
+ };
+
+ let mac_name = if let Some(mac_name) = mac_name {
+ Ident::new(&format!("__inline_mac_{mac_name}"), Span::call_site())
+ } else {
+ let mut input = match LookaheadIter::new(input.clone().into_iter()) {
+ Some(x) => x,
+ None => return input,
+ };
+ loop {
+ match input.next() {
+ None => break Ident::new("__inline_mac", Span::call_site()),
+ Some(TT::Ident(kind)) => match &*kind.to_string() {
+ "impl" => break Ident::new("__inline_mac_impl", Span::call_site()),
+ kind @ ("struct" | "enum" | "union" | "fn" | "mod" | "trait" | "type" | "const" | "static") => {
+ if let TT::Ident(name) = &input.tt {
+ break Ident::new(&format!("__inline_mac_{kind}_{name}"), Span::call_site());
+ } else {
+ break Ident::new(&format!("__inline_mac_{kind}"), Span::call_site());
+ }
+ },
+ _ => {},
+ },
+ _ => {},
+ }
+ }
+ };
+
+ let mut expander = Expander::default();
+ let mut mac = MacWriter::new(mac_name);
+ if let Err(e) = expander.expand(input.into_iter(), &mut mac) {
+ return e;
+ }
+ let mut out = TokenStream::new();
+ mac.finish(&mut out);
+ out.extend(expander.expn);
+ out
+}
+
+/// Wraps a `TokenStream` iterator with a single token lookahead.
+struct LookaheadIter {
+ tt: TT,
+ iter: IntoIter,
+}
+impl LookaheadIter {
+ fn new(mut iter: IntoIter) -> Option<Self> {
+ iter.next().map(|tt| Self { tt, iter })
+ }
+
+ /// Get's the lookahead token, replacing it with the next token in the stream.
+ /// Note: If there isn't a next token, this will not return the lookahead token.
+ fn next(&mut self) -> Option<TT> {
+ self.iter.next().map(|tt| mem::replace(&mut self.tt, tt))
+ }
+}
+
+/// Builds the macro used to implement all the `inline!` macro calls.
+struct MacWriter {
+ name: Ident,
+ macros: TokenStream,
+ next_idx: usize,
+}
+impl MacWriter {
+ fn new(name: Ident) -> Self {
+ Self {
+ name,
+ macros: TokenStream::new(),
+ next_idx: 0,
+ }
+ }
+
+ /// Inserts a new `inline!` call.
+ fn insert(&mut self, name_span: Span, bang_span: Span, body: Group, expander: &mut Expander) -> Result<()> {
+ let idx = self.next_idx;
+ self.next_idx += 1;
+
+ let mut inner = Expander::for_arm(idx);
+ inner.expand(body.stream().into_iter(), self)?;
+ let new_arm = inner.arm.unwrap();
+
+ self.macros.extend([
+ TT::Group(Group::new(Parenthesis, new_arm.args_def)),
+ TT::Punct(Punct::new('=', Joint)),
+ TT::Punct(Punct::new('>', Alone)),
+ TT::Group(Group::new(Parenthesis, inner.expn)),
+ TT::Punct(Punct::new(';', Alone)),
+ ]);
+
+ expander.expn.extend([
+ TT::Ident({
+ let mut name = self.name.clone();
+ name.set_span(name_span);
+ name
+ }),
+ TT::Punct(punct_with_span('!', Alone, bang_span)),
+ ]);
+ let mut call_body = TokenStream::from_iter([TT::Literal(Literal::usize_unsuffixed(idx))]);
+ if let Some(arm) = expander.arm.as_mut() {
+ if !new_arm.args.is_empty() {
+ arm.add_sub_args(new_arm.args, &mut call_body);
+ }
+ } else {
+ call_body.extend(new_arm.args);
+ }
+ let mut g = Group::new(body.delimiter(), call_body);
+ g.set_span(body.span());
+ expander.expn.extend([TT::Group(g)]);
+ Ok(())
+ }
+
+ /// Creates the macro definition.
+ fn finish(self, out: &mut TokenStream) {
+ if self.next_idx != 0 {
+ out.extend([
+ TT::Ident(Ident::new("macro_rules", Span::call_site())),
+ TT::Punct(Punct::new('!', Alone)),
+ TT::Ident(self.name),
+ TT::Group(Group::new(Brace, self.macros)),
+ ])
+ }
+ }
+}
+
+struct MacroArm {
+ args_def: TokenStream,
+ args: Vec<TT>,
+}
+impl MacroArm {
+ fn add_single_arg_def(&mut self, kind: &str, dollar_span: Span, arg_span: Span, out: &mut TokenStream) {
+ let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site());
+ self.args_def.extend([
+ TT::Punct(Punct::new('$', Alone)),
+ TT::Ident(name.clone()),
+ TT::Punct(Punct::new(':', Alone)),
+ TT::Ident(Ident::new(kind, Span::call_site())),
+ ]);
+ name.set_span(arg_span);
+ out.extend([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]);
+ }
+
+ fn add_parenthesized_arg_def(&mut self, kind: Ident, dollar_span: Span, arg_span: Span, out: &mut TokenStream) {
+ let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site());
+ self.args_def.extend([TT::Group(Group::new(
+ Parenthesis,
+ TokenStream::from_iter([
+ TT::Punct(Punct::new('$', Alone)),
+ TT::Ident(name.clone()),
+ TT::Punct(Punct::new(':', Alone)),
+ TT::Ident(kind),
+ ]),
+ ))]);
+ name.set_span(arg_span);
+ out.extend([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]);
+ }
+
+ fn add_multi_arg_def(&mut self, dollar_span: Span, arg_span: Span, out: &mut TokenStream) {
+ let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site());
+ self.args_def.extend([TT::Group(Group::new(
+ Parenthesis,
+ TokenStream::from_iter([
+ TT::Punct(Punct::new('$', Alone)),
+ TT::Group(Group::new(
+ Parenthesis,
+ TokenStream::from_iter([
+ TT::Punct(Punct::new('$', Alone)),
+ TT::Ident(name.clone()),
+ TT::Punct(Punct::new(':', Alone)),
+ TT::Ident(Ident::new("tt", Span::call_site())),
+ ]),
+ )),
+ TT::Punct(Punct::new('*', Alone)),
+ ]),
+ ))]);
+ name.set_span(arg_span);
+ out.extend([
+ TT::Punct(punct_with_span('$', Alone, dollar_span)),
+ TT::Group(group_with_span(
+ Parenthesis,
+ TokenStream::from_iter([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]),
+ dollar_span,
+ )),
+ TT::Punct(punct_with_span('*', Alone, dollar_span)),
+ ]);
+ }
+
+ fn add_arg(&mut self, dollar_span: Span, tt: TT, input: &mut IntoIter, out: &mut TokenStream) -> Result<()> {
+ match tt {
+ TT::Punct(p) if p.as_char() == ESCAPE_CHAR => out.extend([TT::Punct(p)]),
+ TT::Punct(p) if p.as_char() == '\'' && p.spacing() == Joint => {
+ let lt_name = expect_tt(
+ input.next(),
+ |tt| match tt {
+ TT::Ident(x) => Some(x),
+ _ => None,
+ },
+ "lifetime name",
+ p.span(),
+ )?;
+ let arg_span = p.span().join(lt_name.span()).unwrap_or(p.span());
+ self.add_single_arg_def("lifetime", dollar_span, arg_span, out);
+ self.args.extend([TT::Punct(p), TT::Ident(lt_name)]);
+ },
+ TT::Ident(x) => {
+ self.add_single_arg_def("ident", dollar_span, x.span(), out);
+ self.args.push(TT::Ident(x));
+ },
+ TT::Literal(x) => {
+ self.add_single_arg_def("literal", dollar_span, x.span(), out);
+ self.args.push(TT::Literal(x));
+ },
+ TT::Group(g) if g.delimiter() == Parenthesis => {
+ let mut inner = g.stream().into_iter();
+ if let Some(TT::Punct(p)) = inner.next()
+ && p.as_char() == '@'
+ {
+ let kind = expect_tt(
+ inner.next(),
+ |tt| match tt {
+ TT::Ident(kind) => Some(kind),
+ _ => None,
+ },
+ "a macro fragment specifier",
+ p.span(),
+ )?;
+ self.add_parenthesized_arg_def(kind, dollar_span, g.span(), out);
+ self.args.push(TT::Group(group_with_span(Parenthesis, inner.collect(), g.span())))
+ } else {
+ self.add_multi_arg_def(dollar_span, g.span(), out);
+ self.args.push(TT::Group(g));
+ }
+ },
+ tt => return Err(make_error("unsupported escape", tt.span())),
+ };
+ Ok(())
+ }
+
+ fn add_sub_args(&mut self, args: Vec<TT>, out: &mut TokenStream) {
+ self.add_multi_arg_def(Span::call_site(), Span::call_site(), out);
+ self.args
+ .extend([TT::Group(Group::new(Parenthesis, TokenStream::from_iter(args)))]);
+ }
+}
+
+#[derive(Default)]
+struct Expander {
+ arm: Option<MacroArm>,
+ expn: TokenStream,
+}
+impl Expander {
+ fn for_arm(idx: usize) -> Self {
+ Self {
+ arm: Some(MacroArm {
+ args_def: TokenStream::from_iter([TT::Literal(Literal::usize_unsuffixed(idx))]),
+ args: Vec::new(),
+ }),
+ expn: TokenStream::new(),
+ }
+ }
+
+ fn write_tt(&mut self, tt: TT, mac: &mut MacWriter) -> Result<()> {
+ match tt {
+ TT::Group(g) => {
+ let outer = mem::take(&mut self.expn);
+ self.expand(g.stream().into_iter(), mac)?;
+ let inner = mem::replace(&mut self.expn, outer);
+ self.expn
+ .extend([TT::Group(group_with_span(g.delimiter(), inner, g.span()))]);
+ },
+ tt => self.expn.extend([tt]),
+ }
+ Ok(())
+ }
+
+ fn expand(&mut self, input: IntoIter, mac: &mut MacWriter) -> Result<()> {
+ let Some(mut input) = LookaheadIter::new(input) else {
+ return Ok(());
+ };
+ while let Some(tt) = input.next() {
+ if let TT::Punct(p) = &tt
+ && p.as_char() == ESCAPE_CHAR
+ && let Some(arm) = self.arm.as_mut()
+ {
+ arm.add_arg(p.span(), mem::replace(&mut input.tt, tt), &mut input.iter, &mut self.expn)?;
+ if input.next().is_none() {
+ return Ok(());
+ }
+ } else if let TT::Punct(p) = &input.tt
+ && p.as_char() == '!'
+ && let TT::Ident(name) = &tt
+ && name.to_string() == "inline"
+ {
+ let g = expect_tt(
+ input.iter.next(),
+ |tt| match tt {
+ TT::Group(g) => Some(g),
+ _ => None,
+ },
+ "macro arguments",
+ p.span(),
+ )?;
+ mac.insert(name.span(), p.span(), g, self)?;
+ if input.next().is_none() {
+ return Ok(());
+ }
+ } else {
+ self.write_tt(tt, mac)?;
+ }
+ }
+ self.write_tt(input.tt, mac)
+ }
+}
diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs
index f13733af3..b03c21262 100644
--- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs
+++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs
@@ -1,5 +1,5 @@
// this file solely exists to test constants defined in foreign crates.
-// As the most common case is the `http` crate, it replicates `http::HeadewrName`'s structure.
+// As the most common case is the `http` crate, it replicates `http::HeaderName`'s structure.
#![allow(clippy::declare_interior_mutable_const)]
#![allow(unused_tuple_struct_fields)]
diff --git a/src/tools/clippy/tests/ui/boxed_local.rs b/src/tools/clippy/tests/ui/boxed_local.rs
index 4639f00a8..79b6d33fc 100644
--- a/src/tools/clippy/tests/ui/boxed_local.rs
+++ b/src/tools/clippy/tests/ui/boxed_local.rs
@@ -1,4 +1,3 @@
-#![feature(box_syntax)]
#![feature(lint_reasons)]
#![allow(
clippy::borrowed_box,
@@ -34,7 +33,7 @@ fn ok_box_trait(boxed_trait: &Box<dyn Z>) {
}
fn warn_call() {
- let x = box A;
+ let x = Box::new(A);
x.foo();
}
@@ -43,41 +42,41 @@ fn warn_arg(x: Box<A>) {
}
fn nowarn_closure_arg() {
- let x = Some(box A);
+ let x = Some(Box::new(A));
x.map_or((), |x| take_ref(&x));
}
fn warn_rename_call() {
- let x = box A;
+ let x = Box::new(A);
let y = x;
y.foo(); // via autoderef
}
fn warn_notuse() {
- let bz = box A;
+ let bz = Box::new(A);
}
fn warn_pass() {
- let bz = box A;
+ let bz = Box::new(A);
take_ref(&bz); // via deref coercion
}
fn nowarn_return() -> Box<A> {
- box A // moved out, "escapes"
+ Box::new(A) // moved out, "escapes"
}
fn nowarn_move() {
- let bx = box A;
+ let bx = Box::new(A);
drop(bx) // moved in, "escapes"
}
fn nowarn_call() {
- let bx = box A;
+ let bx = Box::new(A);
bx.clone(); // method only available to Box, not via autoderef
}
fn nowarn_pass() {
- let bx = box A;
+ let bx = Box::new(A);
take_box(&bx); // fn needs &Box
}
@@ -86,30 +85,20 @@ fn take_ref(x: &A) {}
fn nowarn_ref_take() {
// false positive, should actually warn
- let x = box A;
+ let x = Box::new(A);
let y = &x;
take_box(y);
}
fn nowarn_match() {
- let x = box A; // moved into a match
+ let x = Box::new(A); // moved into a match
match x {
y => drop(y),
}
}
fn warn_match() {
- let x = box A;
- match &x {
- // not moved
- y => (),
- }
-}
-
-fn nowarn_large_array() {
- // should not warn, is large array
- // and should not be on stack
- let x = box [1; 10000];
+ let x = Box::new(A);
match &x {
// not moved
y => (),
diff --git a/src/tools/clippy/tests/ui/boxed_local.stderr b/src/tools/clippy/tests/ui/boxed_local.stderr
index 9036529f3..10d78fbc0 100644
--- a/src/tools/clippy/tests/ui/boxed_local.stderr
+++ b/src/tools/clippy/tests/ui/boxed_local.stderr
@@ -1,5 +1,5 @@
error: local variable doesn't need to be boxed here
- --> $DIR/boxed_local.rs:41:13
+ --> $DIR/boxed_local.rs:40:13
|
LL | fn warn_arg(x: Box<A>) {
| ^
@@ -7,19 +7,19 @@ 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/boxed_local.rs:132:12
+ --> $DIR/boxed_local.rs:121:12
|
LL | pub fn new(_needs_name: Box<PeekableSeekable<&()>>) -> () {}
| ^^^^^^^^^^^
error: local variable doesn't need to be boxed here
- --> $DIR/boxed_local.rs:196:44
+ --> $DIR/boxed_local.rs:185:44
|
LL | fn default_impl_x(self: Box<Self>, x: Box<u32>) -> u32 {
| ^
error: local variable doesn't need to be boxed here
- --> $DIR/boxed_local.rs:203:16
+ --> $DIR/boxed_local.rs:192:16
|
LL | fn foo(x: Box<u32>) {}
| ^
diff --git a/src/tools/clippy/tests/ui/cast.rs b/src/tools/clippy/tests/ui/cast.rs
index 8b2673c2a..a86b85706 100644
--- a/src/tools/clippy/tests/ui/cast.rs
+++ b/src/tools/clippy/tests/ui/cast.rs
@@ -29,6 +29,12 @@ fn main() {
1f64 as isize;
1f64 as usize;
1f32 as u32 as u16;
+ {
+ let _x: i8 = 1i32 as _;
+ 1f32 as i32;
+ 1f64 as i32;
+ 1f32 as u8;
+ }
// Test clippy::cast_possible_wrap
1u8 as i8;
1u16 as i16;
diff --git a/src/tools/clippy/tests/ui/cast.stderr b/src/tools/clippy/tests/ui/cast.stderr
index 451078de2..65ecf1aa3 100644
--- a/src/tools/clippy/tests/ui/cast.stderr
+++ b/src/tools/clippy/tests/ui/cast.stderr
@@ -44,10 +44,6 @@ LL | 1f32 as i32;
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
= note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
-help: ... or use `try_from` and handle the error accordingly
- |
-LL | i32::try_from(1f32);
- | ~~~~~~~~~~~~~~~~~~~
error: casting `f32` to `u32` may truncate the value
--> $DIR/cast.rs:25:5
@@ -56,10 +52,6 @@ LL | 1f32 as u32;
| ^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
-help: ... or use `try_from` and handle the error accordingly
- |
-LL | u32::try_from(1f32);
- | ~~~~~~~~~~~~~~~~~~~
error: casting `f32` to `u32` may lose the sign of the value
--> $DIR/cast.rs:25:5
@@ -76,10 +68,6 @@ LL | 1f64 as f32;
| ^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
-help: ... or use `try_from` and handle the error accordingly
- |
-LL | f32::try_from(1f64);
- | ~~~~~~~~~~~~~~~~~~~
error: casting `i32` to `i8` may truncate the value
--> $DIR/cast.rs:27:5
@@ -112,10 +100,6 @@ LL | 1f64 as isize;
| ^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
-help: ... or use `try_from` and handle the error accordingly
- |
-LL | isize::try_from(1f64);
- | ~~~~~~~~~~~~~~~~~~~~~
error: casting `f64` to `usize` may truncate the value
--> $DIR/cast.rs:30:5
@@ -124,10 +108,6 @@ LL | 1f64 as usize;
| ^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
-help: ... or use `try_from` and handle the error accordingly
- |
-LL | usize::try_from(1f64);
- | ~~~~~~~~~~~~~~~~~~~~~
error: casting `f64` to `usize` may lose the sign of the value
--> $DIR/cast.rs:30:5
@@ -154,10 +134,6 @@ LL | 1f32 as u32 as u16;
| ^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
-help: ... or use `try_from` and handle the error accordingly
- |
-LL | u32::try_from(1f32) as u16;
- | ~~~~~~~~~~~~~~~~~~~
error: casting `f32` to `u32` may lose the sign of the value
--> $DIR/cast.rs:31:5
@@ -165,8 +141,50 @@ error: casting `f32` to `u32` may lose the sign of the value
LL | 1f32 as u32 as u16;
| ^^^^^^^^^^^
+error: casting `i32` to `i8` may truncate the value
+ --> $DIR/cast.rs:33:22
+ |
+LL | let _x: i8 = 1i32 as _;
+ | ^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | let _x: i8 = 1i32.try_into();
+ | ~~~~~~~~~~~~~~~
+
+error: casting `f32` to `i32` may truncate the value
+ --> $DIR/cast.rs:34:9
+ |
+LL | 1f32 as i32;
+ | ^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+
+error: casting `f64` to `i32` may truncate the value
+ --> $DIR/cast.rs:35:9
+ |
+LL | 1f64 as i32;
+ | ^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+
+error: casting `f32` to `u8` may truncate the value
+ --> $DIR/cast.rs:36:9
+ |
+LL | 1f32 as u8;
+ | ^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+
+error: casting `f32` to `u8` may lose the sign of the value
+ --> $DIR/cast.rs:36:9
+ |
+LL | 1f32 as u8;
+ | ^^^^^^^^^^
+
error: casting `u8` to `i8` may wrap around the value
- --> $DIR/cast.rs:33:5
+ --> $DIR/cast.rs:39:5
|
LL | 1u8 as i8;
| ^^^^^^^^^
@@ -174,43 +192,43 @@ 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:34:5
+ --> $DIR/cast.rs:40:5
|
LL | 1u16 as i16;
| ^^^^^^^^^^^
error: casting `u32` to `i32` may wrap around the value
- --> $DIR/cast.rs:35:5
+ --> $DIR/cast.rs:41:5
|
LL | 1u32 as i32;
| ^^^^^^^^^^^
error: casting `u64` to `i64` may wrap around the value
- --> $DIR/cast.rs:36:5
+ --> $DIR/cast.rs:42:5
|
LL | 1u64 as i64;
| ^^^^^^^^^^^
error: casting `usize` to `isize` may wrap around the value
- --> $DIR/cast.rs:37:5
+ --> $DIR/cast.rs:43:5
|
LL | 1usize as isize;
| ^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value
- --> $DIR/cast.rs:40:5
+ --> $DIR/cast.rs:46:5
|
LL | -1i32 as u32;
| ^^^^^^^^^^^^
error: casting `isize` to `usize` may lose the sign of the value
- --> $DIR/cast.rs:42:5
+ --> $DIR/cast.rs:48:5
|
LL | -1isize as usize;
| ^^^^^^^^^^^^^^^^
error: casting `i64` to `i8` may truncate the value
- --> $DIR/cast.rs:109:5
+ --> $DIR/cast.rs:115:5
|
LL | (-99999999999i64).min(1) as i8; // should be linted because signed
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -222,7 +240,7 @@ LL | i8::try_from((-99999999999i64).min(1)); // should be linted because sig
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: casting `u64` to `u8` may truncate the value
- --> $DIR/cast.rs:121:5
+ --> $DIR/cast.rs:127:5
|
LL | 999999u64.clamp(0, 256) as u8; // should still be linted
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -234,7 +252,7 @@ LL | u8::try_from(999999u64.clamp(0, 256)); // should still be linted
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: casting `main::E2` to `u8` may truncate the value
- --> $DIR/cast.rs:142:21
+ --> $DIR/cast.rs:148:21
|
LL | let _ = self as u8;
| ^^^^^^^^^^
@@ -246,7 +264,7 @@ LL | let _ = u8::try_from(self);
| ~~~~~~~~~~~~~~~~~~
error: casting `main::E2::B` to `u8` will truncate the value
- --> $DIR/cast.rs:143:21
+ --> $DIR/cast.rs:149:21
|
LL | let _ = Self::B as u8;
| ^^^^^^^^^^^^^
@@ -254,7 +272,7 @@ LL | let _ = Self::B as u8;
= note: `-D clippy::cast-enum-truncation` implied by `-D warnings`
error: casting `main::E5` to `i8` may truncate the value
- --> $DIR/cast.rs:179:21
+ --> $DIR/cast.rs:185:21
|
LL | let _ = self as i8;
| ^^^^^^^^^^
@@ -266,13 +284,13 @@ LL | let _ = i8::try_from(self);
| ~~~~~~~~~~~~~~~~~~
error: casting `main::E5::A` to `i8` will truncate the value
- --> $DIR/cast.rs:180:21
+ --> $DIR/cast.rs:186:21
|
LL | let _ = Self::A as i8;
| ^^^^^^^^^^^^^
error: casting `main::E6` to `i16` may truncate the value
- --> $DIR/cast.rs:194:21
+ --> $DIR/cast.rs:200:21
|
LL | let _ = self as i16;
| ^^^^^^^^^^^
@@ -284,7 +302,7 @@ LL | let _ = i16::try_from(self);
| ~~~~~~~~~~~~~~~~~~~
error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers
- --> $DIR/cast.rs:209:21
+ --> $DIR/cast.rs:215:21
|
LL | let _ = self as usize;
| ^^^^^^^^^^^^^
@@ -296,7 +314,7 @@ LL | let _ = usize::try_from(self);
| ~~~~~~~~~~~~~~~~~~~~~
error: casting `main::E10` to `u16` may truncate the value
- --> $DIR/cast.rs:250:21
+ --> $DIR/cast.rs:256:21
|
LL | let _ = self as u16;
| ^^^^^^^^^^^
@@ -308,7 +326,7 @@ LL | let _ = u16::try_from(self);
| ~~~~~~~~~~~~~~~~~~~
error: casting `u32` to `u8` may truncate the value
- --> $DIR/cast.rs:258:13
+ --> $DIR/cast.rs:264:13
|
LL | let c = (q >> 16) as u8;
| ^^^^^^^^^^^^^^^
@@ -316,11 +334,11 @@ LL | let c = (q >> 16) as u8;
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
-LL | let c = u8::try_from((q >> 16));
- | ~~~~~~~~~~~~~~~~~~~~~~~
+LL | let c = u8::try_from(q >> 16);
+ | ~~~~~~~~~~~~~~~~~~~~~
error: casting `u32` to `u8` may truncate the value
- --> $DIR/cast.rs:261:13
+ --> $DIR/cast.rs:267:13
|
LL | let c = (q / 1000) as u8;
| ^^^^^^^^^^^^^^^^
@@ -328,8 +346,8 @@ LL | let c = (q / 1000) as u8;
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
-LL | let c = u8::try_from((q / 1000));
- | ~~~~~~~~~~~~~~~~~~~~~~~~
+LL | let c = u8::try_from(q / 1000);
+ | ~~~~~~~~~~~~~~~~~~~~~~
-error: aborting due to 36 previous errors
+error: aborting due to 41 previous errors
diff --git a/src/tools/clippy/tests/ui/clear_with_drain.fixed b/src/tools/clippy/tests/ui/clear_with_drain.fixed
new file mode 100644
index 000000000..2d9545eee
--- /dev/null
+++ b/src/tools/clippy/tests/ui/clear_with_drain.fixed
@@ -0,0 +1,358 @@
+// run-rustfix
+#![allow(unused)]
+#![warn(clippy::clear_with_drain)]
+
+use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
+
+fn vec_range() {
+ // Do not lint because iterator is assigned
+ let mut v = vec![1, 2, 3];
+ let iter = v.drain(0..v.len());
+
+ // Do not lint because iterator is used
+ let mut v = vec![1, 2, 3];
+ let n = v.drain(0..v.len()).count();
+
+ // Do not lint because iterator is assigned and used
+ let mut v = vec![1, 2, 3];
+ let iter = v.drain(usize::MIN..v.len());
+ let n = iter.count();
+
+ // Do lint
+ let mut v = vec![1, 2, 3];
+ v.clear();
+
+ // Do lint
+ let mut v = vec![1, 2, 3];
+ v.clear();
+}
+
+fn vec_range_from() {
+ // Do not lint because iterator is assigned
+ let mut v = vec![1, 2, 3];
+ let iter = v.drain(0..);
+
+ // Do not lint because iterator is assigned and used
+ let mut v = vec![1, 2, 3];
+ let mut iter = v.drain(0..);
+ let next = iter.next();
+
+ // Do not lint because iterator is used
+ let mut v = vec![1, 2, 3];
+ let next = v.drain(usize::MIN..).next();
+
+ // Do lint
+ let mut v = vec![1, 2, 3];
+ v.clear();
+
+ // Do lint
+ let mut v = vec![1, 2, 3];
+ v.clear();
+}
+
+fn vec_range_full() {
+ // Do not lint because iterator is assigned
+ let mut v = vec![1, 2, 3];
+ let iter = v.drain(..);
+
+ // Do not lint because iterator is used
+ let mut v = vec![1, 2, 3];
+ for x in v.drain(..) {
+ let y = format!("x = {x}");
+ }
+
+ // Do lint
+ let mut v = vec![1, 2, 3];
+ v.clear();
+}
+
+fn vec_range_to() {
+ // Do not lint because iterator is assigned
+ let mut v = vec![1, 2, 3];
+ let iter = v.drain(..v.len());
+
+ // Do not lint because iterator is assigned and used
+ let mut v = vec![1, 2, 3];
+ let iter = v.drain(..v.len());
+ for x in iter {
+ let y = format!("x = {x}");
+ }
+
+ // Do lint
+ let mut v = vec![1, 2, 3];
+ v.clear();
+}
+
+fn vec_partial_drains() {
+ // Do not lint any of these because the ranges are not full
+
+ let mut v = vec![1, 2, 3];
+ v.drain(1..);
+ let mut v = vec![1, 2, 3];
+ v.drain(1..).max();
+
+ let mut v = vec![1, 2, 3];
+ v.drain(..v.len() - 1);
+ let mut v = vec![1, 2, 3];
+ v.drain(..v.len() - 1).min();
+
+ let mut v = vec![1, 2, 3];
+ v.drain(1..v.len() - 1);
+ let mut v = vec![1, 2, 3];
+ let w: Vec<i8> = v.drain(1..v.len() - 1).collect();
+}
+
+fn vec_deque_range() {
+ // Do not lint because iterator is assigned
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let iter = deque.drain(0..deque.len());
+
+ // Do not lint because iterator is used
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let n = deque.drain(0..deque.len()).count();
+
+ // Do not lint because iterator is assigned and used
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let iter = deque.drain(usize::MIN..deque.len());
+ let n = iter.count();
+
+ // Do lint
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.clear();
+
+ // Do lint
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.clear();
+}
+
+fn vec_deque_range_from() {
+ // Do not lint because iterator is assigned
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let iter = deque.drain(0..);
+
+ // Do not lint because iterator is assigned and used
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let mut iter = deque.drain(0..);
+ let next = iter.next();
+
+ // Do not lint because iterator is used
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let next = deque.drain(usize::MIN..).next();
+
+ // Do lint
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.clear();
+
+ // Do lint
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.clear();
+}
+
+fn vec_deque_range_full() {
+ // Do not lint because iterator is assigned
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let iter = deque.drain(..);
+
+ // Do not lint because iterator is used
+ let mut deque = VecDeque::from([1, 2, 3]);
+ for x in deque.drain(..) {
+ let y = format!("x = {x}");
+ }
+
+ // Do lint
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.clear();
+}
+
+fn vec_deque_range_to() {
+ // Do not lint because iterator is assigned
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let iter = deque.drain(..deque.len());
+
+ // Do not lint because iterator is assigned and used
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let iter = deque.drain(..deque.len());
+ for x in iter {
+ let y = format!("x = {x}");
+ }
+
+ // Do lint
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.clear();
+}
+
+fn vec_deque_partial_drains() {
+ // Do not lint any of these because the ranges are not full
+
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.drain(1..);
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.drain(1..).max();
+
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.drain(..deque.len() - 1);
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.drain(..deque.len() - 1).min();
+
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.drain(1..deque.len() - 1);
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let w: Vec<i8> = deque.drain(1..deque.len() - 1).collect();
+}
+
+fn string_range() {
+ // Do not lint because iterator is assigned
+ let mut s = String::from("Hello, world!");
+ let iter = s.drain(0..s.len());
+
+ // Do not lint because iterator is used
+ let mut s = String::from("Hello, world!");
+ let n = s.drain(0..s.len()).count();
+
+ // Do not lint because iterator is assigned and used
+ let mut s = String::from("Hello, world!");
+ let iter = s.drain(usize::MIN..s.len());
+ let n = iter.count();
+
+ // Do lint
+ let mut s = String::from("Hello, world!");
+ s.clear();
+
+ // Do lint
+ let mut s = String::from("Hello, world!");
+ s.clear();
+}
+
+fn string_range_from() {
+ // Do not lint because iterator is assigned
+ let mut s = String::from("Hello, world!");
+ let iter = s.drain(0..);
+
+ // Do not lint because iterator is assigned and used
+ let mut s = String::from("Hello, world!");
+ let mut iter = s.drain(0..);
+ let next = iter.next();
+
+ // Do not lint because iterator is used
+ let mut s = String::from("Hello, world!");
+ let next = s.drain(usize::MIN..).next();
+
+ // Do lint
+ let mut s = String::from("Hello, world!");
+ s.clear();
+
+ // Do lint
+ let mut s = String::from("Hello, world!");
+ s.clear();
+}
+
+fn string_range_full() {
+ // Do not lint because iterator is assigned
+ let mut s = String::from("Hello, world!");
+ let iter = s.drain(..);
+
+ // Do not lint because iterator is used
+ let mut s = String::from("Hello, world!");
+ for x in s.drain(..) {
+ let y = format!("x = {x}");
+ }
+
+ // Do lint
+ let mut s = String::from("Hello, world!");
+ s.clear();
+}
+
+fn string_range_to() {
+ // Do not lint because iterator is assigned
+ let mut s = String::from("Hello, world!");
+ let iter = s.drain(..s.len());
+
+ // Do not lint because iterator is assigned and used
+ let mut s = String::from("Hello, world!");
+ let iter = s.drain(..s.len());
+ for x in iter {
+ let y = format!("x = {x}");
+ }
+
+ // Do lint
+ let mut s = String::from("Hello, world!");
+ s.clear();
+}
+
+fn string_partial_drains() {
+ // Do not lint any of these because the ranges are not full
+
+ let mut s = String::from("Hello, world!");
+ s.drain(1..);
+ let mut s = String::from("Hello, world!");
+ s.drain(1..).max();
+
+ let mut s = String::from("Hello, world!");
+ s.drain(..s.len() - 1);
+ let mut s = String::from("Hello, world!");
+ s.drain(..s.len() - 1).min();
+
+ let mut s = String::from("Hello, world!");
+ s.drain(1..s.len() - 1);
+ let mut s = String::from("Hello, world!");
+ let w: String = s.drain(1..s.len() - 1).collect();
+}
+
+fn hash_set() {
+ // Do not lint because iterator is assigned
+ let mut set = HashSet::from([1, 2, 3]);
+ let iter = set.drain();
+
+ // Do not lint because iterator is assigned and used
+ let mut set = HashSet::from([1, 2, 3]);
+ let mut iter = set.drain();
+ let next = iter.next();
+
+ // Do not lint because iterator is used
+ let mut set = HashSet::from([1, 2, 3]);
+ let next = set.drain().next();
+
+ // Do lint
+ let mut set = HashSet::from([1, 2, 3]);
+ set.clear();
+}
+
+fn hash_map() {
+ // Do not lint because iterator is assigned
+ let mut map = HashMap::from([(1, "a"), (2, "b")]);
+ let iter = map.drain();
+
+ // Do not lint because iterator is assigned and used
+ let mut map = HashMap::from([(1, "a"), (2, "b")]);
+ let mut iter = map.drain();
+ let next = iter.next();
+
+ // Do not lint because iterator is used
+ let mut map = HashMap::from([(1, "a"), (2, "b")]);
+ let next = map.drain().next();
+
+ // Do lint
+ let mut map = HashMap::from([(1, "a"), (2, "b")]);
+ map.clear();
+}
+
+fn binary_heap() {
+ // Do not lint because iterator is assigned
+ let mut heap = BinaryHeap::from([1, 2]);
+ let iter = heap.drain();
+
+ // Do not lint because iterator is assigned and used
+ let mut heap = BinaryHeap::from([1, 2]);
+ let mut iter = heap.drain();
+ let next = iter.next();
+
+ // Do not lint because iterator is used
+ let mut heap = BinaryHeap::from([1, 2]);
+ let next = heap.drain().next();
+
+ // Do lint
+ let mut heap = BinaryHeap::from([1, 2]);
+ heap.clear();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/clear_with_drain.rs b/src/tools/clippy/tests/ui/clear_with_drain.rs
new file mode 100644
index 000000000..4d60ee46e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/clear_with_drain.rs
@@ -0,0 +1,358 @@
+// run-rustfix
+#![allow(unused)]
+#![warn(clippy::clear_with_drain)]
+
+use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
+
+fn vec_range() {
+ // Do not lint because iterator is assigned
+ let mut v = vec![1, 2, 3];
+ let iter = v.drain(0..v.len());
+
+ // Do not lint because iterator is used
+ let mut v = vec![1, 2, 3];
+ let n = v.drain(0..v.len()).count();
+
+ // Do not lint because iterator is assigned and used
+ let mut v = vec![1, 2, 3];
+ let iter = v.drain(usize::MIN..v.len());
+ let n = iter.count();
+
+ // Do lint
+ let mut v = vec![1, 2, 3];
+ v.drain(0..v.len());
+
+ // Do lint
+ let mut v = vec![1, 2, 3];
+ v.drain(usize::MIN..v.len());
+}
+
+fn vec_range_from() {
+ // Do not lint because iterator is assigned
+ let mut v = vec![1, 2, 3];
+ let iter = v.drain(0..);
+
+ // Do not lint because iterator is assigned and used
+ let mut v = vec![1, 2, 3];
+ let mut iter = v.drain(0..);
+ let next = iter.next();
+
+ // Do not lint because iterator is used
+ let mut v = vec![1, 2, 3];
+ let next = v.drain(usize::MIN..).next();
+
+ // Do lint
+ let mut v = vec![1, 2, 3];
+ v.drain(0..);
+
+ // Do lint
+ let mut v = vec![1, 2, 3];
+ v.drain(usize::MIN..);
+}
+
+fn vec_range_full() {
+ // Do not lint because iterator is assigned
+ let mut v = vec![1, 2, 3];
+ let iter = v.drain(..);
+
+ // Do not lint because iterator is used
+ let mut v = vec![1, 2, 3];
+ for x in v.drain(..) {
+ let y = format!("x = {x}");
+ }
+
+ // Do lint
+ let mut v = vec![1, 2, 3];
+ v.drain(..);
+}
+
+fn vec_range_to() {
+ // Do not lint because iterator is assigned
+ let mut v = vec![1, 2, 3];
+ let iter = v.drain(..v.len());
+
+ // Do not lint because iterator is assigned and used
+ let mut v = vec![1, 2, 3];
+ let iter = v.drain(..v.len());
+ for x in iter {
+ let y = format!("x = {x}");
+ }
+
+ // Do lint
+ let mut v = vec![1, 2, 3];
+ v.drain(..v.len());
+}
+
+fn vec_partial_drains() {
+ // Do not lint any of these because the ranges are not full
+
+ let mut v = vec![1, 2, 3];
+ v.drain(1..);
+ let mut v = vec![1, 2, 3];
+ v.drain(1..).max();
+
+ let mut v = vec![1, 2, 3];
+ v.drain(..v.len() - 1);
+ let mut v = vec![1, 2, 3];
+ v.drain(..v.len() - 1).min();
+
+ let mut v = vec![1, 2, 3];
+ v.drain(1..v.len() - 1);
+ let mut v = vec![1, 2, 3];
+ let w: Vec<i8> = v.drain(1..v.len() - 1).collect();
+}
+
+fn vec_deque_range() {
+ // Do not lint because iterator is assigned
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let iter = deque.drain(0..deque.len());
+
+ // Do not lint because iterator is used
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let n = deque.drain(0..deque.len()).count();
+
+ // Do not lint because iterator is assigned and used
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let iter = deque.drain(usize::MIN..deque.len());
+ let n = iter.count();
+
+ // Do lint
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.drain(0..deque.len());
+
+ // Do lint
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.drain(usize::MIN..deque.len());
+}
+
+fn vec_deque_range_from() {
+ // Do not lint because iterator is assigned
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let iter = deque.drain(0..);
+
+ // Do not lint because iterator is assigned and used
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let mut iter = deque.drain(0..);
+ let next = iter.next();
+
+ // Do not lint because iterator is used
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let next = deque.drain(usize::MIN..).next();
+
+ // Do lint
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.drain(0..);
+
+ // Do lint
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.drain(usize::MIN..);
+}
+
+fn vec_deque_range_full() {
+ // Do not lint because iterator is assigned
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let iter = deque.drain(..);
+
+ // Do not lint because iterator is used
+ let mut deque = VecDeque::from([1, 2, 3]);
+ for x in deque.drain(..) {
+ let y = format!("x = {x}");
+ }
+
+ // Do lint
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.drain(..);
+}
+
+fn vec_deque_range_to() {
+ // Do not lint because iterator is assigned
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let iter = deque.drain(..deque.len());
+
+ // Do not lint because iterator is assigned and used
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let iter = deque.drain(..deque.len());
+ for x in iter {
+ let y = format!("x = {x}");
+ }
+
+ // Do lint
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.drain(..deque.len());
+}
+
+fn vec_deque_partial_drains() {
+ // Do not lint any of these because the ranges are not full
+
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.drain(1..);
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.drain(1..).max();
+
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.drain(..deque.len() - 1);
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.drain(..deque.len() - 1).min();
+
+ let mut deque = VecDeque::from([1, 2, 3]);
+ deque.drain(1..deque.len() - 1);
+ let mut deque = VecDeque::from([1, 2, 3]);
+ let w: Vec<i8> = deque.drain(1..deque.len() - 1).collect();
+}
+
+fn string_range() {
+ // Do not lint because iterator is assigned
+ let mut s = String::from("Hello, world!");
+ let iter = s.drain(0..s.len());
+
+ // Do not lint because iterator is used
+ let mut s = String::from("Hello, world!");
+ let n = s.drain(0..s.len()).count();
+
+ // Do not lint because iterator is assigned and used
+ let mut s = String::from("Hello, world!");
+ let iter = s.drain(usize::MIN..s.len());
+ let n = iter.count();
+
+ // Do lint
+ let mut s = String::from("Hello, world!");
+ s.drain(0..s.len());
+
+ // Do lint
+ let mut s = String::from("Hello, world!");
+ s.drain(usize::MIN..s.len());
+}
+
+fn string_range_from() {
+ // Do not lint because iterator is assigned
+ let mut s = String::from("Hello, world!");
+ let iter = s.drain(0..);
+
+ // Do not lint because iterator is assigned and used
+ let mut s = String::from("Hello, world!");
+ let mut iter = s.drain(0..);
+ let next = iter.next();
+
+ // Do not lint because iterator is used
+ let mut s = String::from("Hello, world!");
+ let next = s.drain(usize::MIN..).next();
+
+ // Do lint
+ let mut s = String::from("Hello, world!");
+ s.drain(0..);
+
+ // Do lint
+ let mut s = String::from("Hello, world!");
+ s.drain(usize::MIN..);
+}
+
+fn string_range_full() {
+ // Do not lint because iterator is assigned
+ let mut s = String::from("Hello, world!");
+ let iter = s.drain(..);
+
+ // Do not lint because iterator is used
+ let mut s = String::from("Hello, world!");
+ for x in s.drain(..) {
+ let y = format!("x = {x}");
+ }
+
+ // Do lint
+ let mut s = String::from("Hello, world!");
+ s.drain(..);
+}
+
+fn string_range_to() {
+ // Do not lint because iterator is assigned
+ let mut s = String::from("Hello, world!");
+ let iter = s.drain(..s.len());
+
+ // Do not lint because iterator is assigned and used
+ let mut s = String::from("Hello, world!");
+ let iter = s.drain(..s.len());
+ for x in iter {
+ let y = format!("x = {x}");
+ }
+
+ // Do lint
+ let mut s = String::from("Hello, world!");
+ s.drain(..s.len());
+}
+
+fn string_partial_drains() {
+ // Do not lint any of these because the ranges are not full
+
+ let mut s = String::from("Hello, world!");
+ s.drain(1..);
+ let mut s = String::from("Hello, world!");
+ s.drain(1..).max();
+
+ let mut s = String::from("Hello, world!");
+ s.drain(..s.len() - 1);
+ let mut s = String::from("Hello, world!");
+ s.drain(..s.len() - 1).min();
+
+ let mut s = String::from("Hello, world!");
+ s.drain(1..s.len() - 1);
+ let mut s = String::from("Hello, world!");
+ let w: String = s.drain(1..s.len() - 1).collect();
+}
+
+fn hash_set() {
+ // Do not lint because iterator is assigned
+ let mut set = HashSet::from([1, 2, 3]);
+ let iter = set.drain();
+
+ // Do not lint because iterator is assigned and used
+ let mut set = HashSet::from([1, 2, 3]);
+ let mut iter = set.drain();
+ let next = iter.next();
+
+ // Do not lint because iterator is used
+ let mut set = HashSet::from([1, 2, 3]);
+ let next = set.drain().next();
+
+ // Do lint
+ let mut set = HashSet::from([1, 2, 3]);
+ set.drain();
+}
+
+fn hash_map() {
+ // Do not lint because iterator is assigned
+ let mut map = HashMap::from([(1, "a"), (2, "b")]);
+ let iter = map.drain();
+
+ // Do not lint because iterator is assigned and used
+ let mut map = HashMap::from([(1, "a"), (2, "b")]);
+ let mut iter = map.drain();
+ let next = iter.next();
+
+ // Do not lint because iterator is used
+ let mut map = HashMap::from([(1, "a"), (2, "b")]);
+ let next = map.drain().next();
+
+ // Do lint
+ let mut map = HashMap::from([(1, "a"), (2, "b")]);
+ map.drain();
+}
+
+fn binary_heap() {
+ // Do not lint because iterator is assigned
+ let mut heap = BinaryHeap::from([1, 2]);
+ let iter = heap.drain();
+
+ // Do not lint because iterator is assigned and used
+ let mut heap = BinaryHeap::from([1, 2]);
+ let mut iter = heap.drain();
+ let next = iter.next();
+
+ // Do not lint because iterator is used
+ let mut heap = BinaryHeap::from([1, 2]);
+ let next = heap.drain().next();
+
+ // Do lint
+ let mut heap = BinaryHeap::from([1, 2]);
+ heap.drain();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/clear_with_drain.stderr b/src/tools/clippy/tests/ui/clear_with_drain.stderr
new file mode 100644
index 000000000..20158da11
--- /dev/null
+++ b/src/tools/clippy/tests/ui/clear_with_drain.stderr
@@ -0,0 +1,130 @@
+error: `drain` used to clear a `Vec`
+ --> $DIR/clear_with_drain.rs:23:7
+ |
+LL | v.drain(0..v.len());
+ | ^^^^^^^^^^^^^^^^^ help: try: `clear()`
+ |
+ = note: `-D clippy::clear-with-drain` implied by `-D warnings`
+
+error: `drain` used to clear a `Vec`
+ --> $DIR/clear_with_drain.rs:27:7
+ |
+LL | v.drain(usize::MIN..v.len());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `Vec`
+ --> $DIR/clear_with_drain.rs:46:7
+ |
+LL | v.drain(0..);
+ | ^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `Vec`
+ --> $DIR/clear_with_drain.rs:50:7
+ |
+LL | v.drain(usize::MIN..);
+ | ^^^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `Vec`
+ --> $DIR/clear_with_drain.rs:66:7
+ |
+LL | v.drain(..);
+ | ^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `Vec`
+ --> $DIR/clear_with_drain.rs:83:7
+ |
+LL | v.drain(..v.len());
+ | ^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `VecDeque`
+ --> $DIR/clear_with_drain.rs:121:11
+ |
+LL | deque.drain(0..deque.len());
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `VecDeque`
+ --> $DIR/clear_with_drain.rs:125:11
+ |
+LL | deque.drain(usize::MIN..deque.len());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `VecDeque`
+ --> $DIR/clear_with_drain.rs:144:11
+ |
+LL | deque.drain(0..);
+ | ^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `VecDeque`
+ --> $DIR/clear_with_drain.rs:148:11
+ |
+LL | deque.drain(usize::MIN..);
+ | ^^^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `VecDeque`
+ --> $DIR/clear_with_drain.rs:164:11
+ |
+LL | deque.drain(..);
+ | ^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `VecDeque`
+ --> $DIR/clear_with_drain.rs:181:11
+ |
+LL | deque.drain(..deque.len());
+ | ^^^^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `String`
+ --> $DIR/clear_with_drain.rs:219:7
+ |
+LL | s.drain(0..s.len());
+ | ^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `String`
+ --> $DIR/clear_with_drain.rs:223:7
+ |
+LL | s.drain(usize::MIN..s.len());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `String`
+ --> $DIR/clear_with_drain.rs:242:7
+ |
+LL | s.drain(0..);
+ | ^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `String`
+ --> $DIR/clear_with_drain.rs:246:7
+ |
+LL | s.drain(usize::MIN..);
+ | ^^^^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `String`
+ --> $DIR/clear_with_drain.rs:262:7
+ |
+LL | s.drain(..);
+ | ^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `String`
+ --> $DIR/clear_with_drain.rs:279:7
+ |
+LL | s.drain(..s.len());
+ | ^^^^^^^^^^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `HashSet`
+ --> $DIR/clear_with_drain.rs:317:9
+ |
+LL | set.drain();
+ | ^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `HashMap`
+ --> $DIR/clear_with_drain.rs:336:9
+ |
+LL | map.drain();
+ | ^^^^^^^ help: try: `clear()`
+
+error: `drain` used to clear a `BinaryHeap`
+ --> $DIR/clear_with_drain.rs:355:10
+ |
+LL | heap.drain();
+ | ^^^^^^^ help: try: `clear()`
+
+error: aborting due to 21 previous errors
+
diff --git a/src/tools/clippy/tests/ui/collection_is_never_read.rs b/src/tools/clippy/tests/ui/collection_is_never_read.rs
new file mode 100644
index 000000000..01259a983
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collection_is_never_read.rs
@@ -0,0 +1,190 @@
+#![allow(unused)]
+#![warn(clippy::collection_is_never_read)]
+
+use std::collections::{HashMap, HashSet};
+
+fn main() {}
+
+fn not_a_collection() {
+ // TODO: Expand `collection_is_never_read` beyond collections?
+ let mut x = 10; // Ok
+ x += 1;
+}
+
+fn no_access_at_all() {
+ // Other lints should catch this.
+ let x = vec![1, 2, 3]; // Ok
+}
+
+fn write_without_read() {
+ // The main use case for `collection_is_never_read`.
+ let mut x = HashMap::new(); // WARNING
+ x.insert(1, 2);
+}
+
+fn read_without_write() {
+ let mut x = vec![1, 2, 3]; // Ok
+ let _ = x.len();
+}
+
+fn write_and_read() {
+ let mut x = vec![1, 2, 3]; // Ok
+ x.push(4);
+ let _ = x.len();
+}
+
+fn write_after_read() {
+ // TODO: Warn here, but this requires more extensive data flow analysis.
+ let mut x = vec![1, 2, 3]; // Ok
+ let _ = x.len();
+ x.push(4); // Pointless
+}
+
+fn write_before_reassign() {
+ // TODO: Warn here, but this requires more extensive data flow analysis.
+ let mut x = HashMap::new(); // Ok
+ x.insert(1, 2); // Pointless
+ x = HashMap::new();
+ let _ = x.len();
+}
+
+fn read_in_closure() {
+ let mut x = HashMap::new(); // Ok
+ x.insert(1, 2);
+ let _ = || {
+ let _ = x.len();
+ };
+}
+
+fn write_in_closure() {
+ let mut x = vec![1, 2, 3]; // WARNING
+ let _ = || {
+ x.push(4);
+ };
+}
+
+fn read_in_format() {
+ let mut x = HashMap::new(); // Ok
+ x.insert(1, 2);
+ format!("{x:?}");
+}
+
+fn shadowing_1() {
+ let x = HashMap::<usize, usize>::new(); // Ok
+ let _ = x.len();
+ let mut x = HashMap::new(); // WARNING
+ x.insert(1, 2);
+}
+
+fn shadowing_2() {
+ let mut x = HashMap::new(); // WARNING
+ x.insert(1, 2);
+ let x = HashMap::<usize, usize>::new(); // Ok
+ let _ = x.len();
+}
+
+#[allow(clippy::let_unit_value)]
+fn fake_read_1() {
+ let mut x = vec![1, 2, 3]; // WARNING
+ x.reverse();
+ let _: () = x.clear();
+}
+
+fn fake_read_2() {
+ let mut x = vec![1, 2, 3]; // WARNING
+ x.reverse();
+ println!("{:?}", x.push(5));
+}
+
+fn assignment() {
+ let mut x = vec![1, 2, 3]; // WARNING
+ let y = vec![4, 5, 6]; // Ok
+ x = y;
+}
+
+#[allow(clippy::self_assignment)]
+fn self_assignment() {
+ let mut x = vec![1, 2, 3]; // WARNING
+ x = x;
+}
+
+fn method_argument_but_not_target() {
+ struct MyStruct;
+ impl MyStruct {
+ fn my_method(&self, _argument: &[usize]) {}
+ }
+ let my_struct = MyStruct;
+
+ let mut x = vec![1, 2, 3]; // Ok
+ x.reverse();
+ my_struct.my_method(&x);
+}
+
+fn insert_is_not_a_read() {
+ let mut x = HashSet::new(); // WARNING
+ x.insert(5);
+}
+
+fn insert_is_a_read() {
+ let mut x = HashSet::new(); // Ok
+ if x.insert(5) {
+ println!("5 was inserted");
+ }
+}
+
+fn not_read_if_return_value_not_used() {
+ // `is_empty` does not modify the set, so it's a query. But since the return value is not used, the
+ // lint does not consider it a read here.
+ let x = vec![1, 2, 3]; // WARNING
+ x.is_empty();
+}
+
+fn extension_traits() {
+ trait VecExt<T> {
+ fn method_with_side_effect(&self);
+ fn method_without_side_effect(&self);
+ }
+
+ impl<T> VecExt<T> for Vec<T> {
+ fn method_with_side_effect(&self) {
+ println!("my length: {}", self.len());
+ }
+ fn method_without_side_effect(&self) {}
+ }
+
+ let x = vec![1, 2, 3]; // Ok
+ x.method_with_side_effect();
+
+ let y = vec![1, 2, 3]; // Ok (false negative)
+ y.method_without_side_effect();
+}
+
+fn function_argument() {
+ #[allow(clippy::ptr_arg)]
+ fn foo<T>(v: &Vec<T>) -> usize {
+ v.len()
+ }
+
+ let x = vec![1, 2, 3]; // Ok
+ foo(&x);
+}
+
+fn string() {
+ // Do lint (write without read)
+ let mut s = String::new();
+ s.push_str("Hello, World!");
+
+ // Do not lint (read without write)
+ let mut s = String::from("Hello, World!");
+ let _ = s.len();
+
+ // Do not lint (write and read)
+ let mut s = String::from("Hello, World!");
+ s.push_str("foo, bar");
+ let _ = s.len();
+
+ // Do lint the first line, but not the second
+ let mut s = String::from("Hello, World!");
+ let t = String::from("foo, bar");
+ s = t;
+}
diff --git a/src/tools/clippy/tests/ui/collection_is_never_read.stderr b/src/tools/clippy/tests/ui/collection_is_never_read.stderr
new file mode 100644
index 000000000..cf51a5368
--- /dev/null
+++ b/src/tools/clippy/tests/ui/collection_is_never_read.stderr
@@ -0,0 +1,76 @@
+error: collection is never read
+ --> $DIR/collection_is_never_read.rs:21:5
+ |
+LL | let mut x = HashMap::new(); // WARNING
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::collection-is-never-read` implied by `-D warnings`
+
+error: collection is never read
+ --> $DIR/collection_is_never_read.rs:60:5
+ |
+LL | let mut x = vec![1, 2, 3]; // WARNING
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+ --> $DIR/collection_is_never_read.rs:75:5
+ |
+LL | let mut x = HashMap::new(); // WARNING
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+ --> $DIR/collection_is_never_read.rs:80:5
+ |
+LL | let mut x = HashMap::new(); // WARNING
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+ --> $DIR/collection_is_never_read.rs:88:5
+ |
+LL | let mut x = vec![1, 2, 3]; // WARNING
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+ --> $DIR/collection_is_never_read.rs:94:5
+ |
+LL | let mut x = vec![1, 2, 3]; // WARNING
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+ --> $DIR/collection_is_never_read.rs:100:5
+ |
+LL | let mut x = vec![1, 2, 3]; // WARNING
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+ --> $DIR/collection_is_never_read.rs:107:5
+ |
+LL | let mut x = vec![1, 2, 3]; // WARNING
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+ --> $DIR/collection_is_never_read.rs:124:5
+ |
+LL | let mut x = HashSet::new(); // WARNING
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+ --> $DIR/collection_is_never_read.rs:138:5
+ |
+LL | let x = vec![1, 2, 3]; // WARNING
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+ --> $DIR/collection_is_never_read.rs:174:5
+ |
+LL | let mut s = String::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: collection is never read
+ --> $DIR/collection_is_never_read.rs:187:5
+ |
+LL | let mut s = String::from("Hello, World!");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-10148.rs b/src/tools/clippy/tests/ui/crashes/ice-10148.rs
new file mode 100644
index 000000000..1ab3570c9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-10148.rs
@@ -0,0 +1,9 @@
+// aux-build:../../auxiliary/proc_macros.rs
+
+extern crate proc_macros;
+
+use proc_macros::with_span;
+
+fn main() {
+ println!(with_span!(""something ""));
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-10148.stderr b/src/tools/clippy/tests/ui/crashes/ice-10148.stderr
new file mode 100644
index 000000000..f23e4433f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-10148.stderr
@@ -0,0 +1,12 @@
+error: empty string literal in `println!`
+ --> $DIR/ice-10148.rs:8:5
+ |
+LL | println!(with_span!(""something ""));
+ | ^^^^^^^^^^^^^^^^^^^^-----------^^^^^
+ | |
+ | help: remove the empty string
+ |
+ = note: `-D clippy::println-empty-string` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6179.rs b/src/tools/clippy/tests/ui/crashes/ice-6179.rs
index 4fe92d356..ce1895851 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6179.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-6179.rs
@@ -2,7 +2,7 @@
//! The ICE is mainly caused by using `hir_ty_to_ty`. See the discussion in the PR for details.
#![warn(clippy::use_self)]
-#![allow(dead_code)]
+#![allow(dead_code, clippy::let_with_type_underscore)]
struct Foo;
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr
index efdd56dd4..4787282f5 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr
@@ -6,11 +6,11 @@ LL | _n: PhantomData,
|
help: consider importing one of these items
|
-LL | use core::marker::PhantomData;
+LL + use core::marker::PhantomData;
|
-LL | use serde::__private::PhantomData;
+LL + use serde::__private::PhantomData;
|
-LL | use std::marker::PhantomData;
+LL + use std::marker::PhantomData;
|
error[E0412]: cannot find type `VAL` in this scope
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6254.stderr b/src/tools/clippy/tests/ui/crashes/ice-6254.stderr
index 22d82a30c..263c27d3d 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6254.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-6254.stderr
@@ -6,6 +6,8 @@ LL | FOO_REF_REF => {},
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #62411 <https://github.com/rust-lang/rust/issues/62411>
+ = note: the traits must be derived, manual `impl`s are not sufficient
+ = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details
= note: `-D indirect-structural-match` implied by `-D warnings`
error: aborting due to previous error
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
index a370ccc76..42c15d6a7 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
@@ -1,5 +1,5 @@
// run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::default_numeric_fallback)]
#![allow(
@@ -9,11 +9,12 @@
clippy::unnecessary_operation,
clippy::branches_sharing_code,
clippy::match_single_binding,
- clippy::let_unit_value
+ clippy::let_unit_value,
+ clippy::let_with_type_underscore
)]
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
mod basic_expr {
fn test() {
@@ -166,20 +167,17 @@ mod method_calls {
}
mod in_macro {
- macro_rules! internal_macro {
- () => {
- let x = 22.0_f64;
- };
- }
+ use super::*;
// Should lint in internal macro.
+ #[inline_macros]
fn internal() {
- internal_macro!();
+ inline!(let x = 22.0_f64;);
}
// Should NOT lint in external macro.
fn external() {
- default_numeric_fallback!();
+ external!(let x = 22.;);
}
}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
index 2476fe951..7da7ea254 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
@@ -1,5 +1,5 @@
// run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::default_numeric_fallback)]
#![allow(
@@ -9,11 +9,12 @@
clippy::unnecessary_operation,
clippy::branches_sharing_code,
clippy::match_single_binding,
- clippy::let_unit_value
+ clippy::let_unit_value,
+ clippy::let_with_type_underscore
)]
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
mod basic_expr {
fn test() {
@@ -166,20 +167,17 @@ mod method_calls {
}
mod in_macro {
- macro_rules! internal_macro {
- () => {
- let x = 22.;
- };
- }
+ use super::*;
// Should lint in internal macro.
+ #[inline_macros]
fn internal() {
- internal_macro!();
+ inline!(let x = 22.;);
}
// Should NOT lint in external macro.
fn external() {
- default_numeric_fallback!();
+ external!(let x = 22.;);
}
}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr
index 5df2f6423..b949cd1d5 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr
@@ -1,5 +1,5 @@
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:21:17
+ --> $DIR/default_numeric_fallback_f64.rs:22:17
|
LL | let x = 0.12;
| ^^^^ help: consider adding suffix: `0.12_f64`
@@ -7,147 +7,144 @@ LL | let x = 0.12;
= note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:22:18
+ --> $DIR/default_numeric_fallback_f64.rs:23:18
|
LL | let x = [1., 2., 3.];
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:22:22
+ --> $DIR/default_numeric_fallback_f64.rs:23:22
|
LL | let x = [1., 2., 3.];
| ^^ help: consider adding suffix: `2.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:22:26
+ --> $DIR/default_numeric_fallback_f64.rs:23:26
|
LL | let x = [1., 2., 3.];
| ^^ help: consider adding suffix: `3.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:23:28
+ --> $DIR/default_numeric_fallback_f64.rs:24:28
|
LL | let x = if true { (1., 2.) } else { (3., 4.) };
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:23:32
+ --> $DIR/default_numeric_fallback_f64.rs:24:32
|
LL | let x = if true { (1., 2.) } else { (3., 4.) };
| ^^ help: consider adding suffix: `2.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:23:46
+ --> $DIR/default_numeric_fallback_f64.rs:24:46
|
LL | let x = if true { (1., 2.) } else { (3., 4.) };
| ^^ help: consider adding suffix: `3.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:23:50
+ --> $DIR/default_numeric_fallback_f64.rs:24:50
|
LL | let x = if true { (1., 2.) } else { (3., 4.) };
| ^^ help: consider adding suffix: `4.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:24:23
+ --> $DIR/default_numeric_fallback_f64.rs:25:23
|
LL | let x = match 1. {
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:25:18
+ --> $DIR/default_numeric_fallback_f64.rs:26:18
|
LL | _ => 1.,
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:44:21
+ --> $DIR/default_numeric_fallback_f64.rs:45:21
|
LL | let y = 1.;
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:52:21
+ --> $DIR/default_numeric_fallback_f64.rs:53:21
|
LL | let y = 1.;
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:58:21
+ --> $DIR/default_numeric_fallback_f64.rs:59:21
|
LL | let y = 1.;
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:66:21
+ --> $DIR/default_numeric_fallback_f64.rs:67:21
|
LL | let y = 1.;
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:78:9
+ --> $DIR/default_numeric_fallback_f64.rs:79:9
|
LL | 1.
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:84:27
+ --> $DIR/default_numeric_fallback_f64.rs:85:27
|
LL | let f = || -> _ { 1. };
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:88:29
+ --> $DIR/default_numeric_fallback_f64.rs:89:29
|
LL | let f = || -> f64 { 1. };
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:102:21
+ --> $DIR/default_numeric_fallback_f64.rs:103:21
|
LL | generic_arg(1.);
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:105:32
+ --> $DIR/default_numeric_fallback_f64.rs:106:32
|
LL | let x: _ = generic_arg(1.);
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:123:28
+ --> $DIR/default_numeric_fallback_f64.rs:124:28
|
LL | GenericStruct { x: 1. };
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:126:36
+ --> $DIR/default_numeric_fallback_f64.rs:127:36
|
LL | let _ = GenericStruct { x: 1. };
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:144:24
+ --> $DIR/default_numeric_fallback_f64.rs:145:24
|
LL | GenericEnum::X(1.);
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:164:23
+ --> $DIR/default_numeric_fallback_f64.rs:165:23
|
LL | s.generic_arg(1.);
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:171:21
+ --> $DIR/default_numeric_fallback_f64.rs:175:25
|
-LL | let x = 22.;
- | ^^^ help: consider adding suffix: `22.0_f64`
-...
-LL | internal_macro!();
- | ----------------- in this macro invocation
+LL | inline!(let x = 22.;);
+ | ^^^ help: consider adding suffix: `22.0_f64`
|
- = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_fn_internal` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 24 previous errors
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
index 3f4994f04..b7485b73d 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
@@ -1,5 +1,5 @@
// run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![feature(lint_reasons)]
#![warn(clippy::default_numeric_fallback)]
@@ -9,11 +9,12 @@
clippy::no_effect,
clippy::unnecessary_operation,
clippy::branches_sharing_code,
- clippy::let_unit_value
+ clippy::let_unit_value,
+ clippy::let_with_type_underscore
)]
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
mod basic_expr {
fn test() {
@@ -167,20 +168,17 @@ mod method_calls {
}
mod in_macro {
- macro_rules! internal_macro {
- () => {
- let x = 22_i32;
- };
- }
+ use super::*;
// Should lint in internal macro.
+ #[inline_macros]
fn internal() {
- internal_macro!();
+ inline!(let x = 22_i32;);
}
// Should NOT lint in external macro.
fn external() {
- default_numeric_fallback!();
+ external!(let x = 22;);
}
}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
index 2df0e0978..7307d3135 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
@@ -1,5 +1,5 @@
// run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![feature(lint_reasons)]
#![warn(clippy::default_numeric_fallback)]
@@ -9,11 +9,12 @@
clippy::no_effect,
clippy::unnecessary_operation,
clippy::branches_sharing_code,
- clippy::let_unit_value
+ clippy::let_unit_value,
+ clippy::let_with_type_underscore
)]
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
mod basic_expr {
fn test() {
@@ -167,20 +168,17 @@ mod method_calls {
}
mod in_macro {
- macro_rules! internal_macro {
- () => {
- let x = 22;
- };
- }
+ use super::*;
// Should lint in internal macro.
+ #[inline_macros]
fn internal() {
- internal_macro!();
+ inline!(let x = 22;);
}
// Should NOT lint in external macro.
fn external() {
- default_numeric_fallback!();
+ external!(let x = 22;);
}
}
diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr
index 6f219c3fc..48cd28102 100644
--- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr
+++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr
@@ -1,5 +1,5 @@
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:21:17
+ --> $DIR/default_numeric_fallback_i32.rs:22:17
|
LL | let x = 22;
| ^^ help: consider adding suffix: `22_i32`
@@ -7,159 +7,156 @@ LL | let x = 22;
= note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:22:18
+ --> $DIR/default_numeric_fallback_i32.rs:23:18
|
LL | let x = [1, 2, 3];
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:22:21
+ --> $DIR/default_numeric_fallback_i32.rs:23:21
|
LL | let x = [1, 2, 3];
| ^ help: consider adding suffix: `2_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:22:24
+ --> $DIR/default_numeric_fallback_i32.rs:23:24
|
LL | let x = [1, 2, 3];
| ^ help: consider adding suffix: `3_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:23:28
+ --> $DIR/default_numeric_fallback_i32.rs:24:28
|
LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:23:31
+ --> $DIR/default_numeric_fallback_i32.rs:24:31
|
LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `2_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:23:44
+ --> $DIR/default_numeric_fallback_i32.rs:24:44
|
LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `3_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:23:47
+ --> $DIR/default_numeric_fallback_i32.rs:24:47
|
LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `4_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:24:23
+ --> $DIR/default_numeric_fallback_i32.rs:25:23
|
LL | let x = match 1 {
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:25:13
+ --> $DIR/default_numeric_fallback_i32.rs:26:13
|
LL | 1 => 1,
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:25:18
+ --> $DIR/default_numeric_fallback_i32.rs:26:18
|
LL | 1 => 1,
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:26:18
+ --> $DIR/default_numeric_fallback_i32.rs:27:18
|
LL | _ => 2,
| ^ help: consider adding suffix: `2_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:45:21
+ --> $DIR/default_numeric_fallback_i32.rs:46:21
|
LL | let y = 1;
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:53:21
+ --> $DIR/default_numeric_fallback_i32.rs:54:21
|
LL | let y = 1;
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:59:21
+ --> $DIR/default_numeric_fallback_i32.rs:60:21
|
LL | let y = 1;
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:67:21
+ --> $DIR/default_numeric_fallback_i32.rs:68:21
|
LL | let y = 1;
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:79:9
+ --> $DIR/default_numeric_fallback_i32.rs:80:9
|
LL | 1
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:85:27
+ --> $DIR/default_numeric_fallback_i32.rs:86:27
|
LL | let f = || -> _ { 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:89:29
+ --> $DIR/default_numeric_fallback_i32.rs:90:29
|
LL | let f = || -> i32 { 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:103:21
+ --> $DIR/default_numeric_fallback_i32.rs:104:21
|
LL | generic_arg(1);
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:106:32
+ --> $DIR/default_numeric_fallback_i32.rs:107:32
|
LL | let x: _ = generic_arg(1);
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:124:28
+ --> $DIR/default_numeric_fallback_i32.rs:125:28
|
LL | GenericStruct { x: 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:127:36
+ --> $DIR/default_numeric_fallback_i32.rs:128:36
|
LL | let _ = GenericStruct { x: 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:145:24
+ --> $DIR/default_numeric_fallback_i32.rs:146:24
|
LL | GenericEnum::X(1);
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:165:23
+ --> $DIR/default_numeric_fallback_i32.rs:166:23
|
LL | s.generic_arg(1);
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:172:21
+ --> $DIR/default_numeric_fallback_i32.rs:176:25
|
-LL | let x = 22;
- | ^^ help: consider adding suffix: `22_i32`
-...
-LL | internal_macro!();
- | ----------------- in this macro invocation
+LL | inline!(let x = 22;);
+ | ^^ help: consider adding suffix: `22_i32`
|
- = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_fn_internal` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 26 previous errors
diff --git a/src/tools/clippy/tests/ui/default_trait_access.fixed b/src/tools/clippy/tests/ui/default_trait_access.fixed
index 5640599d4..7842ef3ec 100644
--- a/src/tools/clippy/tests/ui/default_trait_access.fixed
+++ b/src/tools/clippy/tests/ui/default_trait_access.fixed
@@ -1,12 +1,12 @@
// run-rustfix
-// aux-build: proc_macro_with_span.rs
+// aux-build: proc_macros.rs
#![deny(clippy::default_trait_access)]
#![allow(dead_code, unused_imports)]
#![allow(clippy::uninlined_format_args)]
-extern crate proc_macro_with_span;
+extern crate proc_macros;
-use proc_macro_with_span::with_span;
+use proc_macros::with_span;
use std::default;
use std::default::Default as D2;
use std::string;
diff --git a/src/tools/clippy/tests/ui/default_trait_access.rs b/src/tools/clippy/tests/ui/default_trait_access.rs
index 11d4bc5c5..cbb3e59c9 100644
--- a/src/tools/clippy/tests/ui/default_trait_access.rs
+++ b/src/tools/clippy/tests/ui/default_trait_access.rs
@@ -1,12 +1,12 @@
// run-rustfix
-// aux-build: proc_macro_with_span.rs
+// aux-build: proc_macros.rs
#![deny(clippy::default_trait_access)]
#![allow(dead_code, unused_imports)]
#![allow(clippy::uninlined_format_args)]
-extern crate proc_macro_with_span;
+extern crate proc_macros;
-use proc_macro_with_span::with_span;
+use proc_macros::with_span;
use std::default;
use std::default::Default as D2;
use std::string;
diff --git a/src/tools/clippy/tests/ui/deref_addrof.fixed b/src/tools/clippy/tests/ui/deref_addrof.fixed
index 2f489deb1..ca5c03304 100644
--- a/src/tools/clippy/tests/ui/deref_addrof.fixed
+++ b/src/tools/clippy/tests/ui/deref_addrof.fixed
@@ -1,7 +1,12 @@
// run-rustfix
+// aux-build:proc_macros.rs
+
#![allow(clippy::return_self_not_must_use)]
#![warn(clippy::deref_addrof)]
+extern crate proc_macros;
+use proc_macros::inline_macros;
+
fn get_number() -> usize {
10
}
@@ -41,28 +46,15 @@ fn main() {
let _ = unsafe { *core::ptr::addr_of!(a) };
}
-#[rustfmt::skip]
-macro_rules! m {
- ($visitor: expr) => {
- $visitor
- };
-}
-
-#[rustfmt::skip]
-macro_rules! m_mut {
- ($visitor: expr) => {
- $visitor
- };
-}
-
#[derive(Copy, Clone)]
pub struct S;
+#[inline_macros]
impl S {
pub fn f(&self) -> &Self {
- m!(self)
+ inline!($(@expr self))
}
#[allow(unused_mut)] // mut will be unused, once the macro is fixed
pub fn f_mut(mut self) -> Self {
- m_mut!(self)
+ inline!($(@expr self))
}
}
diff --git a/src/tools/clippy/tests/ui/deref_addrof.rs b/src/tools/clippy/tests/ui/deref_addrof.rs
index 49f360b9a..3db5fafe9 100644
--- a/src/tools/clippy/tests/ui/deref_addrof.rs
+++ b/src/tools/clippy/tests/ui/deref_addrof.rs
@@ -1,7 +1,12 @@
// run-rustfix
+// aux-build:proc_macros.rs
+
#![allow(clippy::return_self_not_must_use)]
#![warn(clippy::deref_addrof)]
+extern crate proc_macros;
+use proc_macros::inline_macros;
+
fn get_number() -> usize {
10
}
@@ -41,28 +46,15 @@ fn main() {
let _ = unsafe { *core::ptr::addr_of!(a) };
}
-#[rustfmt::skip]
-macro_rules! m {
- ($visitor: expr) => {
- *& $visitor
- };
-}
-
-#[rustfmt::skip]
-macro_rules! m_mut {
- ($visitor: expr) => {
- *& mut $visitor
- };
-}
-
#[derive(Copy, Clone)]
pub struct S;
+#[inline_macros]
impl S {
pub fn f(&self) -> &Self {
- m!(self)
+ inline!(*& $(@expr self))
}
#[allow(unused_mut)] // mut will be unused, once the macro is fixed
pub fn f_mut(mut self) -> Self {
- m_mut!(self)
+ inline!(*&mut $(@expr self))
}
}
diff --git a/src/tools/clippy/tests/ui/deref_addrof.stderr b/src/tools/clippy/tests/ui/deref_addrof.stderr
index 75371fcdb..e0287522f 100644
--- a/src/tools/clippy/tests/ui/deref_addrof.stderr
+++ b/src/tools/clippy/tests/ui/deref_addrof.stderr
@@ -1,5 +1,5 @@
error: immediately dereferencing a reference
- --> $DIR/deref_addrof.rs:19:13
+ --> $DIR/deref_addrof.rs:24:13
|
LL | let b = *&a;
| ^^^ help: try this: `a`
@@ -7,68 +7,62 @@ LL | let b = *&a;
= note: `-D clippy::deref-addrof` implied by `-D warnings`
error: immediately dereferencing a reference
- --> $DIR/deref_addrof.rs:21:13
+ --> $DIR/deref_addrof.rs:26:13
|
LL | let b = *&get_number();
| ^^^^^^^^^^^^^^ help: try this: `get_number()`
error: immediately dereferencing a reference
- --> $DIR/deref_addrof.rs:26:13
+ --> $DIR/deref_addrof.rs:31:13
|
LL | let b = *&bytes[1..2][0];
| ^^^^^^^^^^^^^^^^ help: try this: `bytes[1..2][0]`
error: immediately dereferencing a reference
- --> $DIR/deref_addrof.rs:30:13
+ --> $DIR/deref_addrof.rs:35:13
|
LL | let b = *&(a);
| ^^^^^ help: try this: `(a)`
error: immediately dereferencing a reference
- --> $DIR/deref_addrof.rs:32:13
+ --> $DIR/deref_addrof.rs:37:13
|
LL | let b = *(&a);
| ^^^^^ help: try this: `a`
error: immediately dereferencing a reference
- --> $DIR/deref_addrof.rs:35:13
+ --> $DIR/deref_addrof.rs:40:13
|
LL | let b = *((&a));
| ^^^^^^^ help: try this: `a`
error: immediately dereferencing a reference
- --> $DIR/deref_addrof.rs:37:13
+ --> $DIR/deref_addrof.rs:42:13
|
LL | let b = *&&a;
| ^^^^ help: try this: `&a`
error: immediately dereferencing a reference
- --> $DIR/deref_addrof.rs:39:14
+ --> $DIR/deref_addrof.rs:44:14
|
LL | let b = **&aref;
| ^^^^^^ help: try this: `aref`
error: immediately dereferencing a reference
- --> $DIR/deref_addrof.rs:47:9
+ --> $DIR/deref_addrof.rs:54:17
|
-LL | *& $visitor
- | ^^^^^^^^^^^ help: try this: `$visitor`
-...
-LL | m!(self)
- | -------- in this macro invocation
+LL | inline!(*& $(@expr self))
+ | ^^^^^^^^^^^^^^^^ help: try this: `$(@expr self)`
|
- = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
error: immediately dereferencing a reference
- --> $DIR/deref_addrof.rs:54:9
+ --> $DIR/deref_addrof.rs:58:17
|
-LL | *& mut $visitor
- | ^^^^^^^^^^^^^^^ help: try this: `$visitor`
-...
-LL | m_mut!(self)
- | ------------ in this macro invocation
+LL | inline!(*&mut $(@expr self))
+ | ^^^^^^^^^^^^^^^^^^^ help: try this: `$(@expr self)`
|
- = note: this error originates in the macro `m_mut` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 10 previous errors
diff --git a/src/tools/clippy/tests/ui/deref_addrof_macro.rs b/src/tools/clippy/tests/ui/deref_addrof_macro.rs
index dcebd6c6e..57c0be3f5 100644
--- a/src/tools/clippy/tests/ui/deref_addrof_macro.rs
+++ b/src/tools/clippy/tests/ui/deref_addrof_macro.rs
@@ -1,10 +1,13 @@
-macro_rules! m {
- ($($x:tt),*) => { &[$(($x, stringify!(x)),)*] };
-}
+// aux-build:proc_macros.rs
+
+#![warn(clippy::deref_addrof)]
+
+extern crate proc_macros;
-#[warn(clippy::deref_addrof)]
-fn f() -> [(i32, &'static str); 3] {
- *m![1, 2, 3] // should be fine
+#[proc_macros::inline_macros]
+fn f() -> i32 {
+ // should be fine
+ *inline!(&$1)
}
fn main() {}
diff --git a/src/tools/clippy/tests/ui/derivable_impls.fixed b/src/tools/clippy/tests/ui/derivable_impls.fixed
index ee8456f5d..89ec33a0d 100644
--- a/src/tools/clippy/tests/ui/derivable_impls.fixed
+++ b/src/tools/clippy/tests/ui/derivable_impls.fixed
@@ -231,4 +231,41 @@ impl Default for NonExhaustiveEnum {
}
}
+// https://github.com/rust-lang/rust-clippy/issues/10396
+
+#[derive(Default)]
+struct DefaultType;
+
+struct GenericType<T = DefaultType> {
+ t: T,
+}
+
+impl Default for GenericType {
+ fn default() -> Self {
+ Self { t: Default::default() }
+ }
+}
+
+struct InnerGenericType<T> {
+ t: T,
+}
+
+impl Default for InnerGenericType<DefaultType> {
+ fn default() -> Self {
+ Self { t: Default::default() }
+ }
+}
+
+struct OtherGenericType<T = DefaultType> {
+ inner: InnerGenericType<T>,
+}
+
+impl Default for OtherGenericType {
+ fn default() -> Self {
+ Self {
+ inner: Default::default(),
+ }
+ }
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/derivable_impls.rs b/src/tools/clippy/tests/ui/derivable_impls.rs
index 14af419bc..def6e4116 100644
--- a/src/tools/clippy/tests/ui/derivable_impls.rs
+++ b/src/tools/clippy/tests/ui/derivable_impls.rs
@@ -267,4 +267,41 @@ impl Default for NonExhaustiveEnum {
}
}
+// https://github.com/rust-lang/rust-clippy/issues/10396
+
+#[derive(Default)]
+struct DefaultType;
+
+struct GenericType<T = DefaultType> {
+ t: T,
+}
+
+impl Default for GenericType {
+ fn default() -> Self {
+ Self { t: Default::default() }
+ }
+}
+
+struct InnerGenericType<T> {
+ t: T,
+}
+
+impl Default for InnerGenericType<DefaultType> {
+ fn default() -> Self {
+ Self { t: Default::default() }
+ }
+}
+
+struct OtherGenericType<T = DefaultType> {
+ inner: InnerGenericType<T>,
+}
+
+impl Default for OtherGenericType {
+ fn default() -> Self {
+ Self {
+ inner: Default::default(),
+ }
+ }
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/derivable_impls.stderr b/src/tools/clippy/tests/ui/derivable_impls.stderr
index 81963c3be..8089f5ea0 100644
--- a/src/tools/clippy/tests/ui/derivable_impls.stderr
+++ b/src/tools/clippy/tests/ui/derivable_impls.stderr
@@ -14,7 +14,8 @@ LL | | }
= help: remove the manual implementation...
help: ...and instead derive it
|
-LL | #[derive(Default)]
+LL + #[derive(Default)]
+LL | struct FooDefault<'a> {
|
error: this `impl` can be derived
@@ -30,7 +31,8 @@ LL | | }
= help: remove the manual implementation...
help: ...and instead derive it
|
-LL | #[derive(Default)]
+LL + #[derive(Default)]
+LL | struct TupleDefault(bool, i32, u64);
|
error: this `impl` can be derived
@@ -46,7 +48,8 @@ LL | | }
= help: remove the manual implementation...
help: ...and instead derive it
|
-LL | #[derive(Default)]
+LL + #[derive(Default)]
+LL | struct StrDefault<'a>(&'a str);
|
error: this `impl` can be derived
@@ -62,7 +65,8 @@ LL | | }
= help: remove the manual implementation...
help: ...and instead derive it
|
-LL | #[derive(Default)]
+LL + #[derive(Default)]
+LL | struct Y(u32);
|
error: this `impl` can be derived
@@ -78,7 +82,8 @@ LL | | }
= help: remove the manual implementation...
help: ...and instead derive it
|
-LL | #[derive(Default)]
+LL + #[derive(Default)]
+LL | struct WithoutSelfCurly {
|
error: this `impl` can be derived
@@ -94,7 +99,8 @@ LL | | }
= help: remove the manual implementation...
help: ...and instead derive it
|
-LL | #[derive(Default)]
+LL + #[derive(Default)]
+LL | struct WithoutSelfParan(bool);
|
error: this `impl` can be derived
@@ -110,7 +116,8 @@ LL | | }
= help: remove the manual implementation...
help: ...and instead derive it
|
-LL | #[derive(Default)]
+LL + #[derive(Default)]
+LL | pub struct RepeatDefault1 {
|
error: this `impl` can be derived
@@ -126,7 +133,8 @@ LL | | }
= help: remove the manual implementation...
help: ...and instead derive it...
|
-LL | #[derive(Default)]
+LL + #[derive(Default)]
+LL | pub enum SimpleEnum {
|
help: ...and mark the default variant
|
diff --git a/src/tools/clippy/tests/ui/doc_unsafe.rs b/src/tools/clippy/tests/ui/doc_unsafe.rs
index b91f7aa0d..30674ce37 100644
--- a/src/tools/clippy/tests/ui/doc_unsafe.rs
+++ b/src/tools/clippy/tests/ui/doc_unsafe.rs
@@ -1,9 +1,9 @@
-// aux-build:doc_unsafe_macros.rs
+// aux-build:proc_macros.rs
#![allow(clippy::let_unit_value)]
-#[macro_use]
-extern crate doc_unsafe_macros;
+extern crate proc_macros;
+use proc_macros::external;
/// This is not sufficiently documented
pub unsafe fn destroy_the_planet() {
@@ -105,7 +105,11 @@ macro_rules! very_unsafe {
very_unsafe!();
// we don't lint code from external macros
-undocd_unsafe!();
+external! {
+ pub unsafe fn oy_vey() {
+ unimplemented!();
+ }
+}
fn main() {
unsafe {
diff --git a/src/tools/clippy/tests/ui/double_must_use.rs b/src/tools/clippy/tests/ui/double_must_use.rs
index 05e087b08..26a387b3c 100644
--- a/src/tools/clippy/tests/ui/double_must_use.rs
+++ b/src/tools/clippy/tests/ui/double_must_use.rs
@@ -21,6 +21,17 @@ pub fn must_use_with_note() -> Result<(), ()> {
unimplemented!();
}
+// vvvv Should not lint (#10486)
+#[must_use]
+async fn async_must_use() -> usize {
+ unimplemented!();
+}
+
+#[must_use]
+async fn async_must_use_result() -> Result<(), ()> {
+ Ok(())
+}
+
fn main() {
must_use_result();
must_use_tuple();
diff --git a/src/tools/clippy/tests/ui/double_must_use.stderr b/src/tools/clippy/tests/ui/double_must_use.stderr
index 3d34557a8..49ab2ea3e 100644
--- a/src/tools/clippy/tests/ui/double_must_use.stderr
+++ b/src/tools/clippy/tests/ui/double_must_use.stderr
@@ -23,5 +23,13 @@ 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
+error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
+ --> $DIR/double_must_use.rs:31:1
+ |
+LL | async fn async_must_use_result() -> Result<(), ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: either add some descriptive text or remove the attribute
+
+error: aborting due to 4 previous errors
diff --git a/src/tools/clippy/tests/ui/empty_loop.rs b/src/tools/clippy/tests/ui/empty_loop.rs
index 8fd7697eb..6a8e6b550 100644
--- a/src/tools/clippy/tests/ui/empty_loop.rs
+++ b/src/tools/clippy/tests/ui/empty_loop.rs
@@ -1,9 +1,9 @@
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::empty_loop)]
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
fn should_trigger() {
loop {}
@@ -16,6 +16,7 @@ fn should_trigger() {
}
}
+#[inline_macros]
fn should_not_trigger() {
loop {
panic!("This is fine")
@@ -38,14 +39,10 @@ fn should_not_trigger() {
loop {}
// We don't lint loops inside macros
- macro_rules! foo {
- () => {
- loop {}
- };
- }
+ inline!(loop {});
// We don't lint external macros
- foofoo!()
+ external!(loop {});
}
fn main() {}
diff --git a/src/tools/clippy/tests/ui/equatable_if_let.fixed b/src/tools/clippy/tests/ui/equatable_if_let.fixed
index 9af2ba962..007702ab5 100644
--- a/src/tools/clippy/tests/ui/equatable_if_let.fixed
+++ b/src/tools/clippy/tests/ui/equatable_if_let.fixed
@@ -1,11 +1,11 @@
// run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)]
#![warn(clippy::equatable_if_let)]
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
use std::cmp::Ordering;
@@ -44,6 +44,7 @@ impl PartialEq for NotStructuralEq {
}
}
+#[inline_macros]
fn main() {
let a = 2;
let b = 3;
@@ -78,14 +79,9 @@ fn main() {
if Some(g) == Some(NotStructuralEq::A) {}
if matches!(h, NoPartialEqStruct { a: 2, b: false }) {}
- macro_rules! m1 {
- (x) => {
- "abc"
- };
- }
- if "abc" == m1!(x) {
+ if "abc" == inline!("abc") {
println!("OK");
}
- equatable_if_let!(a);
+ external!({ if let 2 = $a {} });
}
diff --git a/src/tools/clippy/tests/ui/equatable_if_let.rs b/src/tools/clippy/tests/ui/equatable_if_let.rs
index c3626c081..3bda79776 100644
--- a/src/tools/clippy/tests/ui/equatable_if_let.rs
+++ b/src/tools/clippy/tests/ui/equatable_if_let.rs
@@ -1,11 +1,11 @@
// run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)]
#![warn(clippy::equatable_if_let)]
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
use std::cmp::Ordering;
@@ -44,6 +44,7 @@ impl PartialEq for NotStructuralEq {
}
}
+#[inline_macros]
fn main() {
let a = 2;
let b = 3;
@@ -78,14 +79,9 @@ fn main() {
if let Some(NotStructuralEq::A) = Some(g) {}
if let NoPartialEqStruct { a: 2, b: false } = h {}
- macro_rules! m1 {
- (x) => {
- "abc"
- };
- }
- if let m1!(x) = "abc" {
+ if let inline!("abc") = "abc" {
println!("OK");
}
- equatable_if_let!(a);
+ external!({ if let 2 = $a {} });
}
diff --git a/src/tools/clippy/tests/ui/equatable_if_let.stderr b/src/tools/clippy/tests/ui/equatable_if_let.stderr
index 40ca75b8d..a72d87bb7 100644
--- a/src/tools/clippy/tests/ui/equatable_if_let.stderr
+++ b/src/tools/clippy/tests/ui/equatable_if_let.stderr
@@ -1,5 +1,5 @@
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:59:8
+ --> $DIR/equatable_if_let.rs:60:8
|
LL | if let 2 = a {}
| ^^^^^^^^^ help: try: `a == 2`
@@ -7,82 +7,82 @@ LL | if let 2 = a {}
= note: `-D clippy::equatable-if-let` implied by `-D warnings`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:60:8
+ --> $DIR/equatable_if_let.rs:61:8
|
LL | if let Ordering::Greater = a.cmp(&b) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:61:8
+ --> $DIR/equatable_if_let.rs:62:8
|
LL | if let Some(2) = c {}
| ^^^^^^^^^^^^^^^ help: try: `c == Some(2)`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:62:8
+ --> $DIR/equatable_if_let.rs:63:8
|
LL | if let Struct { a: 2, b: false } = d {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:63:8
+ --> $DIR/equatable_if_let.rs:64:8
|
LL | if let Enum::TupleVariant(32, 64) = e {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:64:8
+ --> $DIR/equatable_if_let.rs:65:8
|
LL | if let Enum::RecordVariant { a: 64, b: 32 } = e {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:65:8
+ --> $DIR/equatable_if_let.rs:66:8
|
LL | if let Enum::UnitVariant = e {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:66:8
+ --> $DIR/equatable_if_let.rs:67:8
|
LL | if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })`
error: this pattern matching can be expressed using `matches!`
- --> $DIR/equatable_if_let.rs:75:8
+ --> $DIR/equatable_if_let.rs:76:8
|
LL | if let NotPartialEq::A = f {}
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(f, NotPartialEq::A)`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:76:8
+ --> $DIR/equatable_if_let.rs:77:8
|
LL | if let NotStructuralEq::A = g {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A`
error: this pattern matching can be expressed using `matches!`
- --> $DIR/equatable_if_let.rs:77:8
+ --> $DIR/equatable_if_let.rs:78:8
|
LL | if let Some(NotPartialEq::A) = Some(f) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(Some(f), Some(NotPartialEq::A))`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:78:8
+ --> $DIR/equatable_if_let.rs:79:8
|
LL | if let Some(NotStructuralEq::A) = Some(g) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)`
error: this pattern matching can be expressed using `matches!`
- --> $DIR/equatable_if_let.rs:79:8
+ --> $DIR/equatable_if_let.rs:80:8
|
LL | if let NoPartialEqStruct { a: 2, b: false } = h {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(h, NoPartialEqStruct { a: 2, b: false })`
error: this pattern matching can be expressed using equality
- --> $DIR/equatable_if_let.rs:86:8
+ --> $DIR/equatable_if_let.rs:82:8
|
-LL | if let m1!(x) = "abc" {
- | ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)`
+LL | if let inline!("abc") = "abc" {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"abc" == inline!("abc")`
error: aborting due to 14 previous errors
diff --git a/src/tools/clippy/tests/ui/erasing_op.rs b/src/tools/clippy/tests/ui/erasing_op.rs
index ae2fad008..74985029e 100644
--- a/src/tools/clippy/tests/ui/erasing_op.rs
+++ b/src/tools/clippy/tests/ui/erasing_op.rs
@@ -31,9 +31,7 @@ impl core::ops::Mul<i32> for Vec1 {
#[allow(clippy::no_effect)]
#[warn(clippy::erasing_op)]
-fn main() {
- let x: u8 = 0;
-
+fn test(x: u8) {
x * 0;
0 & x;
0 / x;
@@ -41,3 +39,7 @@ fn main() {
0 * Vec1 { x: 5 };
Vec1 { x: 5 } * 0;
}
+
+fn main() {
+ test(0)
+}
diff --git a/src/tools/clippy/tests/ui/erasing_op.stderr b/src/tools/clippy/tests/ui/erasing_op.stderr
index 165ed9bfe..979412523 100644
--- a/src/tools/clippy/tests/ui/erasing_op.stderr
+++ b/src/tools/clippy/tests/ui/erasing_op.stderr
@@ -1,5 +1,5 @@
error: this operation will always return zero. This is likely not the intended outcome
- --> $DIR/erasing_op.rs:37:5
+ --> $DIR/erasing_op.rs:35:5
|
LL | x * 0;
| ^^^^^
@@ -7,25 +7,25 @@ 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:38:5
+ --> $DIR/erasing_op.rs:36:5
|
LL | 0 & x;
| ^^^^^
error: this operation will always return zero. This is likely not the intended outcome
- --> $DIR/erasing_op.rs:39:5
+ --> $DIR/erasing_op.rs:37:5
|
LL | 0 / x;
| ^^^^^
error: this operation will always return zero. This is likely not the intended outcome
- --> $DIR/erasing_op.rs:41:5
+ --> $DIR/erasing_op.rs:39:5
|
LL | 0 * Vec1 { x: 5 };
| ^^^^^^^^^^^^^^^^^
error: this operation will always return zero. This is likely not the intended outcome
- --> $DIR/erasing_op.rs:42:5
+ --> $DIR/erasing_op.rs:40:5
|
LL | Vec1 { x: 5 } * 0;
| ^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters.fixed b/src/tools/clippy/tests/ui/extra_unused_type_parameters.fixed
new file mode 100644
index 000000000..19e718625
--- /dev/null
+++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters.fixed
@@ -0,0 +1,105 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_lifetimes)]
+#![warn(clippy::extra_unused_type_parameters)]
+
+fn unused_ty(x: u8) {
+ unimplemented!()
+}
+
+fn unused_multi(x: u8) {
+ unimplemented!()
+}
+
+fn unused_with_lt<'a>(x: &'a u8) {
+ unimplemented!()
+}
+
+fn used_ty<T>(x: T, y: u8) {}
+
+fn used_ref<'a, T>(x: &'a T) {}
+
+fn used_ret<T: Default>(x: u8) -> T {
+ T::default()
+}
+
+fn unused_bounded<U>(x: U) {
+ unimplemented!();
+}
+
+fn some_unused<B, C>(b: B, c: C) {
+ unimplemented!();
+}
+
+fn used_opaque<A>(iter: impl Iterator<Item = A>) -> usize {
+ iter.count()
+}
+
+fn used_ret_opaque<A>() -> impl Iterator<Item = A> {
+ std::iter::empty()
+}
+
+fn used_vec_box<T>(x: Vec<Box<T>>) {}
+
+fn used_body<T: Default + ToString>() -> String {
+ T::default().to_string()
+}
+
+fn used_closure<T: Default + ToString>() -> impl Fn() {
+ || println!("{}", T::default().to_string())
+}
+
+struct S;
+
+impl S {
+ fn unused_ty_impl(&self) {
+ unimplemented!()
+ }
+}
+
+// Don't lint on trait methods
+trait Foo {
+ fn bar<T>(&self);
+}
+
+impl Foo for S {
+ fn bar<T>(&self) {}
+}
+
+fn skip_index<A, Iter>(iter: Iter, index: usize) -> impl Iterator<Item = A>
+where
+ Iter: Iterator<Item = A>,
+{
+ iter.enumerate()
+ .filter_map(move |(i, a)| if i == index { None } else { Some(a) })
+}
+
+fn unused_opaque(dummy: impl Default) {
+ unimplemented!()
+}
+
+mod unexported_trait_bounds {
+ mod private {
+ pub trait Private {}
+ }
+
+ fn priv_trait_bound<T: private::Private>() {
+ unimplemented!();
+ }
+
+ fn unused_with_priv_trait_bound<T: private::Private>() {
+ unimplemented!();
+ }
+}
+
+mod issue10319 {
+ fn assert_send<T: Send>() {}
+
+ fn assert_send_where<T>()
+ where
+ T: Send,
+ {
+ }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs b/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs
index 480174342..e53bb587e 100644
--- a/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs
+++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs
@@ -1,3 +1,5 @@
+// run-rustfix
+
#![allow(unused, clippy::needless_lifetimes)]
#![warn(clippy::extra_unused_type_parameters)]
@@ -21,14 +23,7 @@ fn used_ret<T: Default>(x: u8) -> T {
T::default()
}
-fn unused_bounded<T: Default, U>(x: U) {
- unimplemented!();
-}
-
-fn unused_where_clause<T, U>(x: U)
-where
- T: Default,
-{
+fn unused_bounded<T: Default, U, V: Default>(x: U) {
unimplemented!();
}
diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr b/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr
index 86c88fc9b..c042a5a22 100644
--- a/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr
+++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr
@@ -1,75 +1,64 @@
-error: type parameter goes unused in function definition
- --> $DIR/extra_unused_type_parameters.rs:4:13
+error: type parameter `T` goes unused in function definition
+ --> $DIR/extra_unused_type_parameters.rs:6:13
|
LL | fn unused_ty<T>(x: u8) {
- | ^^^
+ | ^^^ help: consider removing the parameter
|
- = help: consider removing the parameter
= note: `-D clippy::extra-unused-type-parameters` implied by `-D warnings`
-error: type parameters go unused in function definition
- --> $DIR/extra_unused_type_parameters.rs:8:16
+error: type parameters go unused in function definition: T, U
+ --> $DIR/extra_unused_type_parameters.rs:10:16
|
LL | fn unused_multi<T, U>(x: u8) {
- | ^^^^^^
- |
- = help: consider removing the parameters
+ | ^^^^^^ help: consider removing the parameters
-error: type parameter goes unused in function definition
- --> $DIR/extra_unused_type_parameters.rs:12:23
+error: type parameter `T` goes unused in function definition
+ --> $DIR/extra_unused_type_parameters.rs:14:21
|
LL | fn unused_with_lt<'a, T>(x: &'a u8) {
- | ^
- |
- = help: consider removing the parameter
+ | ^^^ help: consider removing the parameter
-error: type parameter goes unused in function definition
- --> $DIR/extra_unused_type_parameters.rs:24:19
+error: type parameters go unused in function definition: T, V
+ --> $DIR/extra_unused_type_parameters.rs:26:19
|
-LL | fn unused_bounded<T: Default, U>(x: U) {
- | ^^^^^^^^^^^
+LL | fn unused_bounded<T: Default, U, V: Default>(x: U) {
+ | ^^^^^^^^^^^^ ^^^^^^^^^^^^
|
- = help: consider removing the parameter
-
-error: type parameter goes unused in function definition
- --> $DIR/extra_unused_type_parameters.rs:28:24
+help: consider removing the parameters
|
-LL | fn unused_where_clause<T, U>(x: U)
- | ^^
+LL - fn unused_bounded<T: Default, U, V: Default>(x: U) {
+LL + fn unused_bounded<U>(x: U) {
|
- = help: consider removing the parameter
-error: type parameters go unused in function definition
- --> $DIR/extra_unused_type_parameters.rs:35:16
+error: type parameters go unused in function definition: A, D, E
+ --> $DIR/extra_unused_type_parameters.rs:30:16
|
LL | fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {
- | ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^
+ | ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: consider removing the parameters
+ |
+LL - fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {
+LL + fn some_unused<B, C>(b: B, c: C) {
|
- = help: consider removing the parameters
-error: type parameter goes unused in function definition
- --> $DIR/extra_unused_type_parameters.rs:60:22
+error: type parameter `T` goes unused in function definition
+ --> $DIR/extra_unused_type_parameters.rs:55:22
|
LL | fn unused_ty_impl<T>(&self) {
- | ^^^
- |
- = help: consider removing the parameter
+ | ^^^ help: consider removing the parameter
-error: type parameters go unused in function definition
- --> $DIR/extra_unused_type_parameters.rs:82:17
+error: type parameters go unused in function definition: A, B
+ --> $DIR/extra_unused_type_parameters.rs:77:17
|
LL | fn unused_opaque<A, B>(dummy: impl Default) {
- | ^^^^^^
- |
- = help: consider removing the parameters
+ | ^^^^^^ help: consider removing the parameters
-error: type parameter goes unused in function definition
- --> $DIR/extra_unused_type_parameters.rs:95:58
+error: type parameter `U` goes unused in function definition
+ --> $DIR/extra_unused_type_parameters.rs:90:56
|
LL | fn unused_with_priv_trait_bound<T: private::Private, U>() {
- | ^
- |
- = help: consider removing the parameter
+ | ^^^ help: consider removing the parameter
-error: aborting due to 9 previous errors
+error: aborting due to 8 previous errors
diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs
new file mode 100644
index 000000000..10b39aa8f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs
@@ -0,0 +1,24 @@
+#![warn(clippy::extra_unused_type_parameters)]
+
+fn unused_where_clause<T, U>(x: U)
+where
+ T: Default,
+{
+ unimplemented!();
+}
+
+fn unused_multi_where_clause<T, U, V: Default>(x: U)
+where
+ T: Default,
+{
+ unimplemented!();
+}
+
+fn unused_all_where_clause<T, U: Default, V: Default>()
+where
+ T: Default,
+{
+ unimplemented!();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr
new file mode 100644
index 000000000..a9580cc89
--- /dev/null
+++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr
@@ -0,0 +1,27 @@
+error: type parameter `T` goes unused in function definition
+ --> $DIR/extra_unused_type_parameters_unfixable.rs:3:24
+ |
+LL | fn unused_where_clause<T, U>(x: U)
+ | ^
+ |
+ = help: consider removing the parameter
+ = note: `-D clippy::extra-unused-type-parameters` implied by `-D warnings`
+
+error: type parameters go unused in function definition: T, V
+ --> $DIR/extra_unused_type_parameters_unfixable.rs:10:30
+ |
+LL | fn unused_multi_where_clause<T, U, V: Default>(x: U)
+ | ^ ^^^^^^^^^^
+ |
+ = help: consider removing the parameters
+
+error: type parameters go unused in function definition: T, U, V
+ --> $DIR/extra_unused_type_parameters_unfixable.rs:17:28
+ |
+LL | fn unused_all_where_clause<T, U: Default, V: Default>()
+ | ^ ^^^^^^^^^^ ^^^^^^^^^^
+ |
+ = help: consider removing the parameters
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.rs b/src/tools/clippy/tests/ui/field_reassign_with_default.rs
index 1f989bb12..0e208b3ed 100644
--- a/src/tools/clippy/tests/ui/field_reassign_with_default.rs
+++ b/src/tools/clippy/tests/ui/field_reassign_with_default.rs
@@ -1,12 +1,12 @@
// aux-build:proc_macro_derive.rs
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::field_reassign_with_default)]
#[macro_use]
extern crate proc_macro_derive;
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
// Don't lint on derives that derive `Default`
// See https://github.com/rust-lang/rust-clippy/issues/6545
@@ -36,14 +36,6 @@ struct D {
b: Option<i32>,
}
-macro_rules! m {
- ($key:ident: $value:tt) => {{
- let mut data = $crate::D::default();
- data.$key = Some($value);
- data
- }};
-}
-
/// Implements .next() that returns a different number each time.
struct SideEffect(i32);
@@ -57,6 +49,7 @@ impl SideEffect {
}
}
+#[inline_macros]
fn main() {
// wrong, produces first error in stderr
let mut a: A = Default::default();
@@ -150,7 +143,18 @@ fn main() {
a.i = vec![1];
// Don't lint in external macros
- field_reassign_with_default!();
+ external! {
+ #[derive(Default)]
+ struct A {
+ pub i: i32,
+ pub j: i64,
+ }
+ fn lint() {
+ let mut a: A = Default::default();
+ a.i = 42;
+ a;
+ }
+ }
// be sure suggestion is correct with generics
let mut a: Wrapper<bool> = Default::default();
@@ -160,9 +164,11 @@ fn main() {
a.i = 42;
// Don't lint in macros
- m! {
- a: 42
- };
+ inline!(
+ let mut data = $crate::D::default();
+ data.$a = Some($42);
+ data
+ );
}
mod m {
diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.stderr b/src/tools/clippy/tests/ui/field_reassign_with_default.stderr
index 710bb66a4..da74f9ef9 100644
--- a/src/tools/clippy/tests/ui/field_reassign_with_default.stderr
+++ b/src/tools/clippy/tests/ui/field_reassign_with_default.stderr
@@ -1,132 +1,132 @@
error: field assignment outside of initializer for an instance created with Default::default()
- --> $DIR/field_reassign_with_default.rs:63:5
+ --> $DIR/field_reassign_with_default.rs:56:5
|
LL | a.i = 42;
| ^^^^^^^^^
|
note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments
- --> $DIR/field_reassign_with_default.rs:62:5
+ --> $DIR/field_reassign_with_default.rs:55:5
|
LL | let mut a: A = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: `-D clippy::field-reassign-with-default` implied by `-D warnings`
error: field assignment outside of initializer for an instance created with Default::default()
- --> $DIR/field_reassign_with_default.rs:103:5
+ --> $DIR/field_reassign_with_default.rs:96:5
|
LL | a.j = 43;
| ^^^^^^^^^
|
note: consider initializing the variable with `main::A { j: 43, i: 42 }` and removing relevant reassignments
- --> $DIR/field_reassign_with_default.rs:102:5
+ --> $DIR/field_reassign_with_default.rs:95:5
|
LL | let mut a: A = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
- --> $DIR/field_reassign_with_default.rs:108:5
+ --> $DIR/field_reassign_with_default.rs:101:5
|
LL | a.i = 42;
| ^^^^^^^^^
|
note: consider initializing the variable with `main::A { i: 42, j: 44 }` and removing relevant reassignments
- --> $DIR/field_reassign_with_default.rs:107:5
+ --> $DIR/field_reassign_with_default.rs:100:5
|
LL | let mut a: A = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
- --> $DIR/field_reassign_with_default.rs:114:5
+ --> $DIR/field_reassign_with_default.rs:107:5
|
LL | a.i = 42;
| ^^^^^^^^^
|
note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments
- --> $DIR/field_reassign_with_default.rs:113:5
+ --> $DIR/field_reassign_with_default.rs:106:5
|
LL | let mut a = A::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
- --> $DIR/field_reassign_with_default.rs:124:5
+ --> $DIR/field_reassign_with_default.rs:117:5
|
LL | a.i = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: consider initializing the variable with `main::A { i: Default::default(), ..Default::default() }` and removing relevant reassignments
- --> $DIR/field_reassign_with_default.rs:123:5
+ --> $DIR/field_reassign_with_default.rs:116:5
|
LL | let mut a: A = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
- --> $DIR/field_reassign_with_default.rs:128:5
+ --> $DIR/field_reassign_with_default.rs:121:5
|
LL | a.i = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: consider initializing the variable with `main::A { i: Default::default(), j: 45 }` and removing relevant reassignments
- --> $DIR/field_reassign_with_default.rs:127:5
+ --> $DIR/field_reassign_with_default.rs:120:5
|
LL | let mut a: A = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
- --> $DIR/field_reassign_with_default.rs:150:5
+ --> $DIR/field_reassign_with_default.rs:143:5
|
LL | a.i = vec![1];
| ^^^^^^^^^^^^^^
|
note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments
- --> $DIR/field_reassign_with_default.rs:149:5
+ --> $DIR/field_reassign_with_default.rs:142:5
|
LL | let mut a: C = C::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
- --> $DIR/field_reassign_with_default.rs:157:5
+ --> $DIR/field_reassign_with_default.rs:161:5
|
LL | a.i = true;
| ^^^^^^^^^^^
|
note: consider initializing the variable with `Wrapper::<bool> { i: true }` and removing relevant reassignments
- --> $DIR/field_reassign_with_default.rs:156:5
+ --> $DIR/field_reassign_with_default.rs:160:5
|
LL | let mut a: Wrapper<bool> = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
- --> $DIR/field_reassign_with_default.rs:160:5
+ --> $DIR/field_reassign_with_default.rs:164:5
|
LL | a.i = 42;
| ^^^^^^^^^
|
note: consider initializing the variable with `WrapperMulti::<i32, i64> { i: 42, ..Default::default() }` and removing relevant reassignments
- --> $DIR/field_reassign_with_default.rs:159:5
+ --> $DIR/field_reassign_with_default.rs:163:5
|
LL | let mut a: WrapperMulti<i32, i64> = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
- --> $DIR/field_reassign_with_default.rs:229:13
+ --> $DIR/field_reassign_with_default.rs:235:13
|
LL | f.name = name.len();
| ^^^^^^^^^^^^^^^^^^^^
|
note: consider initializing the variable with `issue6312::ImplDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments
- --> $DIR/field_reassign_with_default.rs:228:13
+ --> $DIR/field_reassign_with_default.rs:234:13
|
LL | let mut f = ImplDropAllCopy::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
- --> $DIR/field_reassign_with_default.rs:245:13
+ --> $DIR/field_reassign_with_default.rs:251:13
|
LL | f.name = name.len();
| ^^^^^^^^^^^^^^^^^^^^
|
note: consider initializing the variable with `issue6312::NoDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments
- --> $DIR/field_reassign_with_default.rs:244:13
+ --> $DIR/field_reassign_with_default.rs:250:13
|
LL | let mut f = NoDropAllCopy::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/format.fixed b/src/tools/clippy/tests/ui/format.fixed
index cd2f70ee8..beedf2c1d 100644
--- a/src/tools/clippy/tests/ui/format.fixed
+++ b/src/tools/clippy/tests/ui/format.fixed
@@ -1,5 +1,4 @@
// run-rustfix
-// aux-build: proc_macro_with_span.rs
#![warn(clippy::useless_format)]
#![allow(
unused_tuple_struct_fields,
@@ -10,8 +9,6 @@
clippy::uninlined_format_args
)]
-extern crate proc_macro_with_span;
-
struct Foo(pub String);
macro_rules! foo {
@@ -90,7 +87,4 @@ fn main() {
let _ = abc.to_string();
let xx = "xx";
let _ = xx.to_string();
-
- // Issue #10148
- println!(proc_macro_with_span::with_span!(""something ""));
}
diff --git a/src/tools/clippy/tests/ui/format.rs b/src/tools/clippy/tests/ui/format.rs
index c22345a79..e805f1818 100644
--- a/src/tools/clippy/tests/ui/format.rs
+++ b/src/tools/clippy/tests/ui/format.rs
@@ -1,5 +1,4 @@
// run-rustfix
-// aux-build: proc_macro_with_span.rs
#![warn(clippy::useless_format)]
#![allow(
unused_tuple_struct_fields,
@@ -10,8 +9,6 @@
clippy::uninlined_format_args
)]
-extern crate proc_macro_with_span;
-
struct Foo(pub String);
macro_rules! foo {
@@ -92,7 +89,4 @@ fn main() {
let _ = format!("{abc}");
let xx = "xx";
let _ = format!("{xx}");
-
- // Issue #10148
- println!(proc_macro_with_span::with_span!(""something ""));
}
diff --git a/src/tools/clippy/tests/ui/format.stderr b/src/tools/clippy/tests/ui/format.stderr
index a0e5d5c8a..0ef0ac655 100644
--- a/src/tools/clippy/tests/ui/format.stderr
+++ b/src/tools/clippy/tests/ui/format.stderr
@@ -1,5 +1,5 @@
error: useless use of `format!`
- --> $DIR/format.rs:22:5
+ --> $DIR/format.rs:19:5
|
LL | format!("foo");
| ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
@@ -7,19 +7,19 @@ LL | format!("foo");
= note: `-D clippy::useless-format` implied by `-D warnings`
error: useless use of `format!`
- --> $DIR/format.rs:23:5
+ --> $DIR/format.rs:20:5
|
LL | format!("{{}}");
| ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:24:5
+ --> $DIR/format.rs:21:5
|
LL | format!("{{}} abc {{}}");
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:25:5
+ --> $DIR/format.rs:22:5
|
LL | / format!(
LL | | r##"foo {{}}
@@ -34,67 +34,67 @@ LL ~ " bar"##.to_string();
|
error: useless use of `format!`
- --> $DIR/format.rs:30:13
+ --> $DIR/format.rs:27:13
|
LL | let _ = format!("");
| ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()`
error: useless use of `format!`
- --> $DIR/format.rs:32:5
+ --> $DIR/format.rs:29:5
|
LL | format!("{}", "foo");
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:40:5
+ --> $DIR/format.rs:37:5
|
LL | format!("{}", arg);
| ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:70:5
+ --> $DIR/format.rs:67:5
|
LL | format!("{}", 42.to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:72:5
+ --> $DIR/format.rs:69:5
|
LL | format!("{}", x.display().to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:76:18
+ --> $DIR/format.rs:73:18
|
LL | let _ = Some(format!("{}", a + "bar"));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
error: useless use of `format!`
- --> $DIR/format.rs:80:22
+ --> $DIR/format.rs:77:22
|
LL | let _s: String = format!("{}", &*v.join("/n"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:86:13
+ --> $DIR/format.rs:83:13
|
LL | let _ = format!("{x}");
| ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:88:13
+ --> $DIR/format.rs:85:13
|
LL | let _ = format!("{y}", y = x);
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:92:13
+ --> $DIR/format.rs:89:13
|
LL | let _ = format!("{abc}");
| ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()`
error: useless use of `format!`
- --> $DIR/format.rs:94:13
+ --> $DIR/format.rs:91:13
|
LL | let _ = format!("{xx}");
| ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()`
diff --git a/src/tools/clippy/tests/ui/format_args_unfixable.rs b/src/tools/clippy/tests/ui/format_args_unfixable.rs
index eb0ac15bf..423bfaf97 100644
--- a/src/tools/clippy/tests/ui/format_args_unfixable.rs
+++ b/src/tools/clippy/tests/ui/format_args_unfixable.rs
@@ -1,4 +1,5 @@
#![warn(clippy::format_in_format_args, clippy::to_string_in_format_args)]
+#![allow(unused)]
#![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::uninlined_format_args)]
use std::io::{stdout, Error, ErrorKind, Write};
@@ -57,3 +58,46 @@ fn main() {
my_macro!();
println!("error: {}", my_other_macro!());
}
+
+macro_rules! _internal {
+ ($($args:tt)*) => {
+ println!("{}", format_args!($($args)*))
+ };
+}
+
+macro_rules! my_println2 {
+ ($target:expr, $($args:tt)+) => {{
+ if $target {
+ _internal!($($args)+)
+ }
+ }};
+}
+
+macro_rules! my_println2_args {
+ ($target:expr, $($args:tt)+) => {{
+ if $target {
+ _internal!("foo: {}", format_args!($($args)+))
+ }
+ }};
+}
+
+fn test2() {
+ let error = Error::new(ErrorKind::Other, "bad thing");
+
+ // None of these should be linted without the config change
+ my_println2!(true, "error: {}", format!("something failed at {}", Location::caller()));
+ my_println2!(
+ true,
+ "{}: {}",
+ error,
+ format!("something failed at {}", Location::caller())
+ );
+
+ my_println2_args!(true, "error: {}", format!("something failed at {}", Location::caller()));
+ my_println2_args!(
+ true,
+ "{}: {}",
+ error,
+ format!("something failed at {}", Location::caller())
+ );
+}
diff --git a/src/tools/clippy/tests/ui/format_args_unfixable.stderr b/src/tools/clippy/tests/ui/format_args_unfixable.stderr
index b291d475a..c1be48c3b 100644
--- a/src/tools/clippy/tests/ui/format_args_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/format_args_unfixable.stderr
@@ -1,5 +1,5 @@
error: `format!` in `println!` args
- --> $DIR/format_args_unfixable.rs:25:5
+ --> $DIR/format_args_unfixable.rs:26:5
|
LL | println!("error: {}", format!("something failed at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -9,7 +9,7 @@ LL | println!("error: {}", format!("something failed at {}", Location::calle
= note: `-D clippy::format-in-format-args` implied by `-D warnings`
error: `format!` in `println!` args
- --> $DIR/format_args_unfixable.rs:26:5
+ --> $DIR/format_args_unfixable.rs:27:5
|
LL | println!("{}: {}", error, format!("something failed at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -18,7 +18,7 @@ LL | println!("{}: {}", error, format!("something failed at {}", Location::c
= help: or consider changing `format!` to `format_args!`
error: `format!` in `println!` args
- --> $DIR/format_args_unfixable.rs:27:5
+ --> $DIR/format_args_unfixable.rs:28:5
|
LL | println!("{:?}: {}", error, format!("something failed at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -27,7 +27,7 @@ LL | println!("{:?}: {}", error, format!("something failed at {}", Location:
= help: or consider changing `format!` to `format_args!`
error: `format!` in `println!` args
- --> $DIR/format_args_unfixable.rs:28:5
+ --> $DIR/format_args_unfixable.rs:29:5
|
LL | println!("{{}}: {}", format!("something failed at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -36,7 +36,7 @@ LL | println!("{{}}: {}", format!("something failed at {}", Location::caller
= help: or consider changing `format!` to `format_args!`
error: `format!` in `println!` args
- --> $DIR/format_args_unfixable.rs:29:5
+ --> $DIR/format_args_unfixable.rs:30:5
|
LL | println!(r#"error: "{}""#, format!("something failed at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -45,7 +45,7 @@ LL | println!(r#"error: "{}""#, format!("something failed at {}", Location::
= help: or consider changing `format!` to `format_args!`
error: `format!` in `println!` args
- --> $DIR/format_args_unfixable.rs:30:5
+ --> $DIR/format_args_unfixable.rs:31:5
|
LL | println!("error: {}", format!(r#"something failed at "{}""#, Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -54,7 +54,7 @@ LL | println!("error: {}", format!(r#"something failed at "{}""#, Location::
= help: or consider changing `format!` to `format_args!`
error: `format!` in `println!` args
- --> $DIR/format_args_unfixable.rs:31:5
+ --> $DIR/format_args_unfixable.rs:32:5
|
LL | println!("error: {}", format!("something failed at {} {0}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -63,7 +63,7 @@ LL | println!("error: {}", format!("something failed at {} {0}", Location::c
= help: or consider changing `format!` to `format_args!`
error: `format!` in `format!` args
- --> $DIR/format_args_unfixable.rs:32:13
+ --> $DIR/format_args_unfixable.rs:33:13
|
LL | let _ = format!("error: {}", format!("something failed at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -72,7 +72,7 @@ LL | let _ = format!("error: {}", format!("something failed at {}", Location
= help: or consider changing `format!` to `format_args!`
error: `format!` in `write!` args
- --> $DIR/format_args_unfixable.rs:33:13
+ --> $DIR/format_args_unfixable.rs:34:13
|
LL | let _ = write!(
| _____________^
@@ -86,7 +86,7 @@ LL | | );
= help: or consider changing `format!` to `format_args!`
error: `format!` in `writeln!` args
- --> $DIR/format_args_unfixable.rs:38:13
+ --> $DIR/format_args_unfixable.rs:39:13
|
LL | let _ = writeln!(
| _____________^
@@ -100,7 +100,7 @@ LL | | );
= help: or consider changing `format!` to `format_args!`
error: `format!` in `print!` args
- --> $DIR/format_args_unfixable.rs:43:5
+ --> $DIR/format_args_unfixable.rs:44:5
|
LL | print!("error: {}", format!("something failed at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -109,7 +109,7 @@ LL | print!("error: {}", format!("something failed at {}", Location::caller(
= help: or consider changing `format!` to `format_args!`
error: `format!` in `eprint!` args
- --> $DIR/format_args_unfixable.rs:44:5
+ --> $DIR/format_args_unfixable.rs:45:5
|
LL | eprint!("error: {}", format!("something failed at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -118,7 +118,7 @@ LL | eprint!("error: {}", format!("something failed at {}", Location::caller
= help: or consider changing `format!` to `format_args!`
error: `format!` in `eprintln!` args
- --> $DIR/format_args_unfixable.rs:45:5
+ --> $DIR/format_args_unfixable.rs:46:5
|
LL | eprintln!("error: {}", format!("something failed at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -127,7 +127,7 @@ LL | eprintln!("error: {}", format!("something failed at {}", Location::call
= help: or consider changing `format!` to `format_args!`
error: `format!` in `format_args!` args
- --> $DIR/format_args_unfixable.rs:46:13
+ --> $DIR/format_args_unfixable.rs:47:13
|
LL | let _ = format_args!("error: {}", format!("something failed at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -136,7 +136,7 @@ LL | let _ = format_args!("error: {}", format!("something failed at {}", Loc
= help: or consider changing `format!` to `format_args!`
error: `format!` in `assert!` args
- --> $DIR/format_args_unfixable.rs:47:5
+ --> $DIR/format_args_unfixable.rs:48:5
|
LL | assert!(true, "error: {}", format!("something failed at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -145,7 +145,7 @@ LL | assert!(true, "error: {}", format!("something failed at {}", Location::
= help: or consider changing `format!` to `format_args!`
error: `format!` in `assert_eq!` args
- --> $DIR/format_args_unfixable.rs:48:5
+ --> $DIR/format_args_unfixable.rs:49:5
|
LL | assert_eq!(0, 0, "error: {}", format!("something failed at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -154,7 +154,7 @@ LL | assert_eq!(0, 0, "error: {}", format!("something failed at {}", Locatio
= help: or consider changing `format!` to `format_args!`
error: `format!` in `assert_ne!` args
- --> $DIR/format_args_unfixable.rs:49:5
+ --> $DIR/format_args_unfixable.rs:50:5
|
LL | assert_ne!(0, 0, "error: {}", format!("something failed at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -163,7 +163,7 @@ LL | assert_ne!(0, 0, "error: {}", format!("something failed at {}", Locatio
= help: or consider changing `format!` to `format_args!`
error: `format!` in `panic!` args
- --> $DIR/format_args_unfixable.rs:50:5
+ --> $DIR/format_args_unfixable.rs:51:5
|
LL | panic!("error: {}", format!("something failed at {}", Location::caller()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.rs b/src/tools/clippy/tests/ui/ifs_same_cond.rs
index 9850fc091..9ce9a8762 100644
--- a/src/tools/clippy/tests/ui/ifs_same_cond.rs
+++ b/src/tools/clippy/tests/ui/ifs_same_cond.rs
@@ -43,4 +43,30 @@ fn ifs_same_cond() {
}
}
+fn issue10272() {
+ let a = String::from("ha");
+ if a.contains("ah") {
+ } else if a.contains("ah") {
+ // Trigger this lint
+ } else if a.contains("ha") {
+ } else if a == "wow" {
+ }
+
+ let p: *mut i8 = std::ptr::null_mut();
+ if p.is_null() {
+ } else if p.align_offset(0) == 0 {
+ } else if p.is_null() {
+ // ok, p is mutable pointer
+ } else {
+ }
+
+ let x = std::cell::Cell::new(true);
+ if x.get() {
+ } else if !x.take() {
+ } else if x.get() {
+ // ok, x is interior mutable type
+ } else {
+ }
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.stderr b/src/tools/clippy/tests/ui/ifs_same_cond.stderr
index 411308732..9519f6904 100644
--- a/src/tools/clippy/tests/ui/ifs_same_cond.stderr
+++ b/src/tools/clippy/tests/ui/ifs_same_cond.stderr
@@ -35,5 +35,17 @@ note: same as this
LL | if 2 * a == 1 {
| ^^^^^^^^^^
-error: aborting due to 3 previous errors
+error: this `if` has the same condition as a previous `if`
+ --> $DIR/ifs_same_cond.rs:49:15
+ |
+LL | } else if a.contains("ah") {
+ | ^^^^^^^^^^^^^^^^
+ |
+note: same as this
+ --> $DIR/ifs_same_cond.rs:48:8
+ |
+LL | if a.contains("ah") {
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
diff --git a/src/tools/clippy/tests/ui/impl_trait_in_params.stderr b/src/tools/clippy/tests/ui/impl_trait_in_params.stderr
index acfcc2144..803837435 100644
--- a/src/tools/clippy/tests/ui/impl_trait_in_params.stderr
+++ b/src/tools/clippy/tests/ui/impl_trait_in_params.stderr
@@ -5,7 +5,7 @@ LL | pub fn a(_: impl Trait) {}
| ^^^^^^^^^^
|
= note: `-D clippy::impl-trait-in-params` implied by `-D warnings`
-help: add a type paremeter
+help: add a type parameter
|
LL | pub fn a<{ /* Generic name */ }: Trait>(_: impl Trait) {}
| +++++++++++++++++++++++++++++++
@@ -16,7 +16,7 @@ error: '`impl Trait` used as a function parameter'
LL | pub fn c<C: Trait>(_: C, _: impl Trait) {}
| ^^^^^^^^^^
|
-help: add a type paremeter
+help: add a type parameter
|
LL | pub fn c<C: Trait, { /* Generic name */ }: Trait>(_: C, _: impl Trait) {}
| +++++++++++++++++++++++++++++++
diff --git a/src/tools/clippy/tests/ui/implicit_clone.fixed b/src/tools/clippy/tests/ui/implicit_clone.fixed
index 51b1afbe5..8ccc3da7b 100644
--- a/src/tools/clippy/tests/ui/implicit_clone.fixed
+++ b/src/tools/clippy/tests/ui/implicit_clone.fixed
@@ -87,7 +87,7 @@ fn main() {
let kitten = Kitten {};
let _ = kitten.clone();
let _ = own_same_from_ref(&kitten);
- // this shouln't lint
+ // this shouldn't lint
let _ = kitten.to_vec();
// we expect no lints for this
diff --git a/src/tools/clippy/tests/ui/implicit_clone.rs b/src/tools/clippy/tests/ui/implicit_clone.rs
index 8a9027433..593333126 100644
--- a/src/tools/clippy/tests/ui/implicit_clone.rs
+++ b/src/tools/clippy/tests/ui/implicit_clone.rs
@@ -87,7 +87,7 @@ fn main() {
let kitten = Kitten {};
let _ = kitten.to_owned();
let _ = own_same_from_ref(&kitten);
- // this shouln't lint
+ // this shouldn't lint
let _ = kitten.to_vec();
// we expect no lints for this
diff --git a/src/tools/clippy/tests/ui/implicit_hasher.rs b/src/tools/clippy/tests/ui/implicit_hasher.rs
index fd96ca3f4..35d08a07b 100644
--- a/src/tools/clippy/tests/ui/implicit_hasher.rs
+++ b/src/tools/clippy/tests/ui/implicit_hasher.rs
@@ -1,9 +1,11 @@
-// aux-build:implicit_hasher_macros.rs
+// aux-build:proc_macros.rs
+
#![deny(clippy::implicit_hasher)]
#![allow(unused)]
#[macro_use]
-extern crate implicit_hasher_macros;
+extern crate proc_macros;
+use proc_macros::external;
use std::cmp::Eq;
use std::collections::{HashMap, HashSet};
@@ -68,22 +70,19 @@ impl<S: BuildHasher + Default> Foo<i64> for HashSet<String, S> {
pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
-macro_rules! gen {
- (impl) => {
+#[proc_macros::inline_macros]
+pub mod gen {
+ use super::*;
+ inline! {
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>) {}
- };
+ pub fn bar(_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
@@ -94,7 +93,9 @@ pub mod test_macro;
__implicit_hasher_test_macro!(impl<K, V> for HashMap<K, V> where V: test_macro::A);
// #4260
-implicit_hasher_fn!();
+external! {
+ pub fn f(input: &HashMap<u32, u32>) {}
+}
// #7712
pub async fn election_vote(_data: HashMap<i32, i32>) {}
diff --git a/src/tools/clippy/tests/ui/implicit_hasher.stderr b/src/tools/clippy/tests/ui/implicit_hasher.stderr
index 59b0fba2a..83b46de2e 100644
--- a/src/tools/clippy/tests/ui/implicit_hasher.stderr
+++ b/src/tools/clippy/tests/ui/implicit_hasher.stderr
@@ -1,11 +1,11 @@
error: impl for `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:16:35
+ --> $DIR/implicit_hasher.rs:18: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
+ --> $DIR/implicit_hasher.rs:3:9
|
LL | #![deny(clippy::implicit_hasher)]
| ^^^^^^^^^^^^^^^^^^^^^^^
@@ -19,7 +19,7 @@ LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default:
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: impl for `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:25:36
+ --> $DIR/implicit_hasher.rs:27:36
|
LL | impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) {
| ^^^^^^^^^^^^^
@@ -34,7 +34,7 @@ LL | ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Defa
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: impl for `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:30:19
+ --> $DIR/implicit_hasher.rs:32:19
|
LL | impl Foo<i16> for HashMap<String, String> {
| ^^^^^^^^^^^^^^^^^^^^^^^
@@ -49,7 +49,7 @@ LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default:
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: impl for `HashSet` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:47:32
+ --> $DIR/implicit_hasher.rs:49:32
|
LL | impl<T: Hash + Eq> Foo<i8> for HashSet<T> {
| ^^^^^^^^^^
@@ -64,7 +64,7 @@ LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default:
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: impl for `HashSet` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:52:19
+ --> $DIR/implicit_hasher.rs:54:19
|
LL | impl Foo<i16> for HashSet<String> {
| ^^^^^^^^^^^^^^^
@@ -79,7 +79,7 @@ LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default:
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: parameter of type `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:69:23
+ --> $DIR/implicit_hasher.rs:71:23
|
LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
| ^^^^^^^^^^^^^^^^^
@@ -90,7 +90,7 @@ LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32, S>, _s
| +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~
error: parameter of type `HashSet` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:69:53
+ --> $DIR/implicit_hasher.rs:71:53
|
LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
| ^^^^^^^^^^^^
@@ -101,15 +101,12 @@ LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set:
| +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~
error: impl for `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:73:43
+ --> $DIR/implicit_hasher.rs:77: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 the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_mod_gen` (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> {
@@ -120,37 +117,31 @@ LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10,
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: parameter of type `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:81:33
+ --> $DIR/implicit_hasher.rs:83:31
|
-LL | pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
- | ^^^^^^^^^^^^^^^^^
-...
-LL | gen!(fn bar);
- | ------------ in this macro invocation
+LL | pub fn bar(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+ | ^^^^^^^^^^^^^^^^^
|
- = note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_mod_gen` (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>) {}
- | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~
+LL | pub fn bar<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
+ --> $DIR/implicit_hasher.rs:83:61
|
-LL | pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
- | ^^^^^^^^^^^^
-...
-LL | gen!(fn bar);
- | ------------ in this macro invocation
+LL | pub fn bar(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+ | ^^^^^^^^^^^^
|
- = note: this error originates in the macro `gen` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_mod_gen` (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>) {}
- | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~
+LL | pub fn bar<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32, S>) {}
+ | +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~
error: parameter of type `HashMap` should be generalized over different hashers
- --> $DIR/implicit_hasher.rs:100:35
+ --> $DIR/implicit_hasher.rs:101:35
|
LL | pub async fn election_vote(_data: HashMap<i32, i32>) {}
| ^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
index 74ba2f1c5..5aaa00f85 100644
--- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
+++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
@@ -1,10 +1,14 @@
// run-rustfix
+// aux-build:proc_macros.rs
+
#![warn(clippy::inconsistent_struct_constructor)]
#![allow(clippy::redundant_field_names)]
#![allow(clippy::unnecessary_operation)]
#![allow(clippy::no_effect)]
#![allow(dead_code)]
+extern crate proc_macros;
+
#[derive(Default)]
struct Foo {
x: i32,
@@ -12,18 +16,10 @@ struct Foo {
z: i32,
}
-macro_rules! new_foo {
- () => {
- let x = 1;
- let y = 1;
- let z = 1;
- Foo { y, x, z }
- };
-}
-
mod without_base {
use super::Foo;
+ #[proc_macros::inline_macros]
fn test() {
let x = 1;
let y = 1;
@@ -34,7 +30,12 @@ mod without_base {
// Should NOT lint.
// issue #7069.
- new_foo!();
+ inline!({
+ let x = 1;
+ let y = 1;
+ let z = 1;
+ Foo { y, x, z }
+ });
// Should NOT lint because the order is the same as in the definition.
Foo { x, y, z };
diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
index ba96e1e33..2b2dd7f59 100644
--- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
+++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
@@ -1,10 +1,14 @@
// run-rustfix
+// aux-build:proc_macros.rs
+
#![warn(clippy::inconsistent_struct_constructor)]
#![allow(clippy::redundant_field_names)]
#![allow(clippy::unnecessary_operation)]
#![allow(clippy::no_effect)]
#![allow(dead_code)]
+extern crate proc_macros;
+
#[derive(Default)]
struct Foo {
x: i32,
@@ -12,18 +16,10 @@ struct Foo {
z: i32,
}
-macro_rules! new_foo {
- () => {
- let x = 1;
- let y = 1;
- let z = 1;
- Foo { y, x, z }
- };
-}
-
mod without_base {
use super::Foo;
+ #[proc_macros::inline_macros]
fn test() {
let x = 1;
let y = 1;
@@ -34,7 +30,12 @@ mod without_base {
// Should NOT lint.
// issue #7069.
- new_foo!();
+ inline!({
+ let x = 1;
+ let y = 1;
+ let z = 1;
+ Foo { y, x, z }
+ });
// Should NOT lint because the order is the same as in the definition.
Foo { x, y, z };
diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr
index c90189e96..785a6dc9d 100644
--- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr
+++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr
@@ -1,5 +1,5 @@
error: struct constructor field order is inconsistent with struct definition field order
- --> $DIR/inconsistent_struct_constructor.rs:33:9
+ --> $DIR/inconsistent_struct_constructor.rs:29:9
|
LL | Foo { y, x, z };
| ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }`
@@ -7,7 +7,7 @@ LL | Foo { y, x, z };
= note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings`
error: struct constructor field order is inconsistent with struct definition field order
- --> $DIR/inconsistent_struct_constructor.rs:55:9
+ --> $DIR/inconsistent_struct_constructor.rs:56:9
|
LL | / Foo {
LL | | z,
diff --git a/src/tools/clippy/tests/ui/integer_arithmetic.rs b/src/tools/clippy/tests/ui/integer_arithmetic.rs
index 67f24b454..8dfdee662 100644
--- a/src/tools/clippy/tests/ui/integer_arithmetic.rs
+++ b/src/tools/clippy/tests/ui/integer_arithmetic.rs
@@ -4,7 +4,7 @@
#[rustfmt::skip]
fn main() {
let mut i = 1i32;
- let mut var1 = 0i32;
+ let mut var1 = 13i32;
let mut var2 = -1i32;
1 + i;
i * 2;
diff --git a/src/tools/clippy/tests/ui/item_after_statement.rs b/src/tools/clippy/tests/ui/items_after_statement.rs
index 5e92dcab1..f12cb8f22 100644
--- a/src/tools/clippy/tests/ui/item_after_statement.rs
+++ b/src/tools/clippy/tests/ui/items_after_statement.rs
@@ -51,3 +51,20 @@ fn semicolon() {
let _ = S::new(3);
}
+
+fn item_from_macro() {
+ macro_rules! static_assert_size {
+ ($ty:ty, $size:expr) => {
+ const _: [(); $size] = [(); ::std::mem::size_of::<$ty>()];
+ };
+ }
+
+ let _ = 1;
+ static_assert_size!(u32, 4);
+}
+
+fn allow_attribute() {
+ let _ = 1;
+ #[allow(clippy::items_after_statements)]
+ const _: usize = 1;
+}
diff --git a/src/tools/clippy/tests/ui/item_after_statement.stderr b/src/tools/clippy/tests/ui/items_after_statement.stderr
index 2523c53ac..f69635a97 100644
--- a/src/tools/clippy/tests/ui/item_after_statement.stderr
+++ b/src/tools/clippy/tests/ui/items_after_statement.stderr
@@ -1,5 +1,5 @@
error: adding items after statements is confusing, since items exist from the start of the scope
- --> $DIR/item_after_statement.rs:13:5
+ --> $DIR/items_after_statement.rs:13:5
|
LL | / fn foo() {
LL | | println!("foo");
@@ -9,7 +9,7 @@ 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:20:5
+ --> $DIR/items_after_statement.rs:20:5
|
LL | / fn foo() {
LL | | println!("foo");
@@ -17,7 +17,7 @@ LL | | }
| |_____^
error: adding items after statements is confusing, since items exist from the start of the scope
- --> $DIR/item_after_statement.rs:33:13
+ --> $DIR/items_after_statement.rs:33:13
|
LL | / fn say_something() {
LL | | println!("something");
diff --git a/src/tools/clippy/tests/ui/large_enum_variant.rs b/src/tools/clippy/tests/ui/large_enum_variant.rs
index 3b96f09d7..f09f8ae0c 100644
--- a/src/tools/clippy/tests/ui/large_enum_variant.rs
+++ b/src/tools/clippy/tests/ui/large_enum_variant.rs
@@ -1,11 +1,11 @@
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![allow(dead_code)]
#![allow(unused_variables)]
#![warn(clippy::large_enum_variant)]
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::external;
enum LargeEnum {
A(i32),
@@ -155,5 +155,10 @@ enum LargeEnumOfConst {
}
fn main() {
- large_enum_variant!();
+ external!(
+ enum LargeEnumInMacro {
+ A(i32),
+ B([i32; 8000]),
+ }
+ );
}
diff --git a/src/tools/clippy/tests/ui/large_futures.rs b/src/tools/clippy/tests/ui/large_futures.rs
new file mode 100644
index 000000000..4a8ba995d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_futures.rs
@@ -0,0 +1,61 @@
+#![feature(generators)]
+#![warn(clippy::large_futures)]
+#![allow(clippy::future_not_send)]
+#![allow(clippy::manual_async_fn)]
+
+async fn big_fut(_arg: [u8; 1024 * 16]) {}
+
+async fn wait() {
+ let f = async {
+ big_fut([0u8; 1024 * 16]).await;
+ };
+ f.await
+}
+async fn calls_fut(fut: impl std::future::Future<Output = ()>) {
+ loop {
+ wait().await;
+ if true {
+ return fut.await;
+ } else {
+ wait().await;
+ }
+ }
+}
+
+pub async fn test() {
+ let fut = big_fut([0u8; 1024 * 16]);
+ foo().await;
+ calls_fut(fut).await;
+}
+
+pub fn foo() -> impl std::future::Future<Output = ()> {
+ async {
+ let x = [0i32; 1024 * 16];
+ async {}.await;
+ dbg!(x);
+ }
+}
+
+pub async fn lines() {
+ async {
+ let x = [0i32; 1024 * 16];
+ async {}.await;
+ println!("{:?}", x);
+ }
+ .await;
+}
+
+pub async fn macro_expn() {
+ macro_rules! macro_ {
+ () => {
+ async {
+ let x = [0i32; 1024 * 16];
+ async {}.await;
+ println!("macro: {:?}", x);
+ }
+ };
+ }
+ macro_!().await
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/large_futures.stderr b/src/tools/clippy/tests/ui/large_futures.stderr
new file mode 100644
index 000000000..67e0fceff
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_futures.stderr
@@ -0,0 +1,82 @@
+error: large future with a size of 16385 bytes
+ --> $DIR/large_futures.rs:10:9
+ |
+LL | big_fut([0u8; 1024 * 16]).await;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(big_fut([0u8; 1024 * 16]))`
+ |
+ = note: `-D clippy::large-futures` implied by `-D warnings`
+
+error: large future with a size of 16386 bytes
+ --> $DIR/large_futures.rs:12:5
+ |
+LL | f.await
+ | ^ help: consider `Box::pin` on it: `Box::pin(f)`
+
+error: large future with a size of 16387 bytes
+ --> $DIR/large_futures.rs:16:9
+ |
+LL | wait().await;
+ | ^^^^^^ help: consider `Box::pin` on it: `Box::pin(wait())`
+
+error: large future with a size of 16387 bytes
+ --> $DIR/large_futures.rs:20:13
+ |
+LL | wait().await;
+ | ^^^^^^ help: consider `Box::pin` on it: `Box::pin(wait())`
+
+error: large future with a size of 65540 bytes
+ --> $DIR/large_futures.rs:27:5
+ |
+LL | foo().await;
+ | ^^^^^ help: consider `Box::pin` on it: `Box::pin(foo())`
+
+error: large future with a size of 49159 bytes
+ --> $DIR/large_futures.rs:28:5
+ |
+LL | calls_fut(fut).await;
+ | ^^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(calls_fut(fut))`
+
+error: large future with a size of 65540 bytes
+ --> $DIR/large_futures.rs:40:5
+ |
+LL | / async {
+LL | | let x = [0i32; 1024 * 16];
+LL | | async {}.await;
+LL | | println!("{:?}", x);
+LL | | }
+ | |_____^
+ |
+help: consider `Box::pin` on it
+ |
+LL ~ Box::pin(async {
+LL + let x = [0i32; 1024 * 16];
+LL + async {}.await;
+LL + println!("{:?}", x);
+LL + })
+ |
+
+error: large future with a size of 65540 bytes
+ --> $DIR/large_futures.rs:51:13
+ |
+LL | / async {
+LL | | let x = [0i32; 1024 * 16];
+LL | | async {}.await;
+LL | | println!("macro: {:?}", x);
+LL | | }
+ | |_____________^
+...
+LL | macro_!().await
+ | --------- in this macro invocation
+ |
+ = note: this error originates in the macro `macro_` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider `Box::pin` on it
+ |
+LL ~ Box::pin(async {
+LL + let x = [0i32; 1024 * 16];
+LL + async {}.await;
+LL + println!("macro: {:?}", x);
+LL + })
+ |
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.rs b/src/tools/clippy/tests/ui/len_without_is_empty.rs
index b5dec6c46..52aabefae 100644
--- a/src/tools/clippy/tests/ui/len_without_is_empty.rs
+++ b/src/tools/clippy/tests/ui/len_without_is_empty.rs
@@ -282,6 +282,87 @@ impl AsyncLen {
}
}
+// issue #7232
+pub struct AsyncLenWithoutIsEmpty;
+impl AsyncLenWithoutIsEmpty {
+ pub async fn async_task(&self) -> bool {
+ true
+ }
+
+ pub async fn len(&self) -> usize {
+ usize::from(!self.async_task().await)
+ }
+}
+
+// issue #7232
+pub struct AsyncOptionLenWithoutIsEmpty;
+impl AsyncOptionLenWithoutIsEmpty {
+ async fn async_task(&self) -> bool {
+ true
+ }
+
+ pub async fn len(&self) -> Option<usize> {
+ None
+ }
+}
+
+// issue #7232
+pub struct AsyncOptionLenNonIntegral;
+impl AsyncOptionLenNonIntegral {
+ // don't lint
+ pub async fn len(&self) -> Option<String> {
+ None
+ }
+}
+
+// issue #7232
+pub struct AsyncResultLenWithoutIsEmpty;
+impl AsyncResultLenWithoutIsEmpty {
+ async fn async_task(&self) -> bool {
+ true
+ }
+
+ pub async fn len(&self) -> Result<usize, ()> {
+ Err(())
+ }
+}
+
+// issue #7232
+pub struct AsyncOptionLen;
+impl AsyncOptionLen {
+ async fn async_task(&self) -> bool {
+ true
+ }
+
+ pub async fn len(&self) -> Result<usize, ()> {
+ Err(())
+ }
+
+ pub async fn is_empty(&self) -> bool {
+ true
+ }
+}
+
+pub struct AsyncLenSyncIsEmpty;
+impl AsyncLenSyncIsEmpty {
+ pub async fn len(&self) -> u32 {
+ 0
+ }
+
+ pub fn is_empty(&self) -> bool {
+ true
+ }
+}
+
+// issue #9520
+pub struct NonStandardLen;
+impl NonStandardLen {
+ // don't lint
+ pub fn len(&self, something: usize) -> usize {
+ something
+ }
+}
+
// issue #9520
pub struct NonStandardLenAndIsEmptySignature;
impl NonStandardLenAndIsEmptySignature {
@@ -328,4 +409,15 @@ impl NonStandardSignatureWithGenerics {
}
}
+pub struct DifferingErrors;
+impl DifferingErrors {
+ pub fn len(&self) -> Result<usize, u8> {
+ Ok(0)
+ }
+
+ pub fn is_empty(&self) -> Result<bool, u16> {
+ Ok(true)
+ }
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.stderr b/src/tools/clippy/tests/ui/len_without_is_empty.stderr
index 8e890e2e2..1bce1734b 100644
--- a/src/tools/clippy/tests/ui/len_without_is_empty.stderr
+++ b/src/tools/clippy/tests/ui/len_without_is_empty.stderr
@@ -119,5 +119,23 @@ LL | pub fn len(&self) -> Result<usize, ()> {
|
= help: use a custom `Error` type instead
-error: aborting due to 12 previous errors
+error: struct `AsyncLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method
+ --> $DIR/len_without_is_empty.rs:292:5
+ |
+LL | pub async fn len(&self) -> usize {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: struct `AsyncOptionLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method
+ --> $DIR/len_without_is_empty.rs:304:5
+ |
+LL | pub async fn len(&self) -> Option<usize> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: struct `AsyncResultLenWithoutIsEmpty` has a public `len` method, but no `is_empty` method
+ --> $DIR/len_without_is_empty.rs:325:5
+ |
+LL | pub async fn len(&self) -> Result<usize, ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 15 previous errors
diff --git a/src/tools/clippy/tests/ui/let_unit.fixed b/src/tools/clippy/tests/ui/let_unit.fixed
index 6343cff0f..76ff0645f 100644
--- a/src/tools/clippy/tests/ui/let_unit.fixed
+++ b/src/tools/clippy/tests/ui/let_unit.fixed
@@ -175,3 +175,7 @@ fn attributes() {
#[expect(clippy::let_unit_value)]
let _ = f();
}
+
+async fn issue10433() {
+ let _pending: () = std::future::pending().await;
+}
diff --git a/src/tools/clippy/tests/ui/let_unit.rs b/src/tools/clippy/tests/ui/let_unit.rs
index c9bb2849f..895ccfe36 100644
--- a/src/tools/clippy/tests/ui/let_unit.rs
+++ b/src/tools/clippy/tests/ui/let_unit.rs
@@ -175,3 +175,7 @@ fn attributes() {
#[expect(clippy::let_unit_value)]
let _ = f();
}
+
+async fn issue10433() {
+ let _pending: () = std::future::pending().await;
+}
diff --git a/src/tools/clippy/tests/ui/let_with_type_underscore.rs b/src/tools/clippy/tests/ui/let_with_type_underscore.rs
new file mode 100644
index 000000000..175718b94
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_with_type_underscore.rs
@@ -0,0 +1,19 @@
+#![allow(unused)]
+#![warn(clippy::let_with_type_underscore)]
+#![allow(clippy::let_unit_value)]
+
+fn func() -> &'static str {
+ ""
+}
+
+fn main() {
+ // Will lint
+ let x: _ = 1;
+ let _: _ = 2;
+ let x: _ = func();
+
+ let x = 1; // Will not lint, Rust inferres this to an integer before Clippy
+ let x = func();
+ let x: Vec<_> = Vec::<u32>::new();
+ let x: [_; 1] = [1];
+}
diff --git a/src/tools/clippy/tests/ui/let_with_type_underscore.stderr b/src/tools/clippy/tests/ui/let_with_type_underscore.stderr
new file mode 100644
index 000000000..16bf83c70
--- /dev/null
+++ b/src/tools/clippy/tests/ui/let_with_type_underscore.stderr
@@ -0,0 +1,39 @@
+error: variable declared with type underscore
+ --> $DIR/let_with_type_underscore.rs:11:5
+ |
+LL | let x: _ = 1;
+ | ^^^^^^^^^^^^^
+ |
+help: remove the explicit type `_` declaration
+ --> $DIR/let_with_type_underscore.rs:11:10
+ |
+LL | let x: _ = 1;
+ | ^^^
+ = note: `-D clippy::let-with-type-underscore` implied by `-D warnings`
+
+error: variable declared with type underscore
+ --> $DIR/let_with_type_underscore.rs:12:5
+ |
+LL | let _: _ = 2;
+ | ^^^^^^^^^^^^^
+ |
+help: remove the explicit type `_` declaration
+ --> $DIR/let_with_type_underscore.rs:12:10
+ |
+LL | let _: _ = 2;
+ | ^^^
+
+error: variable declared with type underscore
+ --> $DIR/let_with_type_underscore.rs:13:5
+ |
+LL | let x: _ = func();
+ | ^^^^^^^^^^^^^^^^^^
+ |
+help: remove the explicit type `_` declaration
+ --> $DIR/let_with_type_underscore.rs:13:10
+ |
+LL | let x: _ = func();
+ | ^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/lines_filter_map_ok.fixed b/src/tools/clippy/tests/ui/lines_filter_map_ok.fixed
new file mode 100644
index 000000000..f4033cd8e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/lines_filter_map_ok.fixed
@@ -0,0 +1,29 @@
+// run-rustfix
+
+#![allow(unused, clippy::map_identity)]
+#![warn(clippy::lines_filter_map_ok)]
+
+use std::io::{self, BufRead, BufReader};
+
+fn main() -> io::Result<()> {
+ let f = std::fs::File::open("/")?;
+ // Lint
+ BufReader::new(f).lines().map_while(Result::ok).for_each(|_| ());
+ // Lint
+ let f = std::fs::File::open("/")?;
+ BufReader::new(f).lines().map_while(Result::ok).for_each(|_| ());
+ let s = "foo\nbar\nbaz\n";
+ // Lint
+ io::stdin().lines().map_while(Result::ok).for_each(|_| ());
+ // Lint
+ io::stdin().lines().map_while(Result::ok).for_each(|_| ());
+ // Do not lint (not a `Lines` iterator)
+ io::stdin()
+ .lines()
+ .map(std::convert::identity)
+ .filter_map(|x| x.ok())
+ .for_each(|_| ());
+ // Do not lint (not a `Result::ok()` extractor)
+ io::stdin().lines().filter_map(|x| x.err()).for_each(|_| ());
+ Ok(())
+}
diff --git a/src/tools/clippy/tests/ui/lines_filter_map_ok.rs b/src/tools/clippy/tests/ui/lines_filter_map_ok.rs
new file mode 100644
index 000000000..7e11816b2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/lines_filter_map_ok.rs
@@ -0,0 +1,29 @@
+// run-rustfix
+
+#![allow(unused, clippy::map_identity)]
+#![warn(clippy::lines_filter_map_ok)]
+
+use std::io::{self, BufRead, BufReader};
+
+fn main() -> io::Result<()> {
+ let f = std::fs::File::open("/")?;
+ // Lint
+ BufReader::new(f).lines().filter_map(Result::ok).for_each(|_| ());
+ // Lint
+ let f = std::fs::File::open("/")?;
+ BufReader::new(f).lines().flat_map(Result::ok).for_each(|_| ());
+ let s = "foo\nbar\nbaz\n";
+ // Lint
+ io::stdin().lines().filter_map(Result::ok).for_each(|_| ());
+ // Lint
+ io::stdin().lines().filter_map(|x| x.ok()).for_each(|_| ());
+ // Do not lint (not a `Lines` iterator)
+ io::stdin()
+ .lines()
+ .map(std::convert::identity)
+ .filter_map(|x| x.ok())
+ .for_each(|_| ());
+ // Do not lint (not a `Result::ok()` extractor)
+ io::stdin().lines().filter_map(|x| x.err()).for_each(|_| ());
+ Ok(())
+}
diff --git a/src/tools/clippy/tests/ui/lines_filter_map_ok.stderr b/src/tools/clippy/tests/ui/lines_filter_map_ok.stderr
new file mode 100644
index 000000000..cddd403d5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/lines_filter_map_ok.stderr
@@ -0,0 +1,51 @@
+error: `filter_map()` will run forever if the iterator repeatedly produces an `Err`
+ --> $DIR/lines_filter_map_ok.rs:11:31
+ |
+LL | BufReader::new(f).lines().filter_map(Result::ok).for_each(|_| ());
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `map_while(Result::ok)`
+ |
+note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error
+ --> $DIR/lines_filter_map_ok.rs:11:5
+ |
+LL | BufReader::new(f).lines().filter_map(Result::ok).for_each(|_| ());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: `-D clippy::lines-filter-map-ok` implied by `-D warnings`
+
+error: `flat_map()` will run forever if the iterator repeatedly produces an `Err`
+ --> $DIR/lines_filter_map_ok.rs:14:31
+ |
+LL | BufReader::new(f).lines().flat_map(Result::ok).for_each(|_| ());
+ | ^^^^^^^^^^^^^^^^^^^^ help: replace with: `map_while(Result::ok)`
+ |
+note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error
+ --> $DIR/lines_filter_map_ok.rs:14:5
+ |
+LL | BufReader::new(f).lines().flat_map(Result::ok).for_each(|_| ());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `filter_map()` will run forever if the iterator repeatedly produces an `Err`
+ --> $DIR/lines_filter_map_ok.rs:17:25
+ |
+LL | io::stdin().lines().filter_map(Result::ok).for_each(|_| ());
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `map_while(Result::ok)`
+ |
+note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error
+ --> $DIR/lines_filter_map_ok.rs:17:5
+ |
+LL | io::stdin().lines().filter_map(Result::ok).for_each(|_| ());
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: `filter_map()` will run forever if the iterator repeatedly produces an `Err`
+ --> $DIR/lines_filter_map_ok.rs:19:25
+ |
+LL | io::stdin().lines().filter_map(|x| x.ok()).for_each(|_| ());
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `map_while(Result::ok)`
+ |
+note: this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error
+ --> $DIR/lines_filter_map_ok.rs:19:5
+ |
+LL | io::stdin().lines().filter_map(|x| x.ok()).for_each(|_| ());
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.fixed b/src/tools/clippy/tests/ui/macro_use_imports.fixed
index e612480d2..a395e4f56 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports.fixed
+++ b/src/tools/clippy/tests/ui/macro_use_imports.fixed
@@ -16,11 +16,11 @@ extern crate macro_use_helper as mac;
extern crate proc_macro_derive as mini_mac;
mod a {
- use mac::{pub_macro, function_macro, ty_macro, inner_mod_macro, pub_in_private_macro};
+ use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro};
use mac;
use mini_mac::ClippyMiniMacroTest;
use mini_mac;
- use mac::{inner::foofoo, inner::try_err};
+ use mac::{inner::mut_mut, inner::try_err};
use mac::inner;
use mac::inner::nested::string_add;
use mac::inner::nested;
@@ -36,7 +36,7 @@ mod a {
let v: ty_macro!() = Vec::default();
inner::try_err!();
- inner::foofoo!();
+ inner::mut_mut!();
nested::string_add!();
}
}
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.rs b/src/tools/clippy/tests/ui/macro_use_imports.rs
index b34817cc3..b1a287332 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports.rs
+++ b/src/tools/clippy/tests/ui/macro_use_imports.rs
@@ -36,7 +36,7 @@ mod a {
let v: ty_macro!() = Vec::default();
inner::try_err!();
- inner::foofoo!();
+ inner::mut_mut!();
nested::string_add!();
}
}
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.stderr b/src/tools/clippy/tests/ui/macro_use_imports.stderr
index 61843124c..6fd338cef 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports.stderr
+++ b/src/tools/clippy/tests/ui/macro_use_imports.stderr
@@ -16,13 +16,13 @@ error: `macro_use` attributes are no longer needed in the Rust 2018 edition
--> $DIR/macro_use_imports.rs:23:5
|
LL | #[macro_use]
- | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};`
+ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::mut_mut, inner::try_err};`
error: `macro_use` attributes are no longer needed in the Rust 2018 edition
--> $DIR/macro_use_imports.rs:19:5
|
LL | #[macro_use]
- | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, function_macro, ty_macro, inner_mod_macro, pub_in_private_macro};`
+ | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro};`
error: aborting due to 4 previous errors
diff --git a/src/tools/clippy/tests/ui/macro_use_imports_expect.rs b/src/tools/clippy/tests/ui/macro_use_imports_expect.rs
index 8a1b05da9..5aac5af26 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports_expect.rs
+++ b/src/tools/clippy/tests/ui/macro_use_imports_expect.rs
@@ -39,7 +39,7 @@ mod a {
let v: ty_macro!() = Vec::default();
inner::try_err!();
- inner::foofoo!();
+ inner::mut_mut!();
nested::string_add!();
}
}
diff --git a/src/tools/clippy/tests/ui/manual_async_fn.fixed b/src/tools/clippy/tests/ui/manual_async_fn.fixed
index b7e46a4a8..5cc4a43af 100644
--- a/src/tools/clippy/tests/ui/manual_async_fn.fixed
+++ b/src/tools/clippy/tests/ui/manual_async_fn.fixed
@@ -107,4 +107,10 @@ mod issue_5765 {
}
}
+pub async fn issue_10450() -> i32 { 42 }
+
+pub(crate) async fn issue_10450_2() -> i32 { 42 }
+
+pub(self) async fn issue_10450_3() -> i32 { 42 }
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_async_fn.rs b/src/tools/clippy/tests/ui/manual_async_fn.rs
index b05429da6..ba504b8a8 100644
--- a/src/tools/clippy/tests/ui/manual_async_fn.rs
+++ b/src/tools/clippy/tests/ui/manual_async_fn.rs
@@ -127,4 +127,16 @@ mod issue_5765 {
}
}
+pub fn issue_10450() -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+pub(crate) fn issue_10450_2() -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+pub(self) fn issue_10450_3() -> impl Future<Output = i32> {
+ async { 42 }
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_async_fn.stderr b/src/tools/clippy/tests/ui/manual_async_fn.stderr
index 0a903ed6f..f5ee3eb7c 100644
--- a/src/tools/clippy/tests/ui/manual_async_fn.stderr
+++ b/src/tools/clippy/tests/ui/manual_async_fn.stderr
@@ -161,5 +161,50 @@ help: move the body of the async block to the enclosing function
LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b { 42 }
| ~~~~~~
-error: aborting due to 10 previous errors
+error: this function can be simplified using the `async fn` syntax
+ --> $DIR/manual_async_fn.rs:130:1
+ |
+LL | pub fn issue_10450() -> impl Future<Output = i32> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | pub async fn issue_10450() -> i32 {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | pub fn issue_10450() -> impl Future<Output = i32> { 42 }
+ | ~~~~~~
+
+error: this function can be simplified using the `async fn` syntax
+ --> $DIR/manual_async_fn.rs:134:1
+ |
+LL | pub(crate) fn issue_10450_2() -> impl Future<Output = i32> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | pub(crate) async fn issue_10450_2() -> i32 {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | pub(crate) fn issue_10450_2() -> impl Future<Output = i32> { 42 }
+ | ~~~~~~
+
+error: this function can be simplified using the `async fn` syntax
+ --> $DIR/manual_async_fn.rs:138:1
+ |
+LL | pub(self) fn issue_10450_3() -> impl Future<Output = i32> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | pub(self) async fn issue_10450_3() -> i32 {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: move the body of the async block to the enclosing function
+ |
+LL | pub(self) fn issue_10450_3() -> impl Future<Output = i32> { 42 }
+ | ~~~~~~
+
+error: aborting due to 13 previous errors
diff --git a/src/tools/clippy/tests/ui/manual_clamp.rs b/src/tools/clippy/tests/ui/manual_clamp.rs
index f7902e6fd..cdfd8e4c3 100644
--- a/src/tools/clippy/tests/ui/manual_clamp.rs
+++ b/src/tools/clippy/tests/ui/manual_clamp.rs
@@ -326,3 +326,22 @@ fn msrv_1_50() {
input
};
}
+
+const fn _const() {
+ let (input, min, max) = (0, -1, 2);
+ let _ = if input < min {
+ min
+ } else if input > max {
+ max
+ } else {
+ input
+ };
+
+ let mut x = input;
+ if max < x {
+ let x = max;
+ }
+ if min > x {
+ x = min;
+ }
+}
diff --git a/src/tools/clippy/tests/ui/manual_main_separator_str.fixed b/src/tools/clippy/tests/ui/manual_main_separator_str.fixed
new file mode 100644
index 000000000..50f46d6b3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_main_separator_str.fixed
@@ -0,0 +1,39 @@
+// run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::manual_main_separator_str)]
+
+use std::path::MAIN_SEPARATOR;
+
+fn len(s: &str) -> usize {
+ s.len()
+}
+
+struct U<'a> {
+ f: &'a str,
+ g: &'a String,
+}
+
+struct V<T> {
+ f: T,
+}
+
+fn main() {
+ // Should lint
+ let _: &str = std::path::MAIN_SEPARATOR_STR;
+ let _ = len(std::path::MAIN_SEPARATOR_STR);
+ let _: Vec<u16> = std::path::MAIN_SEPARATOR_STR.encode_utf16().collect();
+
+ // Should lint for field `f` only
+ let _ = U {
+ f: std::path::MAIN_SEPARATOR_STR,
+ g: &MAIN_SEPARATOR.to_string(),
+ };
+
+ // Should not lint
+ let _: &String = &MAIN_SEPARATOR.to_string();
+ let _ = &MAIN_SEPARATOR.to_string();
+ let _ = V {
+ f: &MAIN_SEPARATOR.to_string(),
+ };
+}
diff --git a/src/tools/clippy/tests/ui/manual_main_separator_str.rs b/src/tools/clippy/tests/ui/manual_main_separator_str.rs
new file mode 100644
index 000000000..2dbb9e661
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_main_separator_str.rs
@@ -0,0 +1,39 @@
+// run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::manual_main_separator_str)]
+
+use std::path::MAIN_SEPARATOR;
+
+fn len(s: &str) -> usize {
+ s.len()
+}
+
+struct U<'a> {
+ f: &'a str,
+ g: &'a String,
+}
+
+struct V<T> {
+ f: T,
+}
+
+fn main() {
+ // Should lint
+ let _: &str = &MAIN_SEPARATOR.to_string();
+ let _ = len(&MAIN_SEPARATOR.to_string());
+ let _: Vec<u16> = MAIN_SEPARATOR.to_string().encode_utf16().collect();
+
+ // Should lint for field `f` only
+ let _ = U {
+ f: &MAIN_SEPARATOR.to_string(),
+ g: &MAIN_SEPARATOR.to_string(),
+ };
+
+ // Should not lint
+ let _: &String = &MAIN_SEPARATOR.to_string();
+ let _ = &MAIN_SEPARATOR.to_string();
+ let _ = V {
+ f: &MAIN_SEPARATOR.to_string(),
+ };
+}
diff --git a/src/tools/clippy/tests/ui/manual_main_separator_str.stderr b/src/tools/clippy/tests/ui/manual_main_separator_str.stderr
new file mode 100644
index 000000000..e6cefde66
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_main_separator_str.stderr
@@ -0,0 +1,28 @@
+error: taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`
+ --> $DIR/manual_main_separator_str.rs:23:19
+ |
+LL | let _: &str = &MAIN_SEPARATOR.to_string();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::path::MAIN_SEPARATOR_STR`
+ |
+ = note: `-D clippy::manual-main-separator-str` implied by `-D warnings`
+
+error: taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`
+ --> $DIR/manual_main_separator_str.rs:24:17
+ |
+LL | let _ = len(&MAIN_SEPARATOR.to_string());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::path::MAIN_SEPARATOR_STR`
+
+error: taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`
+ --> $DIR/manual_main_separator_str.rs:25:23
+ |
+LL | let _: Vec<u16> = MAIN_SEPARATOR.to_string().encode_utf16().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::path::MAIN_SEPARATOR_STR`
+
+error: taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`
+ --> $DIR/manual_main_separator_str.rs:29:12
+ |
+LL | f: &MAIN_SEPARATOR.to_string(),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::path::MAIN_SEPARATOR_STR`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.fixed b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
index 4cdc0546a..1f6df1b0a 100644
--- a/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
@@ -1,18 +1,13 @@
// run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::manual_rem_euclid)]
+#![allow(clippy::let_with_type_underscore)]
-#[macro_use]
-extern crate macro_rules;
-
-macro_rules! internal_rem_euclid {
- () => {
- let value: i32 = 5;
- let _: i32 = value.rem_euclid(4);
- };
-}
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
+#[inline_macros]
fn main() {
let value: i32 = 5;
@@ -38,10 +33,16 @@ fn main() {
let _: i32 = ((4 % value) + 4) % 4;
// Lint in internal macros
- internal_rem_euclid!();
+ inline!(
+ let value: i32 = 5;
+ let _: i32 = value.rem_euclid(4);
+ );
// Do not lint in external macros
- manual_rem_euclid!();
+ external!(
+ let value: i32 = 5;
+ let _: i32 = ((value % 4) + 4) % 4;
+ );
}
// Should lint for params too
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.rs b/src/tools/clippy/tests/ui/manual_rem_euclid.rs
index 58a9e20f3..b275e8a38 100644
--- a/src/tools/clippy/tests/ui/manual_rem_euclid.rs
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.rs
@@ -1,18 +1,13 @@
// run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::manual_rem_euclid)]
+#![allow(clippy::let_with_type_underscore)]
-#[macro_use]
-extern crate macro_rules;
-
-macro_rules! internal_rem_euclid {
- () => {
- let value: i32 = 5;
- let _: i32 = ((value % 4) + 4) % 4;
- };
-}
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
+#[inline_macros]
fn main() {
let value: i32 = 5;
@@ -38,10 +33,16 @@ fn main() {
let _: i32 = ((4 % value) + 4) % 4;
// Lint in internal macros
- internal_rem_euclid!();
+ inline!(
+ let value: i32 = 5;
+ let _: i32 = ((value % 4) + 4) % 4;
+ );
// Do not lint in external macros
- manual_rem_euclid!();
+ external!(
+ let value: i32 = 5;
+ let _: i32 = ((value % 4) + 4) % 4;
+ );
}
// Should lint for params too
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.stderr b/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
index e3122a588..a43707f89 100644
--- a/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
@@ -1,5 +1,5 @@
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:19:18
+ --> $DIR/manual_rem_euclid.rs:14:18
|
LL | let _: i32 = ((value % 4) + 4) % 4;
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
@@ -7,60 +7,57 @@ LL | let _: i32 = ((value % 4) + 4) % 4;
= note: `-D clippy::manual-rem-euclid` implied by `-D warnings`
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:20:18
+ --> $DIR/manual_rem_euclid.rs:15:18
|
LL | let _: i32 = (4 + (value % 4)) % 4;
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:21:18
+ --> $DIR/manual_rem_euclid.rs:16:18
|
LL | let _: i32 = (value % 4 + 4) % 4;
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:22:18
+ --> $DIR/manual_rem_euclid.rs:17:18
|
LL | let _: i32 = (4 + value % 4) % 4;
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:23:22
+ --> $DIR/manual_rem_euclid.rs:18:22
|
LL | let _: i32 = 1 + (4 + value % 4) % 4;
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:12:22
+ --> $DIR/manual_rem_euclid.rs:38:22
|
LL | let _: i32 = ((value % 4) + 4) % 4;
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
-...
-LL | internal_rem_euclid!();
- | ---------------------- in this macro invocation
|
- = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info)
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:49:5
+ --> $DIR/manual_rem_euclid.rs:50:5
|
LL | ((num % 4) + 4) % 4
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:54:5
+ --> $DIR/manual_rem_euclid.rs:55:5
|
LL | ((num % 4) + 4) % 4
| ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:66:18
+ --> $DIR/manual_rem_euclid.rs:67:18
|
LL | let _: i32 = ((x % 4) + 4) % 4;
| ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
error: manual `rem_euclid` implementation
- --> $DIR/manual_rem_euclid.rs:79:18
+ --> $DIR/manual_rem_euclid.rs:80:18
|
LL | let _: i32 = ((x % 4) + 4) % 4;
| ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs b/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs
new file mode 100644
index 000000000..5082f931f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs
@@ -0,0 +1,36 @@
+#![allow(unused)]
+#![warn(clippy::manual_slice_size_calculation)]
+
+use core::mem::{align_of, size_of};
+
+fn main() {
+ let v_i32 = Vec::<i32>::new();
+ let s_i32 = v_i32.as_slice();
+
+ // True positives:
+ let _ = s_i32.len() * size_of::<i32>(); // WARNING
+ let _ = size_of::<i32>() * s_i32.len(); // WARNING
+ let _ = size_of::<i32>() * s_i32.len() * 5; // WARNING
+
+ let len = s_i32.len();
+ let size = size_of::<i32>();
+ let _ = len * size_of::<i32>(); // WARNING
+ let _ = s_i32.len() * size; // WARNING
+ let _ = len * size; // WARNING
+
+ // True negatives:
+ let _ = size_of::<i32>() + s_i32.len(); // Ok, not a multiplication
+ let _ = size_of::<i32>() * s_i32.partition_point(|_| true); // Ok, not len()
+ let _ = size_of::<i32>() * v_i32.len(); // Ok, not a slice
+ let _ = align_of::<i32>() * s_i32.len(); // Ok, not size_of()
+ let _ = size_of::<u32>() * s_i32.len(); // Ok, different types
+
+ // False negatives:
+ let _ = 5 * size_of::<i32>() * s_i32.len(); // Ok (MISSED OPPORTUNITY)
+ let _ = size_of::<i32>() * 5 * s_i32.len(); // Ok (MISSED OPPORTUNITY)
+}
+
+const fn _const(s_i32: &[i32]) {
+ // True negative:
+ let _ = s_i32.len() * size_of::<i32>(); // Ok, can't use size_of_val in const
+}
diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr b/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr
new file mode 100644
index 000000000..4a24fc60a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr
@@ -0,0 +1,51 @@
+error: manual slice size calculation
+ --> $DIR/manual_slice_size_calculation.rs:11:13
+ |
+LL | let _ = s_i32.len() * size_of::<i32>(); // WARNING
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using std::mem::size_of_value instead
+ = note: `-D clippy::manual-slice-size-calculation` implied by `-D warnings`
+
+error: manual slice size calculation
+ --> $DIR/manual_slice_size_calculation.rs:12:13
+ |
+LL | let _ = size_of::<i32>() * s_i32.len(); // WARNING
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using std::mem::size_of_value instead
+
+error: manual slice size calculation
+ --> $DIR/manual_slice_size_calculation.rs:13:13
+ |
+LL | let _ = size_of::<i32>() * s_i32.len() * 5; // WARNING
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using std::mem::size_of_value instead
+
+error: manual slice size calculation
+ --> $DIR/manual_slice_size_calculation.rs:17:13
+ |
+LL | let _ = len * size_of::<i32>(); // WARNING
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using std::mem::size_of_value instead
+
+error: manual slice size calculation
+ --> $DIR/manual_slice_size_calculation.rs:18:13
+ |
+LL | let _ = s_i32.len() * size; // WARNING
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using std::mem::size_of_value instead
+
+error: manual slice size calculation
+ --> $DIR/manual_slice_size_calculation.rs:19:13
+ |
+LL | let _ = len * size; // WARNING
+ | ^^^^^^^^^^
+ |
+ = help: consider using std::mem::size_of_value instead
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_result_ok.fixed b/src/tools/clippy/tests/ui/match_result_ok.fixed
index 8b91b9854..10ae1ee52 100644
--- a/src/tools/clippy/tests/ui/match_result_ok.fixed
+++ b/src/tools/clippy/tests/ui/match_result_ok.fixed
@@ -16,7 +16,7 @@ fn str_to_int_ok(x: &str) -> i32 {
#[rustfmt::skip]
fn strange_some_no_else(x: &str) -> i32 {
{
- if let Ok(y) = x . parse() {
+ if let Ok(y) = x . parse() {
return y;
};
0
diff --git a/src/tools/clippy/tests/ui/match_result_ok.stderr b/src/tools/clippy/tests/ui/match_result_ok.stderr
index 98a95705c..cbdc56aa2 100644
--- a/src/tools/clippy/tests/ui/match_result_ok.stderr
+++ b/src/tools/clippy/tests/ui/match_result_ok.stderr
@@ -18,7 +18,7 @@ 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() {
+LL | if let Ok(y) = x . parse() {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: matching on `Some` with `ok()` is redundant
diff --git a/src/tools/clippy/tests/ui/match_single_binding.fixed b/src/tools/clippy/tests/ui/match_single_binding.fixed
index 6cfb6661a..201301cc9 100644
--- a/src/tools/clippy/tests/ui/match_single_binding.fixed
+++ b/src/tools/clippy/tests/ui/match_single_binding.fixed
@@ -1,7 +1,12 @@
// run-rustfix
#![warn(clippy::match_single_binding)]
-#![allow(unused_variables)]
-#![allow(clippy::toplevel_ref_arg, clippy::uninlined_format_args)]
+#![allow(
+ unused,
+ clippy::let_unit_value,
+ clippy::no_effect,
+ clippy::toplevel_ref_arg,
+ clippy::uninlined_format_args
+)]
struct Point {
x: i32,
@@ -109,10 +114,9 @@ fn main() {
// Lint
let x = 1;
- println!("Not an array index start");
+ println!("Not an array index start")
}
-#[allow(dead_code)]
fn issue_8723() {
let (mut val, idx) = ("a b", 1);
@@ -125,16 +129,15 @@ fn issue_8723() {
let _ = val;
}
-#[allow(dead_code)]
+fn side_effects() {}
+
fn issue_9575() {
- fn side_effects() {}
let _ = || {
side_effects();
- println!("Needs curlies");
+ println!("Needs curlies")
};
}
-#[allow(dead_code)]
fn issue_9725(r: Option<u32>) {
let x = r;
match x {
@@ -146,3 +149,25 @@ fn issue_9725(r: Option<u32>) {
},
};
}
+
+fn issue_10447() -> usize {
+ ();
+
+ let a = ();
+
+ side_effects();
+
+ let b = side_effects();
+
+ println!("1");
+
+ let c = println!("1");
+
+ let in_expr = [
+ (),
+ side_effects(),
+ println!("1"),
+ ];
+
+ 2
+}
diff --git a/src/tools/clippy/tests/ui/match_single_binding.rs b/src/tools/clippy/tests/ui/match_single_binding.rs
index f188aeb5f..8b047b19c 100644
--- a/src/tools/clippy/tests/ui/match_single_binding.rs
+++ b/src/tools/clippy/tests/ui/match_single_binding.rs
@@ -1,7 +1,12 @@
// run-rustfix
#![warn(clippy::match_single_binding)]
-#![allow(unused_variables)]
-#![allow(clippy::toplevel_ref_arg, clippy::uninlined_format_args)]
+#![allow(
+ unused,
+ clippy::let_unit_value,
+ clippy::no_effect,
+ clippy::toplevel_ref_arg,
+ clippy::uninlined_format_args
+)]
struct Point {
x: i32,
@@ -127,7 +132,6 @@ fn main() {
}
}
-#[allow(dead_code)]
fn issue_8723() {
let (mut val, idx) = ("a b", 1);
@@ -141,15 +145,14 @@ fn issue_8723() {
let _ = val;
}
-#[allow(dead_code)]
+fn side_effects() {}
+
fn issue_9575() {
- fn side_effects() {}
let _ = || match side_effects() {
_ => println!("Needs curlies"),
};
}
-#[allow(dead_code)]
fn issue_9725(r: Option<u32>) {
match r {
x => match x {
@@ -162,3 +165,43 @@ fn issue_9725(r: Option<u32>) {
},
};
}
+
+fn issue_10447() -> usize {
+ match 1 {
+ _ => (),
+ }
+
+ let a = match 1 {
+ _ => (),
+ };
+
+ match 1 {
+ _ => side_effects(),
+ }
+
+ let b = match 1 {
+ _ => side_effects(),
+ };
+
+ match 1 {
+ _ => println!("1"),
+ }
+
+ let c = match 1 {
+ _ => println!("1"),
+ };
+
+ let in_expr = [
+ match 1 {
+ _ => (),
+ },
+ match 1 {
+ _ => side_effects(),
+ },
+ match 1 {
+ _ => println!("1"),
+ },
+ ];
+
+ 2
+}
diff --git a/src/tools/clippy/tests/ui/match_single_binding.stderr b/src/tools/clippy/tests/ui/match_single_binding.stderr
index e960d64ad..9d16af76c 100644
--- a/src/tools/clippy/tests/ui/match_single_binding.stderr
+++ b/src/tools/clippy/tests/ui/match_single_binding.stderr
@@ -1,5 +1,5 @@
error: this match could be written as a `let` statement
- --> $DIR/match_single_binding.rs:28:5
+ --> $DIR/match_single_binding.rs:33:5
|
LL | / match (a, b, c) {
LL | | (x, y, z) => {
@@ -18,7 +18,7 @@ LL + }
|
error: this match could be written as a `let` statement
- --> $DIR/match_single_binding.rs:34:5
+ --> $DIR/match_single_binding.rs:39:5
|
LL | / match (a, b, c) {
LL | | (x, y, z) => println!("{} {} {}", x, y, z),
@@ -32,7 +32,7 @@ LL + println!("{} {} {}", x, y, z);
|
error: this match could be replaced by its body itself
- --> $DIR/match_single_binding.rs:51:5
+ --> $DIR/match_single_binding.rs:56:5
|
LL | / match a {
LL | | _ => println!("whatever"),
@@ -40,7 +40,7 @@ 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
+ --> $DIR/match_single_binding.rs:60:5
|
LL | / match a {
LL | | _ => {
@@ -59,7 +59,7 @@ LL + }
|
error: this match could be replaced by its body itself
- --> $DIR/match_single_binding.rs:62:5
+ --> $DIR/match_single_binding.rs:67:5
|
LL | / match a {
LL | | _ => {
@@ -81,7 +81,7 @@ LL + }
|
error: this match could be written as a `let` statement
- --> $DIR/match_single_binding.rs:72:5
+ --> $DIR/match_single_binding.rs:77:5
|
LL | / match p {
LL | | Point { x, y } => println!("Coords: ({}, {})", x, y),
@@ -95,7 +95,7 @@ LL + println!("Coords: ({}, {})", x, y);
|
error: this match could be written as a `let` statement
- --> $DIR/match_single_binding.rs:76:5
+ --> $DIR/match_single_binding.rs:81:5
|
LL | / match p {
LL | | Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1),
@@ -109,7 +109,7 @@ LL + println!("Coords: ({}, {})", x1, y1);
|
error: this match could be written as a `let` statement
- --> $DIR/match_single_binding.rs:81:5
+ --> $DIR/match_single_binding.rs:86:5
|
LL | / match x {
LL | | ref r => println!("Got a reference to {}", r),
@@ -123,7 +123,7 @@ LL + println!("Got a reference to {}", r);
|
error: this match could be written as a `let` statement
- --> $DIR/match_single_binding.rs:86:5
+ --> $DIR/match_single_binding.rs:91:5
|
LL | / match x {
LL | | ref mut mr => println!("Got a mutable reference to {}", mr),
@@ -137,7 +137,7 @@ 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
+ --> $DIR/match_single_binding.rs:95:5
|
LL | / let product = match coords() {
LL | | Point { x, y } => x * y,
@@ -151,7 +151,7 @@ LL + let product = x * y;
|
error: this match could be written as a `let` statement
- --> $DIR/match_single_binding.rs:98:18
+ --> $DIR/match_single_binding.rs:103:18
|
LL | .map(|i| match i.unwrap() {
| __________________^
@@ -168,16 +168,16 @@ LL ~ })
|
error: this match could be replaced by its body itself
- --> $DIR/match_single_binding.rs:124:5
+ --> $DIR/match_single_binding.rs:129:5
|
LL | / match x {
LL | | // =>
LL | | _ => println!("Not an array index start"),
LL | | }
- | |_____^ help: consider using the match body instead: `println!("Not an array index start");`
+ | |_____^ help: consider using the match body instead: `println!("Not an array index start")`
error: this assignment could be simplified
- --> $DIR/match_single_binding.rs:134:5
+ --> $DIR/match_single_binding.rs:138:5
|
LL | / val = match val.split_at(idx) {
LL | | (pre, suf) => {
@@ -197,7 +197,7 @@ LL ~ };
|
error: this match could be replaced by its scrutinee and body
- --> $DIR/match_single_binding.rs:147:16
+ --> $DIR/match_single_binding.rs:151:16
|
LL | let _ = || match side_effects() {
| ________________^
@@ -209,12 +209,12 @@ help: consider using the scrutinee and body instead
|
LL ~ let _ = || {
LL + side_effects();
-LL + println!("Needs curlies");
+LL + println!("Needs curlies")
LL ~ };
|
error: this match could be written as a `let` statement
- --> $DIR/match_single_binding.rs:154:5
+ --> $DIR/match_single_binding.rs:157:5
|
LL | / match r {
LL | | x => match x {
@@ -238,5 +238,80 @@ LL + },
LL ~ };
|
-error: aborting due to 15 previous errors
+error: this match could be replaced by its body itself
+ --> $DIR/match_single_binding.rs:170:5
+ |
+LL | / match 1 {
+LL | | _ => (),
+LL | | }
+ | |_____^ help: consider using the match body instead: `();`
+
+error: this match could be replaced by its body itself
+ --> $DIR/match_single_binding.rs:174:13
+ |
+LL | let a = match 1 {
+ | _____________^
+LL | | _ => (),
+LL | | };
+ | |_____^ help: consider using the match body instead: `()`
+
+error: this match could be replaced by its body itself
+ --> $DIR/match_single_binding.rs:178:5
+ |
+LL | / match 1 {
+LL | | _ => side_effects(),
+LL | | }
+ | |_____^ help: consider using the match body instead: `side_effects();`
+
+error: this match could be replaced by its body itself
+ --> $DIR/match_single_binding.rs:182:13
+ |
+LL | let b = match 1 {
+ | _____________^
+LL | | _ => side_effects(),
+LL | | };
+ | |_____^ help: consider using the match body instead: `side_effects()`
+
+error: this match could be replaced by its body itself
+ --> $DIR/match_single_binding.rs:186:5
+ |
+LL | / match 1 {
+LL | | _ => println!("1"),
+LL | | }
+ | |_____^ help: consider using the match body instead: `println!("1");`
+
+error: this match could be replaced by its body itself
+ --> $DIR/match_single_binding.rs:190:13
+ |
+LL | let c = match 1 {
+ | _____________^
+LL | | _ => println!("1"),
+LL | | };
+ | |_____^ help: consider using the match body instead: `println!("1")`
+
+error: this match could be replaced by its body itself
+ --> $DIR/match_single_binding.rs:195:9
+ |
+LL | / match 1 {
+LL | | _ => (),
+LL | | },
+ | |_________^ help: consider using the match body instead: `()`
+
+error: this match could be replaced by its body itself
+ --> $DIR/match_single_binding.rs:198:9
+ |
+LL | / match 1 {
+LL | | _ => side_effects(),
+LL | | },
+ | |_________^ help: consider using the match body instead: `side_effects()`
+
+error: this match could be replaced by its body itself
+ --> $DIR/match_single_binding.rs:201:9
+ |
+LL | / match 1 {
+LL | | _ => println!("1"),
+LL | | },
+ | |_________^ help: consider using the match body instead: `println!("1")`
+
+error: aborting due to 24 previous errors
diff --git a/src/tools/clippy/tests/ui/match_single_binding2.fixed b/src/tools/clippy/tests/ui/match_single_binding2.fixed
index 6a7db67e3..e3cf56a42 100644
--- a/src/tools/clippy/tests/ui/match_single_binding2.fixed
+++ b/src/tools/clippy/tests/ui/match_single_binding2.fixed
@@ -30,7 +30,7 @@ fn main() {
#[rustfmt::skip]
Some((first, _second)) => {
let (a, b) = get_tup();
- println!("a {:?} and b {:?}", a, b);
+ println!("a {:?} and b {:?}", a, b)
},
None => println!("nothing"),
}
@@ -49,5 +49,5 @@ fn main() {
0 => 1,
_ => 2,
};
- println!("Single branch");
+ println!("Single branch")
}
diff --git a/src/tools/clippy/tests/ui/match_single_binding2.stderr b/src/tools/clippy/tests/ui/match_single_binding2.stderr
index 22bf7d8be..e180b93e7 100644
--- a/src/tools/clippy/tests/ui/match_single_binding2.stderr
+++ b/src/tools/clippy/tests/ui/match_single_binding2.stderr
@@ -27,7 +27,7 @@ LL | | }
help: consider using a `let` statement
|
LL ~ let (a, b) = get_tup();
-LL + println!("a {:?} and b {:?}", a, b);
+LL + println!("a {:?} and b {:?}", a, b)
|
error: this match could be replaced by its scrutinee and body
@@ -61,7 +61,7 @@ LL ~ match x {
LL + 0 => 1,
LL + _ => 2,
LL + };
-LL + println!("Single branch");
+LL + println!("Single branch")
|
error: aborting due to 4 previous errors
diff --git a/src/tools/clippy/tests/ui/mem_replace.fixed b/src/tools/clippy/tests/ui/mem_replace.fixed
index 874d55843..7fd340173 100644
--- a/src/tools/clippy/tests/ui/mem_replace.fixed
+++ b/src/tools/clippy/tests/ui/mem_replace.fixed
@@ -90,3 +90,37 @@ fn msrv_1_40() {
let mut s = String::from("foo");
let _ = std::mem::take(&mut s);
}
+
+fn issue9824() {
+ struct Foo<'a>(Option<&'a str>);
+ impl<'a> std::ops::Deref for Foo<'a> {
+ type Target = Option<&'a str>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+ impl<'a> std::ops::DerefMut for Foo<'a> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+ }
+
+ struct Bar {
+ opt: Option<u8>,
+ val: String,
+ }
+
+ let mut f = Foo(Some("foo"));
+ let mut b = Bar {
+ opt: Some(1),
+ val: String::from("bar"),
+ };
+
+ // replace option with none
+ let _ = f.0.take();
+ let _ = (*f).take();
+ let _ = b.opt.take();
+ // replace with default
+ let _ = std::mem::take(&mut b.val);
+}
diff --git a/src/tools/clippy/tests/ui/mem_replace.rs b/src/tools/clippy/tests/ui/mem_replace.rs
index f4f3bff51..fa2903add 100644
--- a/src/tools/clippy/tests/ui/mem_replace.rs
+++ b/src/tools/clippy/tests/ui/mem_replace.rs
@@ -90,3 +90,37 @@ fn msrv_1_40() {
let mut s = String::from("foo");
let _ = std::mem::replace(&mut s, String::default());
}
+
+fn issue9824() {
+ struct Foo<'a>(Option<&'a str>);
+ impl<'a> std::ops::Deref for Foo<'a> {
+ type Target = Option<&'a str>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+ impl<'a> std::ops::DerefMut for Foo<'a> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+ }
+
+ struct Bar {
+ opt: Option<u8>,
+ val: String,
+ }
+
+ let mut f = Foo(Some("foo"));
+ let mut b = Bar {
+ opt: Some(1),
+ val: String::from("bar"),
+ };
+
+ // replace option with none
+ let _ = std::mem::replace(&mut f.0, None);
+ let _ = std::mem::replace(&mut *f, None);
+ let _ = std::mem::replace(&mut b.opt, None);
+ // replace with default
+ let _ = std::mem::replace(&mut b.val, String::default());
+}
diff --git a/src/tools/clippy/tests/ui/mem_replace.stderr b/src/tools/clippy/tests/ui/mem_replace.stderr
index caa127f76..58b57be75 100644
--- a/src/tools/clippy/tests/ui/mem_replace.stderr
+++ b/src/tools/clippy/tests/ui/mem_replace.stderr
@@ -122,5 +122,29 @@ error: replacing a value of type `T` with `T::default()` is better expressed usi
LL | let _ = std::mem::replace(&mut s, String::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
-error: aborting due to 20 previous errors
+error: replacing an `Option` with `None`
+ --> $DIR/mem_replace.rs:121:13
+ |
+LL | let _ = std::mem::replace(&mut f.0, None);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `f.0.take()`
+
+error: replacing an `Option` with `None`
+ --> $DIR/mem_replace.rs:122:13
+ |
+LL | let _ = std::mem::replace(&mut *f, None);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `(*f).take()`
+
+error: replacing an `Option` with `None`
+ --> $DIR/mem_replace.rs:123:13
+ |
+LL | let _ = std::mem::replace(&mut b.opt, None);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `b.opt.take()`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+ --> $DIR/mem_replace.rs:125:13
+ |
+LL | let _ = std::mem::replace(&mut b.val, String::default());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut b.val)`
+
+error: aborting due to 24 previous errors
diff --git a/src/tools/clippy/tests/ui/mem_replace_macro.rs b/src/tools/clippy/tests/ui/mem_replace_macro.rs
index 0c09344b8..3932e7d00 100644
--- a/src/tools/clippy/tests/ui/mem_replace_macro.rs
+++ b/src/tools/clippy/tests/ui/mem_replace_macro.rs
@@ -1,21 +1,12 @@
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.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);
-}
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
+#[inline_macros]
fn main() {
- replace_with_default();
+ let s = &mut String::from("foo");
+ inline!(std::mem::replace($s, Default::default()));
+ external!(std::mem::replace($s, Default::default()));
}
diff --git a/src/tools/clippy/tests/ui/mem_replace_macro.stderr b/src/tools/clippy/tests/ui/mem_replace_macro.stderr
index dd69ab8b5..35dda93da 100644
--- a/src/tools/clippy/tests/ui/mem_replace_macro.stderr
+++ b/src/tools/clippy/tests/ui/mem_replace_macro.stderr
@@ -1,14 +1,11 @@
error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
- --> $DIR/mem_replace_macro.rs:9:9
+ --> $DIR/mem_replace_macro.rs:10:13
|
-LL | std::mem::replace($s, Default::default())
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-...
-LL | take!(s);
- | -------- in this macro invocation
+LL | inline!(std::mem::replace($s, Default::default()));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::mem-replace-with-default` implied by `-D warnings`
- = note: this error originates in the macro `take` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
diff --git a/src/tools/clippy/tests/ui/missing_assert_message.rs b/src/tools/clippy/tests/ui/missing_assert_message.rs
new file mode 100644
index 000000000..89404ca88
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_assert_message.rs
@@ -0,0 +1,84 @@
+#![allow(unused)]
+#![warn(clippy::missing_assert_message)]
+
+macro_rules! bar {
+ ($( $x:expr ),*) => {
+ foo()
+ };
+}
+
+fn main() {}
+
+// Should trigger warning
+fn asserts_without_message() {
+ assert!(foo());
+ assert_eq!(foo(), foo());
+ assert_ne!(foo(), foo());
+ debug_assert!(foo());
+ debug_assert_eq!(foo(), foo());
+ debug_assert_ne!(foo(), foo());
+}
+
+// Should trigger warning
+fn asserts_without_message_but_with_macro_calls() {
+ assert!(bar!(true));
+ assert!(bar!(true, false));
+ assert_eq!(bar!(true), foo());
+ assert_ne!(bar!(true, true), bar!(true));
+}
+
+// Should trigger warning
+fn asserts_with_trailing_commas() {
+ assert!(foo(),);
+ assert_eq!(foo(), foo(),);
+ assert_ne!(foo(), foo(),);
+ debug_assert!(foo(),);
+ debug_assert_eq!(foo(), foo(),);
+ debug_assert_ne!(foo(), foo(),);
+}
+
+// Should not trigger warning
+fn asserts_with_message_and_with_macro_calls() {
+ assert!(bar!(true), "msg");
+ assert!(bar!(true, false), "msg");
+ assert_eq!(bar!(true), foo(), "msg");
+ assert_ne!(bar!(true, true), bar!(true), "msg");
+}
+
+// Should not trigger warning
+fn asserts_with_message() {
+ assert!(foo(), "msg");
+ assert_eq!(foo(), foo(), "msg");
+ assert_ne!(foo(), foo(), "msg");
+ debug_assert!(foo(), "msg");
+ debug_assert_eq!(foo(), foo(), "msg");
+ debug_assert_ne!(foo(), foo(), "msg");
+}
+
+// Should not trigger warning
+#[test]
+fn asserts_without_message_but_inside_a_test_function() {
+ assert!(foo());
+ assert_eq!(foo(), foo());
+ assert_ne!(foo(), foo());
+ debug_assert!(foo());
+ debug_assert_eq!(foo(), foo());
+ debug_assert_ne!(foo(), foo());
+}
+
+// Should not trigger warning
+#[cfg(test)]
+mod tests {
+ fn asserts_without_message_but_inside_a_test_module() {
+ assert!(foo());
+ assert_eq!(foo(), foo());
+ assert_ne!(foo(), foo());
+ debug_assert!(foo());
+ debug_assert_eq!(foo(), foo());
+ debug_assert_ne!(foo(), foo());
+ }
+}
+
+fn foo() -> bool {
+ true
+}
diff --git a/src/tools/clippy/tests/ui/missing_assert_message.stderr b/src/tools/clippy/tests/ui/missing_assert_message.stderr
new file mode 100644
index 000000000..ecd038012
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_assert_message.stderr
@@ -0,0 +1,131 @@
+error: assert without any message
+ --> $DIR/missing_assert_message.rs:14:5
+ |
+LL | assert!(foo());
+ | ^^^^^^^^^^^^^^
+ |
+ = help: consider describing why the failing assert is problematic
+ = note: `-D clippy::missing-assert-message` implied by `-D warnings`
+
+error: assert without any message
+ --> $DIR/missing_assert_message.rs:15:5
+ |
+LL | assert_eq!(foo(), foo());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+ --> $DIR/missing_assert_message.rs:16:5
+ |
+LL | assert_ne!(foo(), foo());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+ --> $DIR/missing_assert_message.rs:17:5
+ |
+LL | debug_assert!(foo());
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+ --> $DIR/missing_assert_message.rs:18:5
+ |
+LL | debug_assert_eq!(foo(), foo());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+ --> $DIR/missing_assert_message.rs:19:5
+ |
+LL | debug_assert_ne!(foo(), foo());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+ --> $DIR/missing_assert_message.rs:24:5
+ |
+LL | assert!(bar!(true));
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+ --> $DIR/missing_assert_message.rs:25:5
+ |
+LL | assert!(bar!(true, false));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+ --> $DIR/missing_assert_message.rs:26:5
+ |
+LL | assert_eq!(bar!(true), foo());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+ --> $DIR/missing_assert_message.rs:27:5
+ |
+LL | assert_ne!(bar!(true, true), bar!(true));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+ --> $DIR/missing_assert_message.rs:32:5
+ |
+LL | assert!(foo(),);
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+ --> $DIR/missing_assert_message.rs:33:5
+ |
+LL | assert_eq!(foo(), foo(),);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+ --> $DIR/missing_assert_message.rs:34:5
+ |
+LL | assert_ne!(foo(), foo(),);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+ --> $DIR/missing_assert_message.rs:35:5
+ |
+LL | debug_assert!(foo(),);
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+ --> $DIR/missing_assert_message.rs:36:5
+ |
+LL | debug_assert_eq!(foo(), foo(),);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider describing why the failing assert is problematic
+
+error: assert without any message
+ --> $DIR/missing_assert_message.rs:37:5
+ |
+LL | debug_assert_ne!(foo(), foo(),);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider describing why the failing assert is problematic
+
+error: aborting due to 16 previous errors
+
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
index 75cace181..e6f88c6e6 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
@@ -3,15 +3,15 @@
//! The .stderr output of this test should be empty. Otherwise it's a bug somewhere.
// aux-build:helper.rs
-// aux-build:../../auxiliary/proc_macro_with_span.rs
+// aux-build:../../auxiliary/proc_macros.rs
#![warn(clippy::missing_const_for_fn)]
#![feature(start)]
extern crate helper;
-extern crate proc_macro_with_span;
+extern crate proc_macros;
-use proc_macro_with_span::with_span;
+use proc_macros::with_span;
struct Game;
diff --git a/src/tools/clippy/tests/ui/missing_doc.rs b/src/tools/clippy/tests/ui/missing_doc.rs
index 590ad63c9..575204894 100644
--- a/src/tools/clippy/tests/ui/missing_doc.rs
+++ b/src/tools/clippy/tests/ui/missing_doc.rs
@@ -1,5 +1,5 @@
// needs-asm-support
-// aux-build: proc_macro_with_span.rs
+// aux-build: proc_macros.rs
#![warn(clippy::missing_docs_in_private_items)]
// When denying at the crate level, be sure to not get random warnings from the
@@ -8,9 +8,9 @@
//! Some garbage docs for the crate here
#![doc = "More garbage"]
-extern crate proc_macro_with_span;
+extern crate proc_macros;
-use proc_macro_with_span::with_span;
+use proc_macros::with_span;
use std::arch::global_asm;
type Typedef = String;
diff --git a/src/tools/clippy/tests/ui/missing_doc.stderr b/src/tools/clippy/tests/ui/missing_doc.stderr
index d3bef28bf..4e8a49bf1 100644
--- a/src/tools/clippy/tests/ui/missing_doc.stderr
+++ b/src/tools/clippy/tests/ui/missing_doc.stderr
@@ -6,30 +6,12 @@ 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:17:1
- |
-LL | pub type PubTypedef = String;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
error: missing documentation for a module
--> $DIR/missing_doc.rs:19:1
|
LL | mod module_no_dox {}
| ^^^^^^^^^^^^^^^^^^^^
-error: missing documentation for a module
- --> $DIR/missing_doc.rs:20:1
- |
-LL | pub mod pub_module_no_dox {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a function
- --> $DIR/missing_doc.rs:24:1
- |
-LL | pub fn foo2() {}
- | ^^^^^^^^^^^^^^^^
-
error: missing documentation for a function
--> $DIR/missing_doc.rs:25:1
|
@@ -69,50 +51,18 @@ error: missing documentation for a variant
LL | BarB,
| ^^^^
-error: missing documentation for an enum
- --> $DIR/missing_doc.rs:44:1
- |
-LL | / pub enum PubBaz {
-LL | | PubBazA { a: isize },
-LL | | }
- | |_^
-
-error: missing documentation for a variant
- --> $DIR/missing_doc.rs:45:5
- |
-LL | PubBazA { a: isize },
- | ^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a struct field
- --> $DIR/missing_doc.rs:45:15
- |
-LL | PubBazA { a: isize },
- | ^^^^^^^^
-
error: missing documentation for a constant
--> $DIR/missing_doc.rs:65:1
|
LL | const FOO: u32 = 0;
| ^^^^^^^^^^^^^^^^^^^
-error: missing documentation for a constant
- --> $DIR/missing_doc.rs:72:1
- |
-LL | pub const FOO4: u32 = 0;
- | ^^^^^^^^^^^^^^^^^^^^^^^^
-
error: missing documentation for a static
--> $DIR/missing_doc.rs:74:1
|
LL | static BAR: u32 = 0;
| ^^^^^^^^^^^^^^^^^^^^
-error: missing documentation for a static
- --> $DIR/missing_doc.rs:81:1
- |
-LL | pub static BAR4: u32 = 0;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
-
error: missing documentation for a module
--> $DIR/missing_doc.rs:83:1
|
@@ -126,34 +76,16 @@ LL | | }
| |_^
error: missing documentation for a function
- --> $DIR/missing_doc.rs:86:5
- |
-LL | pub fn undocumented1() {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a function
- --> $DIR/missing_doc.rs:87:5
- |
-LL | pub fn undocumented2() {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a function
--> $DIR/missing_doc.rs:88:5
|
LL | fn undocumented3() {}
| ^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a function
- --> $DIR/missing_doc.rs:93:9
- |
-LL | pub fn also_undocumented1() {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for a function
--> $DIR/missing_doc.rs:94:9
|
LL | fn also_undocumented2() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 24 previous errors
+error: aborting due to 13 previous errors
diff --git a/src/tools/clippy/tests/ui/missing_doc_impl.rs b/src/tools/clippy/tests/ui/missing_doc_impl.rs
index 0396d1193..e2d49b090 100644
--- a/src/tools/clippy/tests/ui/missing_doc_impl.rs
+++ b/src/tools/clippy/tests/ui/missing_doc_impl.rs
@@ -1,4 +1,4 @@
-// aux-build: proc_macro_with_span.rs
+// aux-build: proc_macros.rs
#![warn(clippy::missing_docs_in_private_items)]
#![allow(dead_code)]
@@ -7,8 +7,8 @@
//! Some garbage docs for the crate here
#![doc = "More garbage"]
-extern crate proc_macro_with_span;
-use proc_macro_with_span::with_span;
+extern crate proc_macros;
+use proc_macros::with_span;
struct Foo {
a: isize,
diff --git a/src/tools/clippy/tests/ui/missing_doc_impl.stderr b/src/tools/clippy/tests/ui/missing_doc_impl.stderr
index b410f56e1..111d65469 100644
--- a/src/tools/clippy/tests/ui/missing_doc_impl.stderr
+++ b/src/tools/clippy/tests/ui/missing_doc_impl.stderr
@@ -21,60 +21,12 @@ error: missing documentation for a struct field
LL | b: isize,
| ^^^^^^^^
-error: missing documentation for a struct
- --> $DIR/missing_doc_impl.rs:18: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:19:5
- |
-LL | pub a: isize,
- | ^^^^^^^^^^^^
-
error: missing documentation for a struct field
--> $DIR/missing_doc_impl.rs:20:5
|
LL | b: isize,
| ^^^^^^^^
-error: missing documentation for a trait
- --> $DIR/missing_doc_impl.rs:43:1
- |
-LL | / pub trait C {
-LL | | fn foo(&self);
-LL | | fn foo_with_impl(&self) {}
-LL | | }
- | |_^
-
-error: missing documentation for a method
- --> $DIR/missing_doc_impl.rs:44:5
- |
-LL | fn foo(&self);
- | ^^^^^^^^^^^^^^
-
-error: missing documentation for a method
- --> $DIR/missing_doc_impl.rs:45:5
- |
-LL | fn foo_with_impl(&self) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for an associated type
- --> $DIR/missing_doc_impl.rs:55:5
- |
-LL | type AssociatedType;
- | ^^^^^^^^^^^^^^^^^^^^
-
-error: missing documentation for an associated type
- --> $DIR/missing_doc_impl.rs:56:5
- |
-LL | type AssociatedTypeDef = Self;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
error: missing documentation for an associated function
--> $DIR/missing_doc_impl.rs:67:5
|
@@ -90,12 +42,6 @@ LL | fn bar() {}
| ^^^^^^^^^^^
error: missing documentation for an associated function
- --> $DIR/missing_doc_impl.rs:74:5
- |
-LL | pub fn foo() {}
- | ^^^^^^^^^^^^^^^
-
-error: missing documentation for an associated function
--> $DIR/missing_doc_impl.rs:78:5
|
LL | / fn foo2() -> u32 {
@@ -103,5 +49,5 @@ LL | | 1
LL | | }
| |_____^
-error: aborting due to 15 previous errors
+error: aborting due to 7 previous errors
diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed
index becb9562a..9a47d7c56 100644
--- a/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed
+++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed
@@ -1,5 +1,5 @@
// run-rustfix
-// aux-build: proc_macro_with_span.rs
+// aux-build: proc_macros.rs
#![allow(
dead_code,
@@ -10,8 +10,8 @@
clippy::unusual_byte_groupings
)]
-extern crate proc_macro_with_span;
-use proc_macro_with_span::with_span;
+extern crate proc_macros;
+use proc_macros::with_span;
fn main() {
let fail14 = 2_i32;
diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs
index ee841bcd7..04261cba5 100644
--- a/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs
+++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs
@@ -1,5 +1,5 @@
// run-rustfix
-// aux-build: proc_macro_with_span.rs
+// aux-build: proc_macros.rs
#![allow(
dead_code,
@@ -10,8 +10,8 @@
clippy::unusual_byte_groupings
)]
-extern crate proc_macro_with_span;
-use proc_macro_with_span::with_span;
+extern crate proc_macros;
+use proc_macros::with_span;
fn main() {
let fail14 = 2_32;
diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
index 4511bc99c..9082f1675 100644
--- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
+++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
@@ -1,12 +1,12 @@
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![allow(unused)]
#![allow(deref_nullptr)]
#![allow(clippy::unnecessary_operation)]
#![allow(clippy::drop_copy)]
#![warn(clippy::multiple_unsafe_ops_per_block)]
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::external;
use core::arch::asm;
@@ -113,7 +113,38 @@ unsafe fn read_char_good(ptr: *const u8) -> char {
// no lint
fn issue10259() {
- unsafe_macro!();
+ external!(unsafe {
+ *core::ptr::null::<()>();
+ *core::ptr::null::<()>();
+ });
+}
+
+fn _fn_ptr(x: unsafe fn()) {
+ unsafe {
+ x();
+ x();
+ }
+}
+
+fn _assoc_const() {
+ trait X {
+ const X: unsafe fn();
+ }
+ fn _f<T: X>() {
+ unsafe {
+ T::X();
+ T::X();
+ }
+ }
+}
+
+fn _field_fn_ptr(x: unsafe fn()) {
+ struct X(unsafe fn());
+ let x = X(x);
+ unsafe {
+ x.0();
+ x.0();
+ }
}
fn main() {}
diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
index 303aeb7ae..badc284ec 100644
--- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
+++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
@@ -125,5 +125,65 @@ note: raw pointer dereference occurs here
LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
| ^^^^^^^^^^^^^^^^^^
-error: aborting due to 5 previous errors
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+ --> $DIR/multiple_unsafe_ops_per_block.rs:123:5
+ |
+LL | / unsafe {
+LL | | x();
+LL | | x();
+LL | | }
+ | |_____^
+ |
+note: unsafe function call occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:124:9
+ |
+LL | x();
+ | ^^^
+note: unsafe function call occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:125:9
+ |
+LL | x();
+ | ^^^
+
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+ --> $DIR/multiple_unsafe_ops_per_block.rs:134:9
+ |
+LL | / unsafe {
+LL | | T::X();
+LL | | T::X();
+LL | | }
+ | |_________^
+ |
+note: unsafe function call occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:135:13
+ |
+LL | T::X();
+ | ^^^^^^
+note: unsafe function call occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:136:13
+ |
+LL | T::X();
+ | ^^^^^^
+
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+ --> $DIR/multiple_unsafe_ops_per_block.rs:144:5
+ |
+LL | / unsafe {
+LL | | x.0();
+LL | | x.0();
+LL | | }
+ | |_____^
+ |
+note: unsafe function call occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:145:9
+ |
+LL | x.0();
+ | ^^^^^
+note: unsafe function call occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:146:9
+ |
+LL | x.0();
+ | ^^^^^
+
+error: aborting due to 8 previous errors
diff --git a/src/tools/clippy/tests/ui/must_use_unit.fixed b/src/tools/clippy/tests/ui/must_use_unit.fixed
index 6c9aa434a..b7d375ff8 100644
--- a/src/tools/clippy/tests/ui/must_use_unit.fixed
+++ b/src/tools/clippy/tests/ui/must_use_unit.fixed
@@ -1,11 +1,11 @@
//run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::must_use_unit)]
#![allow(clippy::unused_unit)]
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::external;
pub fn must_use_default() {}
@@ -22,5 +22,8 @@ fn main() {
must_use_with_note();
// We should not lint in external macros
- must_use_unit!();
+ external!(
+ #[must_use]
+ fn foo() {}
+ );
}
diff --git a/src/tools/clippy/tests/ui/must_use_unit.rs b/src/tools/clippy/tests/ui/must_use_unit.rs
index 8a395dc28..74d6b4ca8 100644
--- a/src/tools/clippy/tests/ui/must_use_unit.rs
+++ b/src/tools/clippy/tests/ui/must_use_unit.rs
@@ -1,11 +1,11 @@
//run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::must_use_unit)]
#![allow(clippy::unused_unit)]
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::external;
#[must_use]
pub fn must_use_default() {}
@@ -22,5 +22,8 @@ fn main() {
must_use_with_note();
// We should not lint in external macros
- must_use_unit!();
+ external!(
+ #[must_use]
+ fn foo() {}
+ );
}
diff --git a/src/tools/clippy/tests/ui/mut_mut.rs b/src/tools/clippy/tests/ui/mut_mut.rs
index ee3a85656..06bb08544 100644
--- a/src/tools/clippy/tests/ui/mut_mut.rs
+++ b/src/tools/clippy/tests/ui/mut_mut.rs
@@ -1,10 +1,10 @@
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::mut_mut)]
#![allow(unused)]
#![allow(clippy::no_effect, clippy::uninlined_format_args, clippy::unnecessary_operation)]
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
fn fun(x: &mut &mut u32) -> bool {
**x > 0
@@ -21,6 +21,7 @@ macro_rules! mut_ptr {
}
#[allow(unused_mut, unused_variables)]
+#[inline_macros]
fn main() {
let mut x = &mut &mut 1u32;
{
@@ -37,7 +38,7 @@ fn main() {
***y + **x;
}
- let mut z = mut_ptr!(&mut 3u32);
+ let mut z = inline!(&mut $(&mut 3u32));
}
fn issue939() {
@@ -55,7 +56,7 @@ fn issue939() {
fn issue6922() {
// do not lint from an external macro
- mut_mut!();
+ external!(let mut_mut_ty: &mut &mut u32 = &mut &mut 1u32;);
}
mod issue9035 {
diff --git a/src/tools/clippy/tests/ui/mut_mut.stderr b/src/tools/clippy/tests/ui/mut_mut.stderr
index 6820a85aa..93b857eb2 100644
--- a/src/tools/clippy/tests/ui/mut_mut.stderr
+++ b/src/tools/clippy/tests/ui/mut_mut.stderr
@@ -7,54 +7,51 @@ 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:25:17
+ --> $DIR/mut_mut.rs:26:17
|
LL | let mut x = &mut &mut 1u32;
| ^^^^^^^^^^^^^^
error: generally you want to avoid `&mut &mut _` if possible
- --> $DIR/mut_mut.rs:19:9
+ --> $DIR/mut_mut.rs:41:25
|
-LL | &mut $p
- | ^^^^^^^
-...
-LL | let mut z = mut_ptr!(&mut 3u32);
- | ------------------- in this macro invocation
+LL | let mut z = inline!(&mut $(&mut 3u32));
+ | ^
|
- = note: this error originates in the macro `mut_ptr` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_fn_main` (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:27:21
+ --> $DIR/mut_mut.rs:28:21
|
LL | let mut y = &mut x;
| ^^^^^^
error: generally you want to avoid `&mut &mut _` if possible
- --> $DIR/mut_mut.rs:31:32
+ --> $DIR/mut_mut.rs:32:32
|
LL | let y: &mut &mut u32 = &mut &mut 2;
| ^^^^^^^^^^^
error: generally you want to avoid `&mut &mut _` if possible
- --> $DIR/mut_mut.rs:31:16
+ --> $DIR/mut_mut.rs:32:16
|
LL | let y: &mut &mut u32 = &mut &mut 2;
| ^^^^^^^^^^^^^
error: generally you want to avoid `&mut &mut _` if possible
- --> $DIR/mut_mut.rs:36:37
+ --> $DIR/mut_mut.rs:37: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:36:16
+ --> $DIR/mut_mut.rs:37: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:36:21
+ --> $DIR/mut_mut.rs:37:21
|
LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2;
| ^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/needless_late_init.fixed b/src/tools/clippy/tests/ui/needless_late_init.fixed
index 17f2227ba..86d899bb4 100644
--- a/src/tools/clippy/tests/ui/needless_late_init.fixed
+++ b/src/tools/clippy/tests/ui/needless_late_init.fixed
@@ -1,4 +1,5 @@
// run-rustfix
+// aux-build:proc_macros.rs
#![feature(let_chains)]
#![allow(unused)]
#![allow(
@@ -10,6 +11,8 @@
clippy::uninlined_format_args
)]
+extern crate proc_macros;
+
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::rc::Rc;
@@ -138,6 +141,7 @@ const fn in_const() -> &'static str {
a
}
+#[proc_macros::inline_macros]
fn does_not_lint() {
let z;
if false {
@@ -195,35 +199,27 @@ fn does_not_lint() {
}
y = 3;
- macro_rules! assign {
- ($i:ident) => {
- $i = 1;
- };
- }
let x;
- assign!(x);
+ inline!($x = 1;);
let x;
if true {
- assign!(x);
+ inline!($x = 1;);
} else {
x = 2;
}
- macro_rules! in_macro {
- () => {
- let x;
- x = 1;
+ inline!({
+ let x;
+ x = 1;
- let x;
- if true {
- x = 1;
- } else {
- x = 2;
- }
- };
- }
- in_macro!();
+ let x;
+ if true {
+ x = 1;
+ } else {
+ x = 2;
+ }
+ });
// ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613
let x;
diff --git a/src/tools/clippy/tests/ui/needless_late_init.rs b/src/tools/clippy/tests/ui/needless_late_init.rs
index d84457a29..969afb38e 100644
--- a/src/tools/clippy/tests/ui/needless_late_init.rs
+++ b/src/tools/clippy/tests/ui/needless_late_init.rs
@@ -1,4 +1,5 @@
// run-rustfix
+// aux-build:proc_macros.rs
#![feature(let_chains)]
#![allow(unused)]
#![allow(
@@ -10,6 +11,8 @@
clippy::uninlined_format_args
)]
+extern crate proc_macros;
+
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::rc::Rc;
@@ -138,6 +141,7 @@ const fn in_const() -> &'static str {
a
}
+#[proc_macros::inline_macros]
fn does_not_lint() {
let z;
if false {
@@ -195,35 +199,27 @@ fn does_not_lint() {
}
y = 3;
- macro_rules! assign {
- ($i:ident) => {
- $i = 1;
- };
- }
let x;
- assign!(x);
+ inline!($x = 1;);
let x;
if true {
- assign!(x);
+ inline!($x = 1;);
} else {
x = 2;
}
- macro_rules! in_macro {
- () => {
- let x;
- x = 1;
+ inline!({
+ let x;
+ x = 1;
- let x;
- if true {
- x = 1;
- } else {
- x = 2;
- }
- };
- }
- in_macro!();
+ let x;
+ if true {
+ x = 1;
+ } else {
+ x = 2;
+ }
+ });
// ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613
let x;
diff --git a/src/tools/clippy/tests/ui/needless_late_init.stderr b/src/tools/clippy/tests/ui/needless_late_init.stderr
index 0a256fb4a..eff782f8b 100644
--- a/src/tools/clippy/tests/ui/needless_late_init.stderr
+++ b/src/tools/clippy/tests/ui/needless_late_init.stderr
@@ -1,5 +1,5 @@
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:24:5
+ --> $DIR/needless_late_init.rs:27:5
|
LL | let a;
| ^^^^^^ created here
@@ -13,7 +13,7 @@ LL | let a = "zero";
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:27:5
+ --> $DIR/needless_late_init.rs:30:5
|
LL | let b;
| ^^^^^^ created here
@@ -27,7 +27,7 @@ LL | let b = 1;
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:28:5
+ --> $DIR/needless_late_init.rs:31:5
|
LL | let c;
| ^^^^^^ created here
@@ -41,7 +41,7 @@ LL | let c = 2;
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:32:5
+ --> $DIR/needless_late_init.rs:35:5
|
LL | let d: usize;
| ^^^^^^^^^^^^^ created here
@@ -54,7 +54,7 @@ LL | let d: usize = 1;
| ~~~~~~~~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:35:5
+ --> $DIR/needless_late_init.rs:38:5
|
LL | let e;
| ^^^^^^ created here
@@ -67,7 +67,7 @@ LL | let e = format!("{}", d);
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:40:5
+ --> $DIR/needless_late_init.rs:43:5
|
LL | let a;
| ^^^^^^
@@ -88,7 +88,7 @@ LL | };
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:49:5
+ --> $DIR/needless_late_init.rs:52:5
|
LL | let b;
| ^^^^^^
@@ -109,7 +109,7 @@ LL | };
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:56:5
+ --> $DIR/needless_late_init.rs:59:5
|
LL | let d;
| ^^^^^^
@@ -130,7 +130,7 @@ LL | };
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:64:5
+ --> $DIR/needless_late_init.rs:67:5
|
LL | let e;
| ^^^^^^
@@ -151,7 +151,7 @@ LL | };
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:71:5
+ --> $DIR/needless_late_init.rs:74:5
|
LL | let f;
| ^^^^^^
@@ -167,7 +167,7 @@ LL + 1 => "three",
|
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:77:5
+ --> $DIR/needless_late_init.rs:80:5
|
LL | let g: usize;
| ^^^^^^^^^^^^^
@@ -187,7 +187,7 @@ LL | };
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:85:5
+ --> $DIR/needless_late_init.rs:88:5
|
LL | let x;
| ^^^^^^ created here
@@ -201,7 +201,7 @@ LL | let x = 1;
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:89:5
+ --> $DIR/needless_late_init.rs:92:5
|
LL | let x;
| ^^^^^^ created here
@@ -215,7 +215,7 @@ LL | let x = SignificantDrop;
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:93:5
+ --> $DIR/needless_late_init.rs:96:5
|
LL | let x;
| ^^^^^^ created here
@@ -229,7 +229,7 @@ LL | let x = SignificantDrop;
| ~~~~~
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:112:5
+ --> $DIR/needless_late_init.rs:115:5
|
LL | let a;
| ^^^^^^
@@ -250,7 +250,7 @@ LL | };
| +
error: unneeded late initialization
- --> $DIR/needless_late_init.rs:129:5
+ --> $DIR/needless_late_init.rs:132:5
|
LL | let a;
| ^^^^^^
diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.fixed b/src/tools/clippy/tests/ui/needless_lifetimes.fixed
index f0f1f9298..e6ead69d1 100644
--- a/src/tools/clippy/tests/ui/needless_lifetimes.fixed
+++ b/src/tools/clippy/tests/ui/needless_lifetimes.fixed
@@ -1,5 +1,5 @@
// run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::needless_lifetimes)]
#![allow(
@@ -12,8 +12,8 @@
clippy::get_first
)]
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::inline_macros;
fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {}
@@ -502,30 +502,29 @@ mod pr_9743_output_lifetime_checks {
}
}
+#[inline_macros]
mod in_macro {
- macro_rules! local_one_input_macro {
- () => {
- fn one_input(x: &u8) -> &u8 {
- unimplemented!()
- }
- };
- }
+ use proc_macros::external;
// lint local macro expands to function with needless lifetimes
- local_one_input_macro!();
+ inline! {
+ fn one_input(x: &u8) -> &u8 {
+ unimplemented!()
+ }
+ }
// no lint on external macro
- macro_rules::needless_lifetime!();
-
- macro_rules! expanded_lifetime {
- ($l:lifetime) => {
- fn f<$l>(arg: &$l str) -> &$l str {
- arg
- }
+ external! {
+ fn needless_lifetime<'a>(x: &'a u8) -> &'a u8 {
+ unimplemented!()
}
}
- expanded_lifetime!('a);
+ inline! {
+ fn f<$'a>(arg: &$'a str) -> &$'a str {
+ arg
+ }
+ }
}
mod issue5787 {
diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs
index ddfd10430..06eb43050 100644
--- a/src/tools/clippy/tests/ui/needless_lifetimes.rs
+++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs
@@ -1,5 +1,5 @@
// run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::needless_lifetimes)]
#![allow(
@@ -12,8 +12,8 @@
clippy::get_first
)]
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::inline_macros;
fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
@@ -502,30 +502,29 @@ mod pr_9743_output_lifetime_checks {
}
}
+#[inline_macros]
mod in_macro {
- macro_rules! local_one_input_macro {
- () => {
- fn one_input<'a>(x: &'a u8) -> &'a u8 {
- unimplemented!()
- }
- };
- }
+ use proc_macros::external;
// lint local macro expands to function with needless lifetimes
- local_one_input_macro!();
+ inline! {
+ fn one_input<'a>(x: &'a u8) -> &'a u8 {
+ unimplemented!()
+ }
+ }
// no lint on external macro
- macro_rules::needless_lifetime!();
-
- macro_rules! expanded_lifetime {
- ($l:lifetime) => {
- fn f<$l>(arg: &$l str) -> &$l str {
- arg
- }
+ external! {
+ fn needless_lifetime<'a>(x: &'a u8) -> &'a u8 {
+ unimplemented!()
}
}
- expanded_lifetime!('a);
+ inline! {
+ fn f<$'a>(arg: &$'a str) -> &$'a str {
+ arg
+ }
+ }
}
mod issue5787 {
diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.stderr b/src/tools/clippy/tests/ui/needless_lifetimes.stderr
index 4e3c8f20d..86acc4e00 100644
--- a/src/tools/clippy/tests/ui/needless_lifetimes.stderr
+++ b/src/tools/clippy/tests/ui/needless_lifetimes.stderr
@@ -540,19 +540,16 @@ LL + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8)
|
error: the following explicit lifetimes could be elided: 'a
- --> $DIR/needless_lifetimes.rs:508:13
+ --> $DIR/needless_lifetimes.rs:511:9
|
-LL | fn one_input<'a>(x: &'a u8) -> &'a u8 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-...
-LL | local_one_input_macro!();
- | ------------------------ in this macro invocation
+LL | fn one_input<'a>(x: &'a u8) -> &'a u8 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: this error originates in the macro `local_one_input_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_mod_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
help: elide the lifetimes
|
-LL - fn one_input<'a>(x: &'a u8) -> &'a u8 {
-LL + fn one_input(x: &u8) -> &u8 {
+LL - fn one_input<'a>(x: &'a u8) -> &'a u8 {
+LL + fn one_input(x: &u8) -> &u8 {
|
error: aborting due to 46 previous errors
diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed
index 0f525dd29..57c08996c 100644
--- a/src/tools/clippy/tests/ui/needless_return.fixed
+++ b/src/tools/clippy/tests/ui/needless_return.fixed
@@ -307,4 +307,13 @@ mod issue10049 {
}
}
+fn test_match_as_stmt() {
+ let x = 9;
+ match x {
+ 1 => 2,
+ 2 => return,
+ _ => 0,
+ };
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs
index a1db8375d..7c1feefbe 100644
--- a/src/tools/clippy/tests/ui/needless_return.rs
+++ b/src/tools/clippy/tests/ui/needless_return.rs
@@ -317,4 +317,13 @@ mod issue10049 {
}
}
+fn test_match_as_stmt() {
+ let x = 9;
+ match x {
+ 1 => 2,
+ 2 => return,
+ _ => 0,
+ };
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_update.rs b/src/tools/clippy/tests/ui/needless_update.rs
index b93ff048a..4e8517cad 100644
--- a/src/tools/clippy/tests/ui/needless_update.rs
+++ b/src/tools/clippy/tests/ui/needless_update.rs
@@ -1,5 +1,5 @@
#![warn(clippy::needless_update)]
-#![allow(clippy::no_effect)]
+#![allow(clippy::no_effect, clippy::unnecessary_struct_initialization)]
struct S {
pub a: i32,
diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.rs b/src/tools/clippy/tests/ui/new_ret_no_self.rs
index beec42f08..a2a30c8b9 100644
--- a/src/tools/clippy/tests/ui/new_ret_no_self.rs
+++ b/src/tools/clippy/tests/ui/new_ret_no_self.rs
@@ -406,7 +406,7 @@ mod issue10041 {
struct Bomb;
impl Bomb {
- // Hidden <Rhs = Self> default generic paramter.
+ // Hidden <Rhs = Self> default generic parameter.
pub fn new() -> impl PartialOrd {
0i32
}
diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs
index f08eb092e..1e42e1fba 100644
--- a/src/tools/clippy/tests/ui/no_effect.rs
+++ b/src/tools/clippy/tests/ui/no_effect.rs
@@ -1,7 +1,12 @@
-#![feature(box_syntax, fn_traits, unboxed_closures)]
+#![feature(fn_traits, unboxed_closures)]
#![warn(clippy::no_effect_underscore_binding)]
#![allow(dead_code, path_statements)]
-#![allow(clippy::deref_addrof, clippy::redundant_field_names, clippy::uninlined_format_args)]
+#![allow(
+ clippy::deref_addrof,
+ clippy::redundant_field_names,
+ clippy::uninlined_format_args,
+ clippy::unnecessary_struct_initialization
+)]
struct Unit;
struct Tuple(i32);
@@ -102,7 +107,6 @@ fn main() {
*&42;
&6;
(5, 6, 7);
- box 42;
..;
5..;
..5;
diff --git a/src/tools/clippy/tests/ui/no_effect.stderr b/src/tools/clippy/tests/ui/no_effect.stderr
index 6a1e636f9..f10f2bcf2 100644
--- a/src/tools/clippy/tests/ui/no_effect.stderr
+++ b/src/tools/clippy/tests/ui/no_effect.stderr
@@ -1,5 +1,5 @@
error: statement with no effect
- --> $DIR/no_effect.rs:92:5
+ --> $DIR/no_effect.rs:97:5
|
LL | 0;
| ^^
@@ -7,157 +7,151 @@ LL | 0;
= note: `-D clippy::no-effect` implied by `-D warnings`
error: statement with no effect
- --> $DIR/no_effect.rs:93:5
+ --> $DIR/no_effect.rs:98:5
|
LL | s2;
| ^^^
error: statement with no effect
- --> $DIR/no_effect.rs:94:5
+ --> $DIR/no_effect.rs:99:5
|
LL | Unit;
| ^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:95:5
+ --> $DIR/no_effect.rs:100:5
|
LL | Tuple(0);
| ^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:96:5
+ --> $DIR/no_effect.rs:101:5
|
LL | Struct { field: 0 };
| ^^^^^^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:97:5
+ --> $DIR/no_effect.rs:102:5
|
LL | Struct { ..s };
| ^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:98:5
+ --> $DIR/no_effect.rs:103:5
|
LL | Union { a: 0 };
| ^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:99:5
+ --> $DIR/no_effect.rs:104:5
|
LL | Enum::Tuple(0);
| ^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:100:5
+ --> $DIR/no_effect.rs:105:5
|
LL | Enum::Struct { field: 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:101:5
+ --> $DIR/no_effect.rs:106:5
|
LL | 5 + 6;
| ^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:102:5
+ --> $DIR/no_effect.rs:107:5
|
LL | *&42;
| ^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:103:5
+ --> $DIR/no_effect.rs:108:5
|
LL | &6;
| ^^^
error: statement with no effect
- --> $DIR/no_effect.rs:104:5
+ --> $DIR/no_effect.rs:109:5
|
LL | (5, 6, 7);
| ^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:105:5
- |
-LL | box 42;
- | ^^^^^^^
-
-error: statement with no effect
- --> $DIR/no_effect.rs:106:5
+ --> $DIR/no_effect.rs:110:5
|
LL | ..;
| ^^^
error: statement with no effect
- --> $DIR/no_effect.rs:107:5
+ --> $DIR/no_effect.rs:111:5
|
LL | 5..;
| ^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:108:5
+ --> $DIR/no_effect.rs:112:5
|
LL | ..5;
| ^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:109:5
+ --> $DIR/no_effect.rs:113:5
|
LL | 5..6;
| ^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:110:5
+ --> $DIR/no_effect.rs:114:5
|
LL | 5..=6;
| ^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:111:5
+ --> $DIR/no_effect.rs:115:5
|
LL | [42, 55];
| ^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:112:5
+ --> $DIR/no_effect.rs:116:5
|
LL | [42, 55][1];
| ^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:113:5
+ --> $DIR/no_effect.rs:117:5
|
LL | (42, 55).1;
| ^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:114:5
+ --> $DIR/no_effect.rs:118:5
|
LL | [42; 55];
| ^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:115:5
+ --> $DIR/no_effect.rs:119:5
|
LL | [42; 55][13];
| ^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:117:5
+ --> $DIR/no_effect.rs:121:5
|
LL | || x += 5;
| ^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:119:5
+ --> $DIR/no_effect.rs:123:5
|
LL | FooString { s: s };
| ^^^^^^^^^^^^^^^^^^^
error: binding to `_` prefixed variable with no side-effect
- --> $DIR/no_effect.rs:120:5
+ --> $DIR/no_effect.rs:124:5
|
LL | let _unused = 1;
| ^^^^^^^^^^^^^^^^
@@ -165,22 +159,22 @@ LL | let _unused = 1;
= note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings`
error: binding to `_` prefixed variable with no side-effect
- --> $DIR/no_effect.rs:121:5
+ --> $DIR/no_effect.rs:125:5
|
LL | let _penguin = || println!("Some helpful closure");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: binding to `_` prefixed variable with no side-effect
- --> $DIR/no_effect.rs:122:5
+ --> $DIR/no_effect.rs:126:5
|
LL | let _duck = Struct { field: 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: binding to `_` prefixed variable with no side-effect
- --> $DIR/no_effect.rs:123:5
+ --> $DIR/no_effect.rs:127:5
|
LL | let _cat = [2, 4, 6, 8][2];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 30 previous errors
+error: aborting due to 29 previous errors
diff --git a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.fixed b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.fixed
deleted file mode 100644
index d18dec22a..000000000
--- a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.fixed
+++ /dev/null
@@ -1,48 +0,0 @@
-// run-rustfix
-
-#![allow(unused)]
-#![warn(clippy::no_mangle_with_rust_abi)]
-
-#[no_mangle]
-extern "C" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
-
-#[no_mangle]
-pub extern "C" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
-
-/// # Safety
-/// This function shouldn't be called unless the horsemen are ready
-#[no_mangle]
-pub unsafe extern "C" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
-
-/// # Safety
-/// This function shouldn't be called unless the horsemen are ready
-#[no_mangle]
-unsafe extern "C" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
-
-#[no_mangle]
-extern "C" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
- arg_one: u32,
- arg_two: usize,
-) -> u32 {
- 0
-}
-
-// Must not run on functions that explicitly opt in to Rust ABI with `extern "Rust"`
-#[no_mangle]
-#[rustfmt::skip]
-extern "Rust" fn rust_abi_fn_explicit_opt_in(arg_one: u32, arg_two: usize) {}
-
-fn rust_abi_fn_again(arg_one: u32, arg_two: usize) {}
-
-#[no_mangle]
-extern "C" fn c_abi_fn(arg_one: u32, arg_two: usize) {}
-
-extern "C" fn c_abi_fn_again(arg_one: u32, arg_two: usize) {}
-
-extern "C" {
- fn c_abi_in_block(arg_one: u32, arg_two: usize);
-}
-
-fn main() {
- // test code goes here
-}
diff --git a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs
index 481e1b6d9..b32e72111 100644
--- a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs
+++ b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs
@@ -1,5 +1,3 @@
-// run-rustfix
-
#![allow(unused)]
#![warn(clippy::no_mangle_with_rust_abi)]
diff --git a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr
index 71517d318..da5d31d8f 100644
--- a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr
+++ b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr
@@ -1,31 +1,66 @@
-error: attribute #[no_mangle] set on a Rust ABI function
- --> $DIR/no_mangle_with_rust_abi.rs:7:1
+error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+ --> $DIR/no_mangle_with_rust_abi.rs:5:1
|
LL | fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `extern "C" fn rust_abi_fn_one(arg_one: u32, arg_two: usize)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings`
+help: set an ABI
+ |
+LL | extern "C" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
+ | ++++++++++
+help: or explicitly set the default
+ |
+LL | extern "Rust" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
+ | +++++++++++++
-error: attribute #[no_mangle] set on a Rust ABI function
- --> $DIR/no_mangle_with_rust_abi.rs:10:1
+error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+ --> $DIR/no_mangle_with_rust_abi.rs:8:1
|
LL | pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `pub extern "C" fn rust_abi_fn_two(arg_one: u32, arg_two: usize)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: set an ABI
+ |
+LL | pub extern "C" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
+ | ++++++++++
+help: or explicitly set the default
+ |
+LL | pub extern "Rust" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
+ | +++++++++++++
-error: attribute #[no_mangle] set on a Rust ABI function
- --> $DIR/no_mangle_with_rust_abi.rs:15:1
+error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+ --> $DIR/no_mangle_with_rust_abi.rs:13:1
|
LL | pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `pub unsafe extern "C" fn rust_abi_fn_three(arg_one: u32, arg_two: usize)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: set an ABI
+ |
+LL | pub unsafe extern "C" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
+ | ++++++++++
+help: or explicitly set the default
+ |
+LL | pub unsafe extern "Rust" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
+ | +++++++++++++
-error: attribute #[no_mangle] set on a Rust ABI function
- --> $DIR/no_mangle_with_rust_abi.rs:20:1
+error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+ --> $DIR/no_mangle_with_rust_abi.rs:18:1
|
LL | unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unsafe extern "C" fn rust_abi_fn_four(arg_one: u32, arg_two: usize)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: set an ABI
+ |
+LL | unsafe extern "C" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
+ | ++++++++++
+help: or explicitly set the default
+ |
+LL | unsafe extern "Rust" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
+ | +++++++++++++
-error: attribute #[no_mangle] set on a Rust ABI function
- --> $DIR/no_mangle_with_rust_abi.rs:23:1
+error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+ --> $DIR/no_mangle_with_rust_abi.rs:21:1
|
LL | / fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
LL | | arg_one: u32,
@@ -33,13 +68,14 @@ LL | | arg_two: usize,
LL | | ) -> u32 {
| |________^
|
-help: try
+help: set an ABI
|
-LL + extern "C" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
-LL + arg_one: u32,
-LL + arg_two: usize,
-LL ~ ) -> u32 {
+LL | extern "C" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
+ | ++++++++++
+help: or explicitly set the default
|
+LL | extern "Rust" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
+ | +++++++++++++
error: aborting due to 5 previous errors
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.rs b/src/tools/clippy/tests/ui/nonminimal_bool.rs
index e9b4367ca..80cc7c60f 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool.rs
+++ b/src/tools/clippy/tests/ui/nonminimal_bool.rs
@@ -63,3 +63,50 @@ fn issue9428() {
println!("foo");
}
}
+
+fn issue_10523() {
+ macro_rules! a {
+ ($v:expr) => {
+ $v.is_some()
+ };
+ }
+ let x: Option<u32> = None;
+ if !a!(x) {}
+}
+
+fn issue_10523_1() {
+ macro_rules! a {
+ ($v:expr) => {
+ !$v.is_some()
+ };
+ }
+ let x: Option<u32> = None;
+ if a!(x) {}
+}
+
+fn issue_10523_2() {
+ macro_rules! a {
+ () => {
+ !None::<u32>.is_some()
+ };
+ }
+ if a!() {}
+}
+
+fn issue_10435() {
+ let x = vec![0];
+ let y = vec![1];
+ let z = vec![2];
+
+ // vvv Should not lint
+ #[allow(clippy::nonminimal_bool)]
+ if !x.is_empty() && !(y.is_empty() || z.is_empty()) {
+ println!("{}", line!());
+ }
+
+ // vvv Should not lint (#10435 talks about a bug where it lints)
+ #[allow(clippy::nonminimal_bool)]
+ if !(x == [0]) {
+ println!("{}", line!());
+ }
+}
diff --git a/src/tools/clippy/tests/ui/option_env_unwrap.rs b/src/tools/clippy/tests/ui/option_env_unwrap.rs
index 0141fb785..9a56cf40d 100644
--- a/src/tools/clippy/tests/ui/option_env_unwrap.rs
+++ b/src/tools/clippy/tests/ui/option_env_unwrap.rs
@@ -1,24 +1,16 @@
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::option_env_unwrap)]
#![allow(clippy::map_flatten)]
-#[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)
- };
-}
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
+#[inline_macros]
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");
+ let _ = inline!(option_env!($"PATH").unwrap());
+ let _ = inline!(option_env!($"PATH").expect($"environment variable PATH isn't set"));
+ let _ = external!(option_env!($"PATH").unwrap());
+ let _ = external!(option_env!($"PATH").expect($"environment variable PATH isn't set"));
}
diff --git a/src/tools/clippy/tests/ui/option_env_unwrap.stderr b/src/tools/clippy/tests/ui/option_env_unwrap.stderr
index bc188a07e..7bba62686 100644
--- a/src/tools/clippy/tests/ui/option_env_unwrap.stderr
+++ b/src/tools/clippy/tests/ui/option_env_unwrap.stderr
@@ -1,5 +1,5 @@
error: this will panic at run-time if the environment variable doesn't exist at compile-time
- --> $DIR/option_env_unwrap.rs:18:13
+ --> $DIR/option_env_unwrap.rs:10:13
|
LL | let _ = option_env!("PATH").unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | let _ = option_env!("PATH").unwrap();
= note: `-D clippy::option-env-unwrap` implied by `-D warnings`
error: this will panic at run-time if the environment variable doesn't exist at compile-time
- --> $DIR/option_env_unwrap.rs:19:13
+ --> $DIR/option_env_unwrap.rs:11:13
|
LL | let _ = option_env!("PATH").expect("environment variable PATH isn't set");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,46 +16,40 @@ 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:10:9
+ --> $DIR/option_env_unwrap.rs:12:21
|
-LL | option_env!($env).unwrap()
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
-...
-LL | let _ = option_env_unwrap!("PATH");
- | -------------------------- in this macro invocation
+LL | let _ = inline!(option_env!($"PATH").unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using the `env!` macro instead
- = note: this error originates in the macro `option_env_unwrap` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_fn_main` (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:13:9
+ --> $DIR/option_env_unwrap.rs:13:21
|
-LL | option_env!($env).expect($message)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-...
-LL | let _ = option_env_unwrap!("PATH", "environment variable PATH isn't set");
- | ----------------------------------------------------------------- in this macro invocation
+LL | let _ = inline!(option_env!($"PATH").expect($"environment variable PATH isn't set"));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using the `env!` macro instead
- = note: this error originates in the macro `option_env_unwrap` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_fn_main` (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
+ --> $DIR/option_env_unwrap.rs:14:13
|
-LL | let _ = option_env_unwrap_external!("PATH");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let _ = external!(option_env!($"PATH").unwrap());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using the `env!` macro instead
- = note: this error originates in the macro `option_env_unwrap_external` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `external` (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:23:13
+ --> $DIR/option_env_unwrap.rs:15:13
|
-LL | let _ = option_env_unwrap_external!("PATH", "environment variable PATH isn't set");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let _ = external!(option_env!($"PATH").expect($"environment variable PATH isn't set"));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using the `env!` macro instead
- = note: this error originates in the macro `option_env_unwrap_external` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `external` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 6 previous errors
diff --git a/src/tools/clippy/tests/ui/overflow_check_conditional.rs b/src/tools/clippy/tests/ui/overflow_check_conditional.rs
index 5db75f529..e1e301140 100644
--- a/src/tools/clippy/tests/ui/overflow_check_conditional.rs
+++ b/src/tools/clippy/tests/ui/overflow_check_conditional.rs
@@ -1,9 +1,6 @@
#![warn(clippy::overflow_check_conditional)]
-fn main() {
- let a: u32 = 1;
- let b: u32 = 2;
- let c: u32 = 3;
+fn test(a: u32, b: u32, c: u32) {
if a + b < a {}
if a > a + b {}
if a + b < b {}
@@ -23,3 +20,7 @@ fn main() {
if i > i + j {}
if i - j < i {}
}
+
+fn main() {
+ test(1, 2, 3)
+}
diff --git a/src/tools/clippy/tests/ui/overflow_check_conditional.stderr b/src/tools/clippy/tests/ui/overflow_check_conditional.stderr
index 1b8b146b6..92d1d8ef9 100644
--- a/src/tools/clippy/tests/ui/overflow_check_conditional.stderr
+++ b/src/tools/clippy/tests/ui/overflow_check_conditional.stderr
@@ -1,5 +1,5 @@
error: you are trying to use classic C overflow conditions that will fail in Rust
- --> $DIR/overflow_check_conditional.rs:7:8
+ --> $DIR/overflow_check_conditional.rs:4:8
|
LL | if a + b < a {}
| ^^^^^^^^^
@@ -7,43 +7,43 @@ 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:8:8
+ --> $DIR/overflow_check_conditional.rs:5: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:9:8
+ --> $DIR/overflow_check_conditional.rs:6: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:10:8
+ --> $DIR/overflow_check_conditional.rs:7: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:11:8
+ --> $DIR/overflow_check_conditional.rs:8: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:12:8
+ --> $DIR/overflow_check_conditional.rs:9: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:13:8
+ --> $DIR/overflow_check_conditional.rs:10: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:14:8
+ --> $DIR/overflow_check_conditional.rs:11:8
|
LL | if a < a - b {}
| ^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/print_literal.rs b/src/tools/clippy/tests/ui/print_literal.rs
index 86f908f66..538513e91 100644
--- a/src/tools/clippy/tests/ui/print_literal.rs
+++ b/src/tools/clippy/tests/ui/print_literal.rs
@@ -38,4 +38,8 @@ fn main() {
// named args shouldn't change anything either
println!("{foo} {bar}", foo = "hello", bar = "world");
println!("{bar} {foo}", foo = "hello", bar = "world");
+
+ // The string literal from `file!()` has a callsite span that isn't marked as coming from an
+ // expansion
+ println!("file: {}", file!());
}
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
index df36a9b84..ee7b998a0 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
@@ -1,16 +1,12 @@
// run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::ptr_as_ptr)]
-extern crate macro_rules;
-
-macro_rules! cast_it {
- ($ptr: ident) => {
- $ptr.cast::<i32>()
- };
-}
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
+#[inline_macros]
fn main() {
let ptr: *const u32 = &42_u32;
let mut_ptr: *mut u32 = &mut 42_u32;
@@ -38,10 +34,10 @@ fn main() {
let _: *mut i32 = mut_ptr.cast();
// Make sure the lint is triggered inside a macro
- let _ = cast_it!(ptr);
+ let _ = inline!($ptr.cast::<i32>());
// Do not lint inside macros from external crates
- let _ = macro_rules::ptr_as_ptr_cast!(ptr);
+ let _ = external!($ptr as *const i32);
}
#[clippy::msrv = "1.37"]
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.rs b/src/tools/clippy/tests/ui/ptr_as_ptr.rs
index 302c66462..c88329ce4 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.rs
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.rs
@@ -1,16 +1,12 @@
// run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::ptr_as_ptr)]
-extern crate macro_rules;
-
-macro_rules! cast_it {
- ($ptr: ident) => {
- $ptr as *const i32
- };
-}
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
+#[inline_macros]
fn main() {
let ptr: *const u32 = &42_u32;
let mut_ptr: *mut u32 = &mut 42_u32;
@@ -38,10 +34,10 @@ fn main() {
let _: *mut i32 = mut_ptr as _;
// Make sure the lint is triggered inside a macro
- let _ = cast_it!(ptr);
+ let _ = inline!($ptr as *const i32);
// Do not lint inside macros from external crates
- let _ = macro_rules::ptr_as_ptr_cast!(ptr);
+ let _ = external!($ptr as *const i32);
}
#[clippy::msrv = "1.37"]
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
index a68e1cab6..78d733994 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
@@ -1,5 +1,5 @@
error: `as` casting between raw pointers without changing its mutability
- --> $DIR/ptr_as_ptr.rs:18:13
+ --> $DIR/ptr_as_ptr.rs:14:13
|
LL | let _ = ptr as *const i32;
| ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<i32>()`
@@ -7,48 +7,45 @@ LL | let _ = ptr as *const i32;
= note: `-D clippy::ptr-as-ptr` implied by `-D warnings`
error: `as` casting between raw pointers without changing its mutability
- --> $DIR/ptr_as_ptr.rs:19:13
+ --> $DIR/ptr_as_ptr.rs:15:13
|
LL | let _ = mut_ptr as *mut i32;
| ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
error: `as` casting between raw pointers without changing its mutability
- --> $DIR/ptr_as_ptr.rs:24:17
+ --> $DIR/ptr_as_ptr.rs:20:17
|
LL | let _ = *ptr_ptr as *const i32;
| ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::<i32>()`
error: `as` casting between raw pointers without changing its mutability
- --> $DIR/ptr_as_ptr.rs:37:25
+ --> $DIR/ptr_as_ptr.rs:33:25
|
LL | let _: *const i32 = ptr as *const _;
| ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()`
error: `as` casting between raw pointers without changing its mutability
- --> $DIR/ptr_as_ptr.rs:38:23
+ --> $DIR/ptr_as_ptr.rs:34:23
|
LL | let _: *mut i32 = mut_ptr as _;
| ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()`
error: `as` casting between raw pointers without changing its mutability
- --> $DIR/ptr_as_ptr.rs:10:9
+ --> $DIR/ptr_as_ptr.rs:37:21
|
-LL | $ptr as *const i32
- | ^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `$ptr.cast::<i32>()`
-...
-LL | let _ = cast_it!(ptr);
- | ------------- in this macro invocation
+LL | let _ = inline!($ptr as *const i32);
+ | ^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `$ptr.cast::<i32>()`
|
- = note: this error originates in the macro `cast_it` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info)
error: `as` casting between raw pointers without changing its mutability
- --> $DIR/ptr_as_ptr.rs:62:13
+ --> $DIR/ptr_as_ptr.rs:58:13
|
LL | let _ = ptr as *const i32;
| ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<i32>()`
error: `as` casting between raw pointers without changing its mutability
- --> $DIR/ptr_as_ptr.rs:63:13
+ --> $DIR/ptr_as_ptr.rs:59:13
|
LL | let _ = mut_ptr as *mut i32;
| ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
diff --git a/src/tools/clippy/tests/ui/redundant_async_block.fixed b/src/tools/clippy/tests/ui/redundant_async_block.fixed
new file mode 100644
index 000000000..ad96993c4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_async_block.fixed
@@ -0,0 +1,193 @@
+// run-rustfix
+
+#![allow(unused, clippy::manual_async_fn)]
+#![warn(clippy::redundant_async_block)]
+
+use std::future::Future;
+
+async fn func1(n: usize) -> usize {
+ n + 1
+}
+
+async fn func2() -> String {
+ let s = String::from("some string");
+ let f = async { (*s).to_owned() };
+ let x = f;
+ x.await
+}
+
+fn main() {
+ let fut1 = async { 17 };
+ // Lint
+ let fut2 = fut1;
+
+ let fut1 = async { 25 };
+ // Lint
+ let fut2 = fut1;
+
+ // Lint
+ let fut = async { 42 };
+
+ // Do not lint: not a single expression
+ let fut = async {
+ func1(10).await;
+ func2().await
+ };
+
+ // Do not lint: expression contains `.await`
+ let fut = async { func1(func2().await.len()).await };
+}
+
+#[allow(clippy::let_and_return)]
+fn capture_local() -> impl Future<Output = i32> {
+ let fut = async { 17 };
+ // Lint
+ fut
+}
+
+fn capture_local_closure(s: &str) -> impl Future<Output = &str> {
+ let f = move || std::future::ready(s);
+ // Do not lint: `f` would not live long enough
+ async move { f().await }
+}
+
+#[allow(clippy::let_and_return)]
+fn capture_arg(s: &str) -> impl Future<Output = &str> {
+ let fut = async move { s };
+ // Lint
+ fut
+}
+
+fn capture_future_arg<T>(f: impl Future<Output = T>) -> impl Future<Output = T> {
+ // Lint
+ f
+}
+
+fn capture_func_result<FN, F, T>(f: FN) -> impl Future<Output = T>
+where
+ F: Future<Output = T>,
+ FN: FnOnce() -> F,
+{
+ // Do not lint, as f() would be evaluated prematurely
+ async { f().await }
+}
+
+fn double_future(f: impl Future<Output = impl Future<Output = u32>>) -> impl Future<Output = u32> {
+ // Do not lint, we will get a `.await` outside a `.async`
+ async { f.await.await }
+}
+
+fn await_in_async<F, R>(f: F) -> impl Future<Output = u32>
+where
+ F: FnOnce() -> R,
+ R: Future<Output = u32>,
+{
+ // Lint
+ async { f().await + 1 }
+}
+
+#[derive(Debug, Clone)]
+struct F {}
+
+impl F {
+ async fn run(&self) {}
+}
+
+pub async fn run() {
+ let f = F {};
+ let c = f.clone();
+ // Do not lint: `c` would not live long enough
+ spawn(async move { c.run().await });
+ let _f = f;
+}
+
+fn spawn<F: Future + 'static>(_: F) {}
+
+async fn work(_: &str) {}
+
+fn capture() {
+ let val = "Hello World".to_owned();
+ // Do not lint: `val` would not live long enough
+ spawn(async { work(&{ val }).await });
+}
+
+fn await_from_macro() -> impl Future<Output = u32> {
+ macro_rules! mac {
+ ($e:expr) => {
+ $e.await
+ };
+ }
+ // Do not lint: the macro may change in the future
+ // or return different things depending on its argument
+ async { mac!(async { 42 }) }
+}
+
+fn async_expr_from_macro() -> impl Future<Output = u32> {
+ macro_rules! mac {
+ () => {
+ async { 42 }
+ };
+ }
+ // Do not lint: the macro may change in the future
+ async { mac!().await }
+}
+
+fn async_expr_from_macro_deep() -> impl Future<Output = u32> {
+ macro_rules! mac {
+ () => {
+ async { 42 }
+ };
+ }
+ // Do not lint: the macro may change in the future
+ async { ({ mac!() }).await }
+}
+
+fn all_from_macro() -> impl Future<Output = u32> {
+ macro_rules! mac {
+ () => {
+ // Lint
+ async { 42 }
+ };
+ }
+ mac!()
+}
+
+fn parts_from_macro() -> impl Future<Output = u32> {
+ macro_rules! mac {
+ ($e: expr) => {
+ // Do not lint: `$e` might not always be side-effect free
+ async { $e.await }
+ };
+ }
+ mac!(async { 42 })
+}
+
+fn safe_parts_from_macro() -> impl Future<Output = u32> {
+ macro_rules! mac {
+ ($e: expr) => {
+ // Lint
+ async { $e }
+ };
+ }
+ mac!(42)
+}
+
+fn parts_from_macro_deep() -> impl Future<Output = u32> {
+ macro_rules! mac {
+ ($e: expr) => {
+ // Do not lint: `$e` might not always be side-effect free
+ async { ($e,).0.await }
+ };
+ }
+ let f = std::future::ready(42);
+ mac!(f)
+}
+
+fn await_from_macro_deep() -> impl Future<Output = u32> {
+ macro_rules! mac {
+ ($e:expr) => {{ $e }.await};
+ }
+ // Do not lint: the macro may change in the future
+ // or return different things depending on its argument
+ async { mac!(async { 42 }) }
+}
diff --git a/src/tools/clippy/tests/ui/redundant_async_block.rs b/src/tools/clippy/tests/ui/redundant_async_block.rs
new file mode 100644
index 000000000..7ae235583
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_async_block.rs
@@ -0,0 +1,193 @@
+// run-rustfix
+
+#![allow(unused, clippy::manual_async_fn)]
+#![warn(clippy::redundant_async_block)]
+
+use std::future::Future;
+
+async fn func1(n: usize) -> usize {
+ n + 1
+}
+
+async fn func2() -> String {
+ let s = String::from("some string");
+ let f = async { (*s).to_owned() };
+ let x = async { f.await };
+ x.await
+}
+
+fn main() {
+ let fut1 = async { 17 };
+ // Lint
+ let fut2 = async { fut1.await };
+
+ let fut1 = async { 25 };
+ // Lint
+ let fut2 = async move { fut1.await };
+
+ // Lint
+ let fut = async { async { 42 }.await };
+
+ // Do not lint: not a single expression
+ let fut = async {
+ func1(10).await;
+ func2().await
+ };
+
+ // Do not lint: expression contains `.await`
+ let fut = async { func1(func2().await.len()).await };
+}
+
+#[allow(clippy::let_and_return)]
+fn capture_local() -> impl Future<Output = i32> {
+ let fut = async { 17 };
+ // Lint
+ async move { fut.await }
+}
+
+fn capture_local_closure(s: &str) -> impl Future<Output = &str> {
+ let f = move || std::future::ready(s);
+ // Do not lint: `f` would not live long enough
+ async move { f().await }
+}
+
+#[allow(clippy::let_and_return)]
+fn capture_arg(s: &str) -> impl Future<Output = &str> {
+ let fut = async move { s };
+ // Lint
+ async move { fut.await }
+}
+
+fn capture_future_arg<T>(f: impl Future<Output = T>) -> impl Future<Output = T> {
+ // Lint
+ async { f.await }
+}
+
+fn capture_func_result<FN, F, T>(f: FN) -> impl Future<Output = T>
+where
+ F: Future<Output = T>,
+ FN: FnOnce() -> F,
+{
+ // Do not lint, as f() would be evaluated prematurely
+ async { f().await }
+}
+
+fn double_future(f: impl Future<Output = impl Future<Output = u32>>) -> impl Future<Output = u32> {
+ // Do not lint, we will get a `.await` outside a `.async`
+ async { f.await.await }
+}
+
+fn await_in_async<F, R>(f: F) -> impl Future<Output = u32>
+where
+ F: FnOnce() -> R,
+ R: Future<Output = u32>,
+{
+ // Lint
+ async { async { f().await + 1 }.await }
+}
+
+#[derive(Debug, Clone)]
+struct F {}
+
+impl F {
+ async fn run(&self) {}
+}
+
+pub async fn run() {
+ let f = F {};
+ let c = f.clone();
+ // Do not lint: `c` would not live long enough
+ spawn(async move { c.run().await });
+ let _f = f;
+}
+
+fn spawn<F: Future + 'static>(_: F) {}
+
+async fn work(_: &str) {}
+
+fn capture() {
+ let val = "Hello World".to_owned();
+ // Do not lint: `val` would not live long enough
+ spawn(async { work(&{ val }).await });
+}
+
+fn await_from_macro() -> impl Future<Output = u32> {
+ macro_rules! mac {
+ ($e:expr) => {
+ $e.await
+ };
+ }
+ // Do not lint: the macro may change in the future
+ // or return different things depending on its argument
+ async { mac!(async { 42 }) }
+}
+
+fn async_expr_from_macro() -> impl Future<Output = u32> {
+ macro_rules! mac {
+ () => {
+ async { 42 }
+ };
+ }
+ // Do not lint: the macro may change in the future
+ async { mac!().await }
+}
+
+fn async_expr_from_macro_deep() -> impl Future<Output = u32> {
+ macro_rules! mac {
+ () => {
+ async { 42 }
+ };
+ }
+ // Do not lint: the macro may change in the future
+ async { ({ mac!() }).await }
+}
+
+fn all_from_macro() -> impl Future<Output = u32> {
+ macro_rules! mac {
+ () => {
+ // Lint
+ async { async { 42 }.await }
+ };
+ }
+ mac!()
+}
+
+fn parts_from_macro() -> impl Future<Output = u32> {
+ macro_rules! mac {
+ ($e: expr) => {
+ // Do not lint: `$e` might not always be side-effect free
+ async { $e.await }
+ };
+ }
+ mac!(async { 42 })
+}
+
+fn safe_parts_from_macro() -> impl Future<Output = u32> {
+ macro_rules! mac {
+ ($e: expr) => {
+ // Lint
+ async { async { $e }.await }
+ };
+ }
+ mac!(42)
+}
+
+fn parts_from_macro_deep() -> impl Future<Output = u32> {
+ macro_rules! mac {
+ ($e: expr) => {
+ // Do not lint: `$e` might not always be side-effect free
+ async { ($e,).0.await }
+ };
+ }
+ let f = std::future::ready(42);
+ mac!(f)
+}
+
+fn await_from_macro_deep() -> impl Future<Output = u32> {
+ macro_rules! mac {
+ ($e:expr) => {{ $e }.await};
+ }
+ // Do not lint: the macro may change in the future
+ // or return different things depending on its argument
+ async { mac!(async { 42 }) }
+}
diff --git a/src/tools/clippy/tests/ui/redundant_async_block.stderr b/src/tools/clippy/tests/ui/redundant_async_block.stderr
new file mode 100644
index 000000000..f3dcb09b4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_async_block.stderr
@@ -0,0 +1,74 @@
+error: this async expression only awaits a single future
+ --> $DIR/redundant_async_block.rs:15:13
+ |
+LL | let x = async { f.await };
+ | ^^^^^^^^^^^^^^^^^ help: you can reduce it to: `f`
+ |
+ = note: `-D clippy::redundant-async-block` implied by `-D warnings`
+
+error: this async expression only awaits a single future
+ --> $DIR/redundant_async_block.rs:22:16
+ |
+LL | let fut2 = async { fut1.await };
+ | ^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1`
+
+error: this async expression only awaits a single future
+ --> $DIR/redundant_async_block.rs:26:16
+ |
+LL | let fut2 = async move { fut1.await };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1`
+
+error: this async expression only awaits a single future
+ --> $DIR/redundant_async_block.rs:29:15
+ |
+LL | let fut = async { async { 42 }.await };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `async { 42 }`
+
+error: this async expression only awaits a single future
+ --> $DIR/redundant_async_block.rs:45:5
+ |
+LL | async move { fut.await }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut`
+
+error: this async expression only awaits a single future
+ --> $DIR/redundant_async_block.rs:58:5
+ |
+LL | async move { fut.await }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut`
+
+error: this async expression only awaits a single future
+ --> $DIR/redundant_async_block.rs:63:5
+ |
+LL | async { f.await }
+ | ^^^^^^^^^^^^^^^^^ help: you can reduce it to: `f`
+
+error: this async expression only awaits a single future
+ --> $DIR/redundant_async_block.rs:86:5
+ |
+LL | async { async { f().await + 1 }.await }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `async { f().await + 1 }`
+
+error: this async expression only awaits a single future
+ --> $DIR/redundant_async_block.rs:149:13
+ |
+LL | async { async { 42 }.await }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `async { 42 }`
+...
+LL | mac!()
+ | ------ in this macro invocation
+ |
+ = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this async expression only awaits a single future
+ --> $DIR/redundant_async_block.rs:169:13
+ |
+LL | async { async { $e }.await }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `async { $e }`
+...
+LL | mac!(42)
+ | -------- in this macro invocation
+ |
+ = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
index c0e49ff4c..b987fd2ce 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
@@ -2,6 +2,7 @@
#![feature(async_closure)]
#![warn(clippy::redundant_closure_call)]
+#![allow(clippy::redundant_async_block)]
#![allow(unused)]
async fn something() -> u32 {
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
index 9e6e54348..633a2979d 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
@@ -2,6 +2,7 @@
#![feature(async_closure)]
#![warn(clippy::redundant_closure_call)]
+#![allow(clippy::redundant_async_block)]
#![allow(unused)]
async fn something() -> u32 {
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
index d71bcba2a..8a1f07716 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
@@ -1,5 +1,5 @@
error: try not to call a closure in the expression where it is declared
- --> $DIR/redundant_closure_call_fixable.rs:16:13
+ --> $DIR/redundant_closure_call_fixable.rs:17:13
|
LL | let a = (|| 42)();
| ^^^^^^^^^ help: try doing something like: `42`
@@ -7,7 +7,7 @@ LL | let a = (|| 42)();
= note: `-D clippy::redundant-closure-call` implied by `-D warnings`
error: try not to call a closure in the expression where it is declared
- --> $DIR/redundant_closure_call_fixable.rs:17:13
+ --> $DIR/redundant_closure_call_fixable.rs:18:13
|
LL | let b = (async || {
| _____________^
@@ -27,7 +27,7 @@ LL ~ };
|
error: try not to call a closure in the expression where it is declared
- --> $DIR/redundant_closure_call_fixable.rs:22:13
+ --> $DIR/redundant_closure_call_fixable.rs:23:13
|
LL | let c = (|| {
| _____________^
@@ -47,13 +47,13 @@ LL ~ };
|
error: try not to call a closure in the expression where it is declared
- --> $DIR/redundant_closure_call_fixable.rs:27:13
+ --> $DIR/redundant_closure_call_fixable.rs:28:13
|
LL | let d = (async || something().await)();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { something().await }`
error: try not to call a closure in the expression where it is declared
- --> $DIR/redundant_closure_call_fixable.rs:36:13
+ --> $DIR/redundant_closure_call_fixable.rs:37:13
|
LL | (|| m!())()
| ^^^^^^^^^^^ help: try doing something like: `m!()`
@@ -64,7 +64,7 @@ LL | m2!();
= note: this error originates in the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info)
error: try not to call a closure in the expression where it is declared
- --> $DIR/redundant_closure_call_fixable.rs:31:13
+ --> $DIR/redundant_closure_call_fixable.rs:32:13
|
LL | (|| 0)()
| ^^^^^^^^ help: try doing something like: `0`
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed
index b88c5d0be..42348df44 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed
@@ -69,8 +69,8 @@ fn issue5504() {
}
fn try_result_opt() -> Result<i32, i32> {
- while (r#try!(result_opt())).is_some() {}
- if (r#try!(result_opt())).is_some() {}
+ while r#try!(result_opt()).is_some() {}
+ if r#try!(result_opt()).is_some() {}
Ok(42)
}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr
index e6afe9eb7..d6a46babb 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr
@@ -88,13 +88,13 @@ error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_result.rs:84:19
|
LL | while let Some(_) = r#try!(result_opt()) {}
- | ----------^^^^^^^----------------------- help: try this: `while (r#try!(result_opt())).is_some()`
+ | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_result.rs:85:16
|
LL | if let Some(_) = r#try!(result_opt()) {}
- | -------^^^^^^^----------------------- help: try this: `if (r#try!(result_opt())).is_some()`
+ | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_result.rs:91:12
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.fixed b/src/tools/clippy/tests/ui/single_component_path_imports.fixed
index 4c40739d6..8c96c4715 100644
--- a/src/tools/clippy/tests/ui/single_component_path_imports.fixed
+++ b/src/tools/clippy/tests/ui/single_component_path_imports.fixed
@@ -2,9 +2,11 @@
#![warn(clippy::single_component_path_imports)]
#![allow(unused_imports)]
+use core;
use serde as edres;
pub use serde;
+use std;
macro_rules! m {
() => {
@@ -17,6 +19,10 @@ fn main() {
// False positive #5154, shouldn't trigger lint.
m!();
+
+ // False positive #10549
+ let _ = self::std::io::stdout();
+ let _ = 0 as self::core::ffi::c_uint;
}
mod hello_mod {
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.rs b/src/tools/clippy/tests/ui/single_component_path_imports.rs
index 9280bab3c..8434bf7ea 100644
--- a/src/tools/clippy/tests/ui/single_component_path_imports.rs
+++ b/src/tools/clippy/tests/ui/single_component_path_imports.rs
@@ -2,9 +2,11 @@
#![warn(clippy::single_component_path_imports)]
#![allow(unused_imports)]
+use core;
use regex;
use serde as edres;
pub use serde;
+use std;
macro_rules! m {
() => {
@@ -17,6 +19,10 @@ fn main() {
// False positive #5154, shouldn't trigger lint.
m!();
+
+ // False positive #10549
+ let _ = self::std::io::stdout();
+ let _ = 0 as self::core::ffi::c_uint;
}
mod hello_mod {
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.stderr b/src/tools/clippy/tests/ui/single_component_path_imports.stderr
index 71dcc25d6..d69a86470 100644
--- a/src/tools/clippy/tests/ui/single_component_path_imports.stderr
+++ b/src/tools/clippy/tests/ui/single_component_path_imports.stderr
@@ -1,5 +1,5 @@
error: this import is redundant
- --> $DIR/single_component_path_imports.rs:5:1
+ --> $DIR/single_component_path_imports.rs:6:1
|
LL | use regex;
| ^^^^^^^^^^ help: remove it entirely
@@ -7,7 +7,7 @@ LL | use regex;
= note: `-D clippy::single-component-path-imports` implied by `-D warnings`
error: this import is redundant
- --> $DIR/single_component_path_imports.rs:23:5
+ --> $DIR/single_component_path_imports.rs:29:5
|
LL | use regex;
| ^^^^^^^^^^ help: remove it entirely
diff --git a/src/tools/clippy/tests/ui/single_match_else.rs b/src/tools/clippy/tests/ui/single_match_else.rs
index 5d03f77e9..3c86f41f3 100644
--- a/src/tools/clippy/tests/ui/single_match_else.rs
+++ b/src/tools/clippy/tests/ui/single_match_else.rs
@@ -1,9 +1,9 @@
-// aux-build: proc_macro_with_span.rs
+// aux-build: proc_macros.rs
#![warn(clippy::single_match_else)]
#![allow(clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)]
-extern crate proc_macro_with_span;
-use proc_macro_with_span::with_span;
+extern crate proc_macros;
+use proc_macros::with_span;
enum ExprNode {
ExprAddrOf,
diff --git a/src/tools/clippy/tests/ui/string_add.rs b/src/tools/clippy/tests/ui/string_add.rs
index 16673c01e..20edbe31f 100644
--- a/src/tools/clippy/tests/ui/string_add.rs
+++ b/src/tools/clippy/tests/ui/string_add.rs
@@ -1,7 +1,7 @@
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::external;
#[warn(clippy::string_add)]
#[allow(clippy::string_add_assign, unused)]
@@ -22,5 +22,8 @@ fn main() {
x = x + 1;
assert_eq!(2, x);
- string_add!();
+ external!({
+ let y = "".to_owned();
+ let z = y + "...";
+ });
}
diff --git a/src/tools/clippy/tests/ui/suspicious_doc_comments.fixed b/src/tools/clippy/tests/ui/suspicious_doc_comments.fixed
new file mode 100644
index 000000000..b404df94d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_doc_comments.fixed
@@ -0,0 +1,81 @@
+// run-rustfix
+#![allow(unused)]
+#![warn(clippy::suspicious_doc_comments)]
+
+//! Real module documentation.
+//! Fake module documentation.
+fn baz() {}
+
+pub mod singleline_outer_doc {
+ //! This module contains useful functions.
+
+ pub fn bar() {}
+}
+
+pub mod singleline_inner_doc {
+ //! This module contains useful functions.
+
+ pub fn bar() {}
+}
+
+pub mod multiline_outer_doc {
+ /*! This module contains useful functions.
+ */
+
+ pub fn bar() {}
+}
+
+pub mod multiline_inner_doc {
+ /*! This module contains useful functions.
+ */
+
+ pub fn bar() {}
+}
+
+pub mod multiline_outer_doc2 {
+ //! This module
+ //! contains
+ //! useful functions.
+
+ pub fn bar() {}
+}
+
+pub mod multiline_outer_doc3 {
+ //! a
+ //! b
+
+ /// c
+ pub fn bar() {}
+}
+
+pub mod multiline_outer_doc4 {
+ //! a
+ /// b
+ pub fn bar() {}
+}
+
+pub mod multiline_outer_doc_gap {
+ //! a
+
+ //! b
+ pub fn bar() {}
+}
+
+pub mod multiline_outer_doc_commented {
+ /////! This outer doc comment was commented out.
+ pub fn bar() {}
+}
+
+pub mod outer_doc_macro {
+ //! Very cool macro
+ macro_rules! x {
+ () => {};
+ }
+}
+
+pub mod useless_outer_doc {
+ //! Huh.
+ use std::mem;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/suspicious_doc_comments.rs b/src/tools/clippy/tests/ui/suspicious_doc_comments.rs
new file mode 100644
index 000000000..46eff51e2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_doc_comments.rs
@@ -0,0 +1,81 @@
+// run-rustfix
+#![allow(unused)]
+#![warn(clippy::suspicious_doc_comments)]
+
+//! Real module documentation.
+///! Fake module documentation.
+fn baz() {}
+
+pub mod singleline_outer_doc {
+ ///! This module contains useful functions.
+
+ pub fn bar() {}
+}
+
+pub mod singleline_inner_doc {
+ //! This module contains useful functions.
+
+ pub fn bar() {}
+}
+
+pub mod multiline_outer_doc {
+ /**! This module contains useful functions.
+ */
+
+ pub fn bar() {}
+}
+
+pub mod multiline_inner_doc {
+ /*! This module contains useful functions.
+ */
+
+ pub fn bar() {}
+}
+
+pub mod multiline_outer_doc2 {
+ ///! This module
+ ///! contains
+ ///! useful functions.
+
+ pub fn bar() {}
+}
+
+pub mod multiline_outer_doc3 {
+ ///! a
+ ///! b
+
+ /// c
+ pub fn bar() {}
+}
+
+pub mod multiline_outer_doc4 {
+ ///! a
+ /// b
+ pub fn bar() {}
+}
+
+pub mod multiline_outer_doc_gap {
+ ///! a
+
+ ///! b
+ pub fn bar() {}
+}
+
+pub mod multiline_outer_doc_commented {
+ /////! This outer doc comment was commented out.
+ pub fn bar() {}
+}
+
+pub mod outer_doc_macro {
+ ///! Very cool macro
+ macro_rules! x {
+ () => {};
+ }
+}
+
+pub mod useless_outer_doc {
+ ///! Huh.
+ use std::mem;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/suspicious_doc_comments.stderr b/src/tools/clippy/tests/ui/suspicious_doc_comments.stderr
new file mode 100644
index 000000000..6c167df27
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_doc_comments.stderr
@@ -0,0 +1,114 @@
+error: this is an outer doc comment and does not apply to the parent module or crate
+ --> $DIR/suspicious_doc_comments.rs:6:1
+ |
+LL | ///! Fake module documentation.
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::suspicious-doc-comments` implied by `-D warnings`
+help: use an inner doc comment to document the parent module or crate
+ |
+LL | //! Fake module documentation.
+ |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+ --> $DIR/suspicious_doc_comments.rs:10:5
+ |
+LL | ///! This module contains useful functions.
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: use an inner doc comment to document the parent module or crate
+ |
+LL | //! This module contains useful functions.
+ |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+ --> $DIR/suspicious_doc_comments.rs:22:5
+ |
+LL | / /**! This module contains useful functions.
+LL | | */
+ | |_______^
+ |
+help: use an inner doc comment to document the parent module or crate
+ |
+LL ~ /*! This module contains useful functions.
+LL + */
+ |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+ --> $DIR/suspicious_doc_comments.rs:36:5
+ |
+LL | / ///! This module
+LL | | ///! contains
+LL | | ///! useful functions.
+ | |__________________________^
+ |
+help: use an inner doc comment to document the parent module or crate
+ |
+LL ~ //! This module
+LL ~ //! contains
+LL ~ //! useful functions.
+ |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+ --> $DIR/suspicious_doc_comments.rs:44:5
+ |
+LL | / ///! a
+LL | | ///! b
+ | |__________^
+ |
+help: use an inner doc comment to document the parent module or crate
+ |
+LL ~ //! a
+LL ~ //! b
+ |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+ --> $DIR/suspicious_doc_comments.rs:52:5
+ |
+LL | ///! a
+ | ^^^^^^
+ |
+help: use an inner doc comment to document the parent module or crate
+ |
+LL | //! a
+ |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+ --> $DIR/suspicious_doc_comments.rs:58:5
+ |
+LL | / ///! a
+LL | |
+LL | | ///! b
+ | |__________^
+ |
+help: use an inner doc comment to document the parent module or crate
+ |
+LL ~ //! a
+LL |
+LL ~ //! b
+ |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+ --> $DIR/suspicious_doc_comments.rs:70:5
+ |
+LL | ///! Very cool macro
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+help: use an inner doc comment to document the parent module or crate
+ |
+LL | //! Very cool macro
+ |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+ --> $DIR/suspicious_doc_comments.rs:77:5
+ |
+LL | ///! Huh.
+ | ^^^^^^^^^
+ |
+help: use an inner doc comment to document the parent module or crate
+ |
+LL | //! Huh.
+ |
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/suspicious_doc_comments_unfixable.rs b/src/tools/clippy/tests/ui/suspicious_doc_comments_unfixable.rs
new file mode 100644
index 000000000..ad98c7f49
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_doc_comments_unfixable.rs
@@ -0,0 +1,16 @@
+#![allow(unused)]
+#![warn(clippy::suspicious_doc_comments)]
+
+///! a
+///! b
+/// c
+///! d
+pub fn foo() {}
+
+///! a
+///! b
+/// c
+///! d
+use std::mem;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/suspicious_doc_comments_unfixable.stderr b/src/tools/clippy/tests/ui/suspicious_doc_comments_unfixable.stderr
new file mode 100644
index 000000000..f89146dad
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_doc_comments_unfixable.stderr
@@ -0,0 +1,37 @@
+error: this is an outer doc comment and does not apply to the parent module or crate
+ --> $DIR/suspicious_doc_comments_unfixable.rs:4:1
+ |
+LL | / ///! a
+LL | | ///! b
+LL | | /// c
+LL | | ///! d
+ | |______^
+ |
+ = note: `-D clippy::suspicious-doc-comments` implied by `-D warnings`
+help: use an inner doc comment to document the parent module or crate
+ |
+LL + //! a
+LL + //! b
+LL | /// c
+LL + //! d
+ |
+
+error: this is an outer doc comment and does not apply to the parent module or crate
+ --> $DIR/suspicious_doc_comments_unfixable.rs:10:1
+ |
+LL | / ///! a
+LL | | ///! b
+LL | | /// c
+LL | | ///! d
+ | |______^
+ |
+help: use an inner doc comment to document the parent module or crate
+ |
+LL + //! a
+LL + //! b
+LL | /// c
+LL + //! d
+ |
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/swap.fixed b/src/tools/clippy/tests/ui/swap.fixed
index fa89706a8..9703674d1 100644
--- a/src/tools/clippy/tests/ui/swap.fixed
+++ b/src/tools/clippy/tests/ui/swap.fixed
@@ -1,4 +1,5 @@
// run-rustfix
+// aux-build: macro_rules.rs
#![warn(clippy::all)]
#![allow(
@@ -8,7 +9,8 @@
redundant_semicolons,
dead_code,
unused_assignments,
- unused_variables
+ unused_variables,
+ clippy::let_and_return
)]
struct Foo(u32);
@@ -65,19 +67,19 @@ fn xor_swap_locals() {
// This is an xor-based swap of local variables.
let mut a = 0;
let mut b = 1;
- std::mem::swap(&mut a, &mut b)
+ std::mem::swap(&mut a, &mut b);
}
fn xor_field_swap() {
// This is an xor-based swap of fields in a struct.
let mut bar = Bar { a: 0, b: 1 };
- std::mem::swap(&mut bar.a, &mut bar.b)
+ std::mem::swap(&mut bar.a, &mut bar.b);
}
fn xor_slice_swap() {
// This is an xor-based swap of a slice
let foo = &mut [1, 2];
- foo.swap(0, 1)
+ foo.swap(0, 1);
}
fn xor_no_swap() {
@@ -186,3 +188,14 @@ const fn issue_9864(mut u: u32) -> u32 {
v = temp;
u + v
}
+
+#[macro_use]
+extern crate macro_rules;
+
+const fn issue_10421(x: u32) -> u32 {
+ issue_10421!();
+ let a = x;
+ let a = a;
+ let a = a;
+ a
+}
diff --git a/src/tools/clippy/tests/ui/swap.rs b/src/tools/clippy/tests/ui/swap.rs
index ef8a81c83..a0228065e 100644
--- a/src/tools/clippy/tests/ui/swap.rs
+++ b/src/tools/clippy/tests/ui/swap.rs
@@ -1,4 +1,5 @@
// run-rustfix
+// aux-build: macro_rules.rs
#![warn(clippy::all)]
#![allow(
@@ -8,7 +9,8 @@
redundant_semicolons,
dead_code,
unused_assignments,
- unused_variables
+ unused_variables,
+ clippy::let_and_return
)]
struct Foo(u32);
@@ -215,3 +217,14 @@ const fn issue_9864(mut u: u32) -> u32 {
v = temp;
u + v
}
+
+#[macro_use]
+extern crate macro_rules;
+
+const fn issue_10421(x: u32) -> u32 {
+ issue_10421!();
+ let a = x;
+ let a = a;
+ let a = a;
+ a
+}
diff --git a/src/tools/clippy/tests/ui/swap.stderr b/src/tools/clippy/tests/ui/swap.stderr
index f0acbfe25..0c2462684 100644
--- a/src/tools/clippy/tests/ui/swap.stderr
+++ b/src/tools/clippy/tests/ui/swap.stderr
@@ -1,106 +1,106 @@
error: this looks like you are swapping `bar.a` and `bar.b` manually
- --> $DIR/swap.rs:25:5
+ --> $DIR/swap.rs:27:5
|
LL | / let temp = bar.a;
LL | | bar.a = bar.b;
LL | | bar.b = temp;
- | |________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b)`
+ | |_________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);`
|
= note: or maybe you should use `std::mem::replace`?
= note: `-D clippy::manual-swap` implied by `-D warnings`
error: this looks like you are swapping elements of `foo` manually
- --> $DIR/swap.rs:37:5
+ --> $DIR/swap.rs:39:5
|
LL | / let temp = foo[0];
LL | | foo[0] = foo[1];
LL | | foo[1] = temp;
- | |_________________^ help: try: `foo.swap(0, 1)`
+ | |__________________^ help: try: `foo.swap(0, 1);`
error: this looks like you are swapping elements of `foo` manually
- --> $DIR/swap.rs:46:5
+ --> $DIR/swap.rs:48:5
|
LL | / let temp = foo[0];
LL | | foo[0] = foo[1];
LL | | foo[1] = temp;
- | |_________________^ help: try: `foo.swap(0, 1)`
+ | |__________________^ help: try: `foo.swap(0, 1);`
error: this looks like you are swapping elements of `foo` manually
- --> $DIR/swap.rs:65:5
+ --> $DIR/swap.rs:67:5
|
LL | / let temp = foo[0];
LL | | foo[0] = foo[1];
LL | | foo[1] = temp;
- | |_________________^ help: try: `foo.swap(0, 1)`
+ | |__________________^ help: try: `foo.swap(0, 1);`
error: this looks like you are swapping `a` and `b` manually
- --> $DIR/swap.rs:76:5
+ --> $DIR/swap.rs:78:5
|
LL | / a ^= b;
LL | | b ^= a;
LL | | a ^= b;
- | |___________^ help: try: `std::mem::swap(&mut a, &mut b)`
+ | |___________^ help: try: `std::mem::swap(&mut a, &mut b);`
error: this looks like you are swapping `bar.a` and `bar.b` manually
- --> $DIR/swap.rs:84:5
+ --> $DIR/swap.rs:86:5
|
LL | / bar.a ^= bar.b;
LL | | bar.b ^= bar.a;
LL | | bar.a ^= bar.b;
- | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b)`
+ | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b);`
error: this looks like you are swapping elements of `foo` manually
- --> $DIR/swap.rs:92:5
+ --> $DIR/swap.rs:94:5
|
LL | / foo[0] ^= foo[1];
LL | | foo[1] ^= foo[0];
LL | | foo[0] ^= foo[1];
- | |_____________________^ help: try: `foo.swap(0, 1)`
+ | |_____________________^ help: try: `foo.swap(0, 1);`
error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually
- --> $DIR/swap.rs:121:5
+ --> $DIR/swap.rs:123:5
|
LL | / let temp = foo[0][1];
LL | | foo[0][1] = bar[1][0];
LL | | bar[1][0] = temp;
- | |____________________^ help: try: `std::mem::swap(&mut foo[0][1], &mut bar[1][0])`
+ | |_____________________^ help: try: `std::mem::swap(&mut foo[0][1], &mut bar[1][0]);`
|
= note: or maybe you should use `std::mem::replace`?
error: this looks like you are swapping `a` and `b` manually
- --> $DIR/swap.rs:135:7
+ --> $DIR/swap.rs:137:7
|
LL | ; let t = a;
| _______^
LL | | a = b;
LL | | b = t;
- | |_________^ help: try: `std::mem::swap(&mut a, &mut b)`
+ | |__________^ 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:144:7
+ --> $DIR/swap.rs:146:7
|
LL | ; let t = c.0;
| _______^
LL | | c.0 = a;
LL | | a = t;
- | |_________^ help: try: `std::mem::swap(&mut c.0, &mut a)`
+ | |__________^ 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 swapping `b` and `a` manually
- --> $DIR/swap.rs:170:5
+ --> $DIR/swap.rs:172:5
|
LL | / let t = b;
LL | | b = a;
LL | | a = t;
- | |_________^ help: try: `std::mem::swap(&mut b, &mut a)`
+ | |__________^ help: try: `std::mem::swap(&mut b, &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:132:5
+ --> $DIR/swap.rs:134:5
|
LL | / a = b;
LL | | b = a;
@@ -110,7 +110,7 @@ LL | | b = a;
= note: `-D clippy::almost-swapped` implied by `-D warnings`
error: this looks like you are trying to swap `c.0` and `a`
- --> $DIR/swap.rs:141:5
+ --> $DIR/swap.rs:143:5
|
LL | / c.0 = a;
LL | | a = c.0;
@@ -119,7 +119,7 @@ LL | | a = c.0;
= note: or maybe you should use `std::mem::replace`?
error: this looks like you are trying to swap `a` and `b`
- --> $DIR/swap.rs:148:5
+ --> $DIR/swap.rs:150:5
|
LL | / let a = b;
LL | | let b = a;
@@ -128,7 +128,7 @@ LL | | let b = a;
= note: or maybe you should use `std::mem::replace`?
error: this looks like you are trying to swap `d` and `c`
- --> $DIR/swap.rs:153:5
+ --> $DIR/swap.rs:155:5
|
LL | / d = c;
LL | | c = d;
@@ -137,7 +137,7 @@ LL | | c = d;
= note: or maybe you should use `std::mem::replace`?
error: this looks like you are trying to swap `a` and `b`
- --> $DIR/swap.rs:157:5
+ --> $DIR/swap.rs:159:5
|
LL | / let a = b;
LL | | b = a;
@@ -146,12 +146,12 @@ LL | | b = a;
= note: or maybe you should use `std::mem::replace`?
error: this looks like you are swapping `s.0.x` and `s.0.y` manually
- --> $DIR/swap.rs:205:5
+ --> $DIR/swap.rs:207:5
|
LL | / let t = s.0.x;
LL | | s.0.x = s.0.y;
LL | | s.0.y = t;
- | |_____________^ help: try: `std::mem::swap(&mut s.0.x, &mut s.0.y)`
+ | |______________^ help: try: `std::mem::swap(&mut s.0.x, &mut s.0.y);`
|
= note: or maybe you should use `std::mem::replace`?
diff --git a/src/tools/clippy/tests/ui/tests_outside_test_module.rs b/src/tools/clippy/tests/ui/tests_outside_test_module.rs
new file mode 100644
index 000000000..1982b1d01
--- /dev/null
+++ b/src/tools/clippy/tests/ui/tests_outside_test_module.rs
@@ -0,0 +1,18 @@
+// compile-flags: --test
+#![allow(unused)]
+#![warn(clippy::tests_outside_test_module)]
+
+fn main() {
+ // test code goes here
+}
+
+// Should lint
+#[test]
+fn my_test() {}
+
+#[cfg(test)]
+mod tests {
+ // Should not lint
+ #[test]
+ fn my_test() {}
+}
diff --git a/src/tools/clippy/tests/ui/tests_outside_test_module.stderr b/src/tools/clippy/tests/ui/tests_outside_test_module.stderr
new file mode 100644
index 000000000..125a79d6e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/tests_outside_test_module.stderr
@@ -0,0 +1,11 @@
+error: this function marked with #[test] is outside a #[cfg(test)] module
+ --> $DIR/tests_outside_test_module.rs:11:1
+ |
+LL | fn my_test() {}
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: move it to a testing module marked with #[cfg(test)]
+ = note: `-D clippy::tests-outside-test-module` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed b/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed
index 09fb66ca3..174c858a4 100644
--- a/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed
+++ b/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed
@@ -1,17 +1,12 @@
// run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::toplevel_ref_arg)]
-#![allow(clippy::uninlined_format_args)]
+#![allow(clippy::uninlined_format_args, unused)]
-#[macro_use]
-extern crate macro_rules;
-
-macro_rules! gen_binding {
- () => {
- let _y = &42;
- };
-}
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
+#[inline_macros]
fn main() {
// Closures should not warn
let y = |ref x| println!("{:?}", x);
@@ -38,13 +33,8 @@ fn main() {
for ref _x in 0..10 {}
// lint in macro
- #[allow(unused)]
- {
- gen_binding!();
- }
+ inline!(let _y = &42;);
// do not lint in external macro
- {
- ref_arg_binding!();
- }
+ external!(let ref _y = 42;);
}
diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg.rs b/src/tools/clippy/tests/ui/toplevel_ref_arg.rs
index 9d1f2f810..4b81a0611 100644
--- a/src/tools/clippy/tests/ui/toplevel_ref_arg.rs
+++ b/src/tools/clippy/tests/ui/toplevel_ref_arg.rs
@@ -1,17 +1,12 @@
// run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::toplevel_ref_arg)]
-#![allow(clippy::uninlined_format_args)]
+#![allow(clippy::uninlined_format_args, unused)]
-#[macro_use]
-extern crate macro_rules;
-
-macro_rules! gen_binding {
- () => {
- let ref _y = 42;
- };
-}
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
+#[inline_macros]
fn main() {
// Closures should not warn
let y = |ref x| println!("{:?}", x);
@@ -38,13 +33,8 @@ fn main() {
for ref _x in 0..10 {}
// lint in macro
- #[allow(unused)]
- {
- gen_binding!();
- }
+ inline!(let ref _y = 42;);
// do not lint in external macro
- {
- ref_arg_binding!();
- }
+ external!(let ref _y = 42;);
}
diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg.stderr b/src/tools/clippy/tests/ui/toplevel_ref_arg.stderr
index 9c853020a..407c2d9fc 100644
--- a/src/tools/clippy/tests/ui/toplevel_ref_arg.stderr
+++ b/src/tools/clippy/tests/ui/toplevel_ref_arg.stderr
@@ -1,5 +1,5 @@
error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
- --> $DIR/toplevel_ref_arg.rs:20:9
+ --> $DIR/toplevel_ref_arg.rs:15:9
|
LL | let ref _x = 1;
| ----^^^^^^----- help: try: `let _x = &1;`
@@ -7,39 +7,36 @@ LL | let ref _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:22:9
+ --> $DIR/toplevel_ref_arg.rs:17: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:24:9
+ --> $DIR/toplevel_ref_arg.rs:19: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:26:9
+ --> $DIR/toplevel_ref_arg.rs:21: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:31:9
+ --> $DIR/toplevel_ref_arg.rs:26:9
|
LL | let ref _x = vec![1, 2, 3];
| ----^^^^^^----------------- help: try: `let _x = &vec![1, 2, 3];`
error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead
- --> $DIR/toplevel_ref_arg.rs:11:13
+ --> $DIR/toplevel_ref_arg.rs:36:17
|
-LL | let ref _y = 42;
- | ----^^^^^^------ help: try: `let _y = &42;`
-...
-LL | gen_binding!();
- | -------------- in this macro invocation
+LL | inline!(let ref _y = 42;);
+ | ----^^^^^^------ help: try: `let _y = &42;`
|
- = note: this error originates in the macro `gen_binding` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 6 previous errors
diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs
index 1a493fbce..2047593e7 100644
--- a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs
+++ b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs
@@ -1,33 +1,27 @@
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![warn(clippy::toplevel_ref_arg)]
#![allow(unused)]
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
fn the_answer(ref mut x: u8) {
*x = 42;
}
-macro_rules! gen_function {
- () => {
- fn fun_example(ref _x: usize) {}
- };
-}
-
+#[inline_macros]
fn main() {
let mut x = 0;
the_answer(x);
// lint in macro
- #[allow(unused)]
- {
- gen_function!();
+ inline! {
+ fn fun_example(ref _x: usize) {}
}
// do not lint in external macro
- {
- ref_arg_function!();
+ external! {
+ fn fun_example2(ref _x: usize) {}
}
}
diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr
index e97011c7f..7307bd599 100644
--- a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr
+++ b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr
@@ -7,15 +7,12 @@ LL | fn the_answer(ref mut x: u8) {
= note: `-D clippy::toplevel-ref-arg` implied by `-D warnings`
error: `ref` directly on a function argument is ignored. Consider using a reference type instead
- --> $DIR/toplevel_ref_arg_non_rustfix.rs:15:24
+ --> $DIR/toplevel_ref_arg_non_rustfix.rs:20:24
|
LL | fn fun_example(ref _x: usize) {}
| ^^^^^^
-...
-LL | gen_function!();
- | --------------- in this macro invocation
|
- = note: this error originates in the macro `gen_function` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 2 previous errors
diff --git a/src/tools/clippy/tests/ui/trailing_empty_array.rs b/src/tools/clippy/tests/ui/trailing_empty_array.rs
index c39b0bcaf..8e3749eef 100644
--- a/src/tools/clippy/tests/ui/trailing_empty_array.rs
+++ b/src/tools/clippy/tests/ui/trailing_empty_array.rs
@@ -155,7 +155,6 @@ struct TupleStructReprC(i32, [usize; 0]);
type NamedTuple = (i32, [usize; 0]);
-#[rustfmt::skip] // [rustfmt#4995](https://github.com/rust-lang/rustfmt/issues/4995)
struct ConstParamZeroDefault<const N: usize = 0> {
field: i32,
last: [usize; N],
@@ -166,7 +165,6 @@ struct ConstParamNoDefault<const N: usize> {
last: [usize; N],
}
-#[rustfmt::skip]
struct ConstParamNonZeroDefault<const N: usize = 1> {
field: i32,
last: [usize; N],
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
index 55307506e..cc84ba25b 100644
--- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
@@ -4,7 +4,7 @@
// would otherwise be responsible for
#![warn(clippy::useless_transmute)]
#![warn(clippy::transmute_ptr_to_ptr)]
-#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)]
+#![allow(unused, clippy::borrow_as_ptr)]
use std::mem::{size_of, transmute};
@@ -77,3 +77,9 @@ fn cannot_be_expressed_as_pointer_cast(in_param: Single) -> Pair {
unsafe { transmute::<Single, Pair>(in_param) }
}
+
+fn issue_10449() {
+ fn f() {}
+
+ let _x: u8 = unsafe { *(f as *const u8) };
+}
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
index e7360f3f9..aa65ab4dd 100644
--- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
@@ -4,7 +4,7 @@
// would otherwise be responsible for
#![warn(clippy::useless_transmute)]
#![warn(clippy::transmute_ptr_to_ptr)]
-#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)]
+#![allow(unused, clippy::borrow_as_ptr)]
use std::mem::{size_of, transmute};
@@ -77,3 +77,9 @@ fn cannot_be_expressed_as_pointer_cast(in_param: Single) -> Pair {
unsafe { transmute::<Single, Pair>(in_param) }
}
+
+fn issue_10449() {
+ fn f() {}
+
+ let _x: u8 = unsafe { *std::mem::transmute::<fn(), *const u8>(f) };
+}
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
index e862fcb67..58f5162c7 100644
--- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
@@ -58,5 +58,11 @@ error: transmute from a reference to a pointer
LL | unsafe { transmute::<&[i32; 1], *const u8>(in_param) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8`
-error: aborting due to 9 previous errors
+error: transmute from `fn()` to `*const u8` which could be expressed as a pointer cast instead
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:84:28
+ |
+LL | let _x: u8 = unsafe { *std::mem::transmute::<fn(), *const u8>(f) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(f as *const u8)`
+
+error: aborting due to 10 previous errors
diff --git a/src/tools/clippy/tests/ui/try_err.fixed b/src/tools/clippy/tests/ui/try_err.fixed
index 264194419..dc497b169 100644
--- a/src/tools/clippy/tests/ui/try_err.fixed
+++ b/src/tools/clippy/tests/ui/try_err.fixed
@@ -1,11 +1,11 @@
// run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![deny(clippy::try_err)]
#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)]
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
use std::io;
use std::task::Poll;
@@ -79,36 +79,22 @@ fn nested_error() -> Result<i32, i32> {
Ok(1)
}
-// Bad suggestion when in macro (see #6242)
-macro_rules! try_validation {
- ($e: expr) => {{
- match $e {
+#[inline_macros]
+fn calling_macro() -> Result<i32, i32> {
+ // macro
+ inline!(
+ match $(Ok::<_, i32>(5)) {
Ok(_) => 0,
Err(_) => return Err(1),
}
- }};
-}
-
-macro_rules! ret_one {
- () => {
- 1
- };
-}
-
-macro_rules! try_validation_in_macro {
- ($e: expr) => {{
- match $e {
+ );
+ // `Err` arg is another macro
+ inline!(
+ match $(Ok::<_, i32>(5)) {
Ok(_) => 0,
- Err(_) => return Err(ret_one!()),
+ Err(_) => return Err(inline!(1)),
}
- }};
-}
-
-fn calling_macro() -> Result<i32, i32> {
- // macro
- try_validation!(Ok::<_, i32>(5));
- // `Err` arg is another macro
- try_validation_in_macro!(Ok::<_, i32>(5));
+ );
Ok(5)
}
@@ -121,24 +107,19 @@ fn main() {
calling_macro().unwrap();
// We don't want to lint in external macros
- try_err!();
-}
-
-macro_rules! bar {
- () => {
- String::from("aasdfasdfasdfa")
- };
-}
-
-macro_rules! foo {
- () => {
- bar!()
- };
+ external! {
+ pub fn try_err_fn() -> Result<i32, i32> {
+ let err: i32 = 1;
+ // To avoid warnings during rustfix
+ if true { Err(err)? } else { Ok(2) }
+ }
+ }
}
+#[inline_macros]
pub fn macro_inside(fail: bool) -> Result<i32, String> {
if fail {
- return Err(foo!());
+ return Err(inline!(inline!(String::from("aasdfasdfasdfa"))));
}
Ok(0)
}
diff --git a/src/tools/clippy/tests/ui/try_err.rs b/src/tools/clippy/tests/ui/try_err.rs
index bc6979bf4..86aeb75cd 100644
--- a/src/tools/clippy/tests/ui/try_err.rs
+++ b/src/tools/clippy/tests/ui/try_err.rs
@@ -1,11 +1,11 @@
// run-rustfix
-// aux-build:macro_rules.rs
+// aux-build:proc_macros.rs
#![deny(clippy::try_err)]
#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)]
-#[macro_use]
-extern crate macro_rules;
+extern crate proc_macros;
+use proc_macros::{external, inline_macros};
use std::io;
use std::task::Poll;
@@ -79,36 +79,22 @@ fn nested_error() -> Result<i32, i32> {
Ok(1)
}
-// Bad suggestion when in macro (see #6242)
-macro_rules! try_validation {
- ($e: expr) => {{
- match $e {
+#[inline_macros]
+fn calling_macro() -> Result<i32, i32> {
+ // macro
+ inline!(
+ match $(Ok::<_, i32>(5)) {
Ok(_) => 0,
Err(_) => Err(1)?,
}
- }};
-}
-
-macro_rules! ret_one {
- () => {
- 1
- };
-}
-
-macro_rules! try_validation_in_macro {
- ($e: expr) => {{
- match $e {
+ );
+ // `Err` arg is another macro
+ inline!(
+ match $(Ok::<_, i32>(5)) {
Ok(_) => 0,
- Err(_) => Err(ret_one!())?,
+ Err(_) => Err(inline!(1))?,
}
- }};
-}
-
-fn calling_macro() -> Result<i32, i32> {
- // macro
- try_validation!(Ok::<_, i32>(5));
- // `Err` arg is another macro
- try_validation_in_macro!(Ok::<_, i32>(5));
+ );
Ok(5)
}
@@ -121,24 +107,19 @@ fn main() {
calling_macro().unwrap();
// We don't want to lint in external macros
- try_err!();
-}
-
-macro_rules! bar {
- () => {
- String::from("aasdfasdfasdfa")
- };
-}
-
-macro_rules! foo {
- () => {
- bar!()
- };
+ external! {
+ pub fn try_err_fn() -> Result<i32, i32> {
+ let err: i32 = 1;
+ // To avoid warnings during rustfix
+ if true { Err(err)? } else { Ok(2) }
+ }
+ }
}
+#[inline_macros]
pub fn macro_inside(fail: bool) -> Result<i32, String> {
if fail {
- Err(foo!())?;
+ Err(inline!(inline!(String::from("aasdfasdfasdfa"))))?;
}
Ok(0)
}
diff --git a/src/tools/clippy/tests/ui/try_err.stderr b/src/tools/clippy/tests/ui/try_err.stderr
index 0cb1328fb..4ad0e2e56 100644
--- a/src/tools/clippy/tests/ui/try_err.stderr
+++ b/src/tools/clippy/tests/ui/try_err.stderr
@@ -29,53 +29,47 @@ LL | Err(err)?;
| ^^^^^^^^^ help: try this: `return Err(err.into())`
error: returning an `Err(_)` with the `?` operator
- --> $DIR/try_err.rs:87:23
+ --> $DIR/try_err.rs:88:23
|
LL | Err(_) => Err(1)?,
| ^^^^^^^ help: try this: `return Err(1)`
-...
-LL | try_validation!(Ok::<_, i32>(5));
- | -------------------------------- in this macro invocation
|
- = note: this error originates in the macro `try_validation` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_fn_calling_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
error: returning an `Err(_)` with the `?` operator
- --> $DIR/try_err.rs:102:23
+ --> $DIR/try_err.rs:95:23
|
-LL | Err(_) => Err(ret_one!())?,
- | ^^^^^^^^^^^^^^^^ help: try this: `return Err(ret_one!())`
-...
-LL | try_validation_in_macro!(Ok::<_, i32>(5));
- | ----------------------------------------- in this macro invocation
+LL | Err(_) => Err(inline!(1))?,
+ | ^^^^^^^^^^^^^^^^ help: try this: `return Err(inline!(1))`
|
- = note: this error originates in the macro `try_validation_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `__inline_mac_fn_calling_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
error: returning an `Err(_)` with the `?` operator
- --> $DIR/try_err.rs:141:9
+ --> $DIR/try_err.rs:122:9
|
-LL | Err(foo!())?;
- | ^^^^^^^^^^^^ help: try this: `return Err(foo!())`
+LL | Err(inline!(inline!(String::from("aasdfasdfasdfa"))))?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Err(inline!(inline!(String::from("aasdfasdfasdfa"))))`
error: returning an `Err(_)` with the `?` operator
- --> $DIR/try_err.rs:148:9
+ --> $DIR/try_err.rs:129:9
|
LL | Err(io::ErrorKind::WriteZero)?
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))`
error: returning an `Err(_)` with the `?` operator
- --> $DIR/try_err.rs:150:9
+ --> $DIR/try_err.rs:131:9
|
LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))?
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))`
error: returning an `Err(_)` with the `?` operator
- --> $DIR/try_err.rs:158:9
+ --> $DIR/try_err.rs:139:9
|
LL | Err(io::ErrorKind::NotFound)?
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))`
error: returning an `Err(_)` with the `?` operator
- --> $DIR/try_err.rs:167:16
+ --> $DIR/try_err.rs:148:16
|
LL | return Err(42)?;
| ^^^^^^^^ help: try this: `Err(42)`
diff --git a/src/tools/clippy/tests/ui/uninit.rs b/src/tools/clippy/tests/ui/uninit.rs
index 211317317..c996de894 100644
--- a/src/tools/clippy/tests/ui/uninit.rs
+++ b/src/tools/clippy/tests/ui/uninit.rs
@@ -1,15 +1,17 @@
#![feature(stmt_expr_attributes)]
#![allow(clippy::let_unit_value, invalid_value)]
-use std::mem::{self, MaybeUninit};
+use std::mem::MaybeUninit;
+
+union MyOwnMaybeUninit {
+ value: u8,
+ uninit: (),
+}
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
+ // This is OK, because ZSTs do not contain data.
let _: () = unsafe { MaybeUninit::uninit().assume_init() };
// This is OK, because `MaybeUninit` allows uninitialized data.
@@ -21,6 +23,31 @@ fn main() {
// This is OK, because all constitutent types are uninit-compatible.
let _: (MaybeUninit<usize>, [MaybeUninit<bool>; 2]) = unsafe { MaybeUninit::uninit().assume_init() };
+ // This is OK, because our own MaybeUninit is just as fine as the one from core.
+ let _: MyOwnMaybeUninit = unsafe { MaybeUninit::uninit().assume_init() };
+
+ // This is OK, because empty arrays don't contain data.
+ let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() };
+
// Was a false negative.
- let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() };
+ let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
+
+ polymorphic::<()>();
+ polymorphic_maybe_uninit_array::<10>();
+ polymorphic_maybe_uninit::<u8>();
+
+ fn polymorphic<T>() {
+ // We are conservative around polymorphic types.
+ let _: T = unsafe { MaybeUninit::uninit().assume_init() };
+ }
+
+ fn polymorphic_maybe_uninit_array<const N: usize>() {
+ // While the type is polymorphic, MaybeUninit<u8> is not.
+ let _: [MaybeUninit<u8>; N] = unsafe { MaybeUninit::uninit().assume_init() };
+ }
+
+ fn polymorphic_maybe_uninit<T>() {
+ // The entire type is polymorphic, but it's wrapped in a MaybeUninit.
+ let _: MaybeUninit<T> = unsafe { MaybeUninit::uninit().assume_init() };
+ }
}
diff --git a/src/tools/clippy/tests/ui/uninit.stderr b/src/tools/clippy/tests/ui/uninit.stderr
index 15ef23494..248de56da 100644
--- a/src/tools/clippy/tests/ui/uninit.stderr
+++ b/src/tools/clippy/tests/ui/uninit.stderr
@@ -1,5 +1,5 @@
error: this call for this type may be undefined behavior
- --> $DIR/uninit.rs:7:29
+ --> $DIR/uninit.rs:12:29
|
LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,16 +7,16 @@ 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:10:31
+ --> $DIR/uninit.rs:33:29
|
-LL | let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this call for this type may be undefined behavior
- --> $DIR/uninit.rs:25:29
+ --> $DIR/uninit.rs:41:29
|
-LL | let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let _: T = unsafe { MaybeUninit::uninit().assume_init() };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
diff --git a/src/tools/clippy/tests/ui/uninit_vec.rs b/src/tools/clippy/tests/ui/uninit_vec.rs
index 194e4fc15..79effc82f 100644
--- a/src/tools/clippy/tests/ui/uninit_vec.rs
+++ b/src/tools/clippy/tests/ui/uninit_vec.rs
@@ -7,6 +7,11 @@ struct MyVec {
vec: Vec<u8>,
}
+union MyOwnMaybeUninit {
+ value: u8,
+ uninit: (),
+}
+
fn main() {
// with_capacity() -> set_len() should be detected
let mut vec: Vec<u8> = Vec::with_capacity(1000);
@@ -97,4 +102,34 @@ fn main() {
unsafe {
vec.set_len(0);
}
+
+ // ZSTs should not be detected
+ let mut vec: Vec<()> = Vec::with_capacity(1000);
+ unsafe {
+ vec.set_len(10);
+ }
+
+ // unions should not be detected
+ let mut vec: Vec<MyOwnMaybeUninit> = Vec::with_capacity(1000);
+ unsafe {
+ vec.set_len(10);
+ }
+
+ polymorphic::<()>();
+
+ fn polymorphic<T>() {
+ // We are conservative around polymorphic types.
+ let mut vec: Vec<T> = Vec::with_capacity(1000);
+ unsafe {
+ vec.set_len(10);
+ }
+ }
+
+ fn poly_maybe_uninit<T>() {
+ // We are conservative around polymorphic types.
+ let mut vec: Vec<MaybeUninit<T>> = Vec::with_capacity(1000);
+ unsafe {
+ vec.set_len(10);
+ }
+ }
}
diff --git a/src/tools/clippy/tests/ui/uninit_vec.stderr b/src/tools/clippy/tests/ui/uninit_vec.stderr
index 77fc689f0..9cdf0c95a 100644
--- a/src/tools/clippy/tests/ui/uninit_vec.stderr
+++ b/src/tools/clippy/tests/ui/uninit_vec.stderr
@@ -1,5 +1,5 @@
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
- --> $DIR/uninit_vec.rs:12:5
+ --> $DIR/uninit_vec.rs:17:5
|
LL | let mut vec: Vec<u8> = Vec::with_capacity(1000);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -11,7 +11,7 @@ LL | vec.set_len(200);
= note: `-D clippy::uninit-vec` implied by `-D warnings`
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
- --> $DIR/uninit_vec.rs:18:5
+ --> $DIR/uninit_vec.rs:23:5
|
LL | vec.reserve(1000);
| ^^^^^^^^^^^^^^^^^^
@@ -22,7 +22,7 @@ LL | vec.set_len(200);
= help: initialize the buffer or wrap the content in `MaybeUninit`
error: calling `set_len()` on empty `Vec` creates out-of-bound values
- --> $DIR/uninit_vec.rs:24:5
+ --> $DIR/uninit_vec.rs:29:5
|
LL | let mut vec: Vec<u8> = Vec::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -31,7 +31,7 @@ LL | vec.set_len(200);
| ^^^^^^^^^^^^^^^^
error: calling `set_len()` on empty `Vec` creates out-of-bound values
- --> $DIR/uninit_vec.rs:30:5
+ --> $DIR/uninit_vec.rs:35:5
|
LL | let mut vec: Vec<u8> = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -40,7 +40,7 @@ LL | vec.set_len(200);
| ^^^^^^^^^^^^^^^^
error: calling `set_len()` on empty `Vec` creates out-of-bound values
- --> $DIR/uninit_vec.rs:35:5
+ --> $DIR/uninit_vec.rs:40:5
|
LL | let mut vec: Vec<u8> = Vec::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -49,7 +49,7 @@ LL | vec.set_len(200);
| ^^^^^^^^^^^^^^^^
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
- --> $DIR/uninit_vec.rs:49:5
+ --> $DIR/uninit_vec.rs:54:5
|
LL | let mut vec: Vec<u8> = Vec::with_capacity(1000);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -60,7 +60,7 @@ LL | vec.set_len(200);
= help: initialize the buffer or wrap the content in `MaybeUninit`
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
- --> $DIR/uninit_vec.rs:58:5
+ --> $DIR/uninit_vec.rs:63:5
|
LL | my_vec.vec.reserve(1000);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -71,7 +71,7 @@ LL | my_vec.vec.set_len(200);
= help: initialize the buffer or wrap the content in `MaybeUninit`
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
- --> $DIR/uninit_vec.rs:63:5
+ --> $DIR/uninit_vec.rs:68:5
|
LL | my_vec.vec = Vec::with_capacity(1000);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -82,7 +82,7 @@ LL | my_vec.vec.set_len(200);
= help: initialize the buffer or wrap the content in `MaybeUninit`
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
- --> $DIR/uninit_vec.rs:42:9
+ --> $DIR/uninit_vec.rs:47:9
|
LL | let mut vec: Vec<u8> = Vec::with_capacity(1000);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -92,7 +92,7 @@ LL | vec.set_len(200);
= help: initialize the buffer or wrap the content in `MaybeUninit`
error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
- --> $DIR/uninit_vec.rs:45:9
+ --> $DIR/uninit_vec.rs:50:9
|
LL | vec.reserve(1000);
| ^^^^^^^^^^^^^^^^^^
@@ -101,5 +101,16 @@ LL | vec.set_len(200);
|
= help: initialize the buffer or wrap the content in `MaybeUninit`
-error: aborting due to 10 previous errors
+error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
+ --> $DIR/uninit_vec.rs:122:9
+ |
+LL | let mut vec: Vec<T> = Vec::with_capacity(1000);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | unsafe {
+LL | vec.set_len(10);
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: initialize the buffer or wrap the content in `MaybeUninit`
+
+error: aborting due to 11 previous errors
diff --git a/src/tools/clippy/tests/ui/uninlined_format_args.fixed b/src/tools/clippy/tests/ui/uninlined_format_args.fixed
index cbd5cc5fc..3122081a4 100644
--- a/src/tools/clippy/tests/ui/uninlined_format_args.fixed
+++ b/src/tools/clippy/tests/ui/uninlined_format_args.fixed
@@ -1,11 +1,11 @@
-// aux-build:proc_macro_with_span.rs
+// aux-build:proc_macros.rs
// run-rustfix
#![warn(clippy::uninlined_format_args)]
-#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)]
+#![allow(named_arguments_used_positionally, unused)]
#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)]
-extern crate proc_macro_with_span;
-use proc_macro_with_span::with_span;
+extern crate proc_macros;
+use proc_macros::with_span;
macro_rules! no_param_str {
() => {
@@ -119,7 +119,7 @@ fn tester(fn_arg: i32) {
println!("Width = {local_i32}, value with width = {local_f64:local_i32$}");
println!("{local_i32:width$.prec$}");
println!("{width:width$.prec$}");
- println!("{}", format!("{local_i32}"));
+ println!("{}", format!("{}", local_i32));
my_println!("{}", local_i32);
my_println_args!("{}", local_i32);
@@ -178,3 +178,87 @@ fn _meets_msrv() {
fn _do_not_fire() {
println!("{:?}", None::<()>);
}
+
+macro_rules! _internal {
+ ($($args:tt)*) => {
+ println!("{}", format_args!($($args)*))
+ };
+}
+
+macro_rules! my_println2 {
+ ($target:expr, $($args:tt)+) => {{
+ if $target {
+ _internal!($($args)+)
+ }
+ }};
+}
+
+macro_rules! my_println2_args {
+ ($target:expr, $($args:tt)+) => {{
+ if $target {
+ _internal!("foo: {}", format_args!($($args)+))
+ }
+ }};
+}
+
+macro_rules! my_concat {
+ ($fmt:literal $(, $e:expr)*) => {
+ println!(concat!("ERROR: ", $fmt), $($e,)*)
+ }
+}
+
+macro_rules! my_good_macro {
+ ($fmt:literal $(, $e:expr)* $(,)?) => {
+ println!($fmt $(, $e)*)
+ }
+}
+
+macro_rules! my_bad_macro {
+ ($fmt:literal, $($e:expr),*) => {
+ println!($fmt, $($e,)*)
+ }
+}
+
+macro_rules! my_bad_macro2 {
+ ($fmt:literal) => {
+ let s = $fmt.clone();
+ println!("{}", s);
+ };
+ ($fmt:literal, $($e:expr)+) => {
+ println!($fmt, $($e,)*)
+ };
+}
+
+// This abomination was suggested by @Alexendoo, may the Rust gods have mercy on their soul...
+// https://github.com/rust-lang/rust-clippy/pull/9948#issuecomment-1327965962
+macro_rules! used_twice {
+ (
+ large = $large:literal,
+ small = $small:literal,
+ $val:expr,
+ ) => {
+ if $val < 5 {
+ println!($small, $val);
+ } else {
+ println!($large, $val);
+ }
+ };
+}
+
+fn tester2() {
+ let local_i32 = 1;
+ my_println2_args!(true, "{}", local_i32);
+ my_println2!(true, "{}", local_i32);
+ my_concat!("{}", local_i32);
+ my_good_macro!("{}", local_i32);
+ my_good_macro!("{}", local_i32,);
+
+ // FIXME: Broken false positives, currently unhandled
+ my_bad_macro!("{}", local_i32);
+ my_bad_macro2!("{}", local_i32);
+ used_twice! {
+ large = "large value: {}",
+ small = "small value: {}",
+ local_i32,
+ };
+}
diff --git a/src/tools/clippy/tests/ui/uninlined_format_args.rs b/src/tools/clippy/tests/ui/uninlined_format_args.rs
index cf0ea5be4..b153ef256 100644
--- a/src/tools/clippy/tests/ui/uninlined_format_args.rs
+++ b/src/tools/clippy/tests/ui/uninlined_format_args.rs
@@ -1,11 +1,11 @@
-// aux-build:proc_macro_with_span.rs
+// aux-build:proc_macros.rs
// run-rustfix
#![warn(clippy::uninlined_format_args)]
-#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)]
+#![allow(named_arguments_used_positionally, unused)]
#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)]
-extern crate proc_macro_with_span;
-use proc_macro_with_span::with_span;
+extern crate proc_macros;
+use proc_macros::with_span;
macro_rules! no_param_str {
() => {
@@ -183,3 +183,87 @@ fn _meets_msrv() {
fn _do_not_fire() {
println!("{:?}", None::<()>);
}
+
+macro_rules! _internal {
+ ($($args:tt)*) => {
+ println!("{}", format_args!($($args)*))
+ };
+}
+
+macro_rules! my_println2 {
+ ($target:expr, $($args:tt)+) => {{
+ if $target {
+ _internal!($($args)+)
+ }
+ }};
+}
+
+macro_rules! my_println2_args {
+ ($target:expr, $($args:tt)+) => {{
+ if $target {
+ _internal!("foo: {}", format_args!($($args)+))
+ }
+ }};
+}
+
+macro_rules! my_concat {
+ ($fmt:literal $(, $e:expr)*) => {
+ println!(concat!("ERROR: ", $fmt), $($e,)*)
+ }
+}
+
+macro_rules! my_good_macro {
+ ($fmt:literal $(, $e:expr)* $(,)?) => {
+ println!($fmt $(, $e)*)
+ }
+}
+
+macro_rules! my_bad_macro {
+ ($fmt:literal, $($e:expr),*) => {
+ println!($fmt, $($e,)*)
+ }
+}
+
+macro_rules! my_bad_macro2 {
+ ($fmt:literal) => {
+ let s = $fmt.clone();
+ println!("{}", s);
+ };
+ ($fmt:literal, $($e:expr)+) => {
+ println!($fmt, $($e,)*)
+ };
+}
+
+// This abomination was suggested by @Alexendoo, may the Rust gods have mercy on their soul...
+// https://github.com/rust-lang/rust-clippy/pull/9948#issuecomment-1327965962
+macro_rules! used_twice {
+ (
+ large = $large:literal,
+ small = $small:literal,
+ $val:expr,
+ ) => {
+ if $val < 5 {
+ println!($small, $val);
+ } else {
+ println!($large, $val);
+ }
+ };
+}
+
+fn tester2() {
+ let local_i32 = 1;
+ my_println2_args!(true, "{}", local_i32);
+ my_println2!(true, "{}", local_i32);
+ my_concat!("{}", local_i32);
+ my_good_macro!("{}", local_i32);
+ my_good_macro!("{}", local_i32,);
+
+ // FIXME: Broken false positives, currently unhandled
+ my_bad_macro!("{}", local_i32);
+ my_bad_macro2!("{}", local_i32);
+ used_twice! {
+ large = "large value: {}",
+ small = "small value: {}",
+ local_i32,
+ };
+}
diff --git a/src/tools/clippy/tests/ui/uninlined_format_args.stderr b/src/tools/clippy/tests/ui/uninlined_format_args.stderr
index a12abf8be..dc4af6ef4 100644
--- a/src/tools/clippy/tests/ui/uninlined_format_args.stderr
+++ b/src/tools/clippy/tests/ui/uninlined_format_args.stderr
@@ -775,18 +775,6 @@ LL + println!("{width:width$.prec$}");
|
error: variables can be used directly in the `format!` string
- --> $DIR/uninlined_format_args.rs:125:20
- |
-LL | println!("{}", format!("{}", local_i32));
- | ^^^^^^^^^^^^^^^^^^^^^^^^
- |
-help: change this to
- |
-LL - println!("{}", format!("{}", local_i32));
-LL + println!("{}", format!("{local_i32}"));
- |
-
-error: variables can be used directly in the `format!` string
--> $DIR/uninlined_format_args.rs:143:5
|
LL | / println!(
@@ -856,5 +844,5 @@ LL - println!("expand='{}'", local_i32);
LL + println!("expand='{local_i32}'");
|
-error: aborting due to 72 previous errors
+error: aborting due to 71 previous errors
diff --git a/src/tools/clippy/tests/ui/unit_arg.rs b/src/tools/clippy/tests/ui/unit_arg.rs
index 07e70873a..674ae4f1d 100644
--- a/src/tools/clippy/tests/ui/unit_arg.rs
+++ b/src/tools/clippy/tests/ui/unit_arg.rs
@@ -1,4 +1,4 @@
-// aux-build: proc_macro_with_span.rs
+// aux-build: proc_macros.rs
#![warn(clippy::unit_arg)]
#![allow(unused_must_use, unused_variables)]
#![allow(
@@ -13,9 +13,9 @@
clippy::unused_unit
)]
-extern crate proc_macro_with_span;
+extern crate proc_macros;
-use proc_macro_with_span::with_span;
+use proc_macros::with_span;
use std::fmt::Debug;
fn foo<T: Debug>(t: T) {
diff --git a/src/tools/clippy/tests/ui/unnecessary_box_returns.rs b/src/tools/clippy/tests/ui/unnecessary_box_returns.rs
new file mode 100644
index 000000000..fe60d9297
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_box_returns.rs
@@ -0,0 +1,60 @@
+#![warn(clippy::unnecessary_box_returns)]
+
+trait Bar {
+ // lint
+ fn baz(&self) -> Box<usize>;
+}
+
+pub struct Foo {}
+
+impl Bar for Foo {
+ // don't lint: this is a problem with the trait, not the implementation
+ fn baz(&self) -> Box<usize> {
+ Box::new(42)
+ }
+}
+
+impl Foo {
+ fn baz(&self) -> Box<usize> {
+ // lint
+ Box::new(13)
+ }
+}
+
+// lint
+fn bxed_usize() -> Box<usize> {
+ Box::new(5)
+}
+
+// lint
+fn _bxed_foo() -> Box<Foo> {
+ Box::new(Foo {})
+}
+
+// don't lint: this is exported
+pub fn bxed_foo() -> Box<Foo> {
+ Box::new(Foo {})
+}
+
+// don't lint: str is unsized
+fn bxed_str() -> Box<str> {
+ "Hello, world!".to_string().into_boxed_str()
+}
+
+// don't lint: function contains the word, "box"
+fn boxed_usize() -> Box<usize> {
+ Box::new(7)
+}
+
+// don't lint: this has an unspecified return type
+fn default() {}
+
+// don't lint: this doesn't return a Box
+fn string() -> String {
+ String::from("Hello, world")
+}
+
+fn main() {
+ // don't lint: this is a closure
+ let a = || -> Box<usize> { Box::new(5) };
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr b/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr
new file mode 100644
index 000000000..b17512c10
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr
@@ -0,0 +1,35 @@
+error: boxed return of the sized type `usize`
+ --> $DIR/unnecessary_box_returns.rs:5:22
+ |
+LL | fn baz(&self) -> Box<usize>;
+ | ^^^^^^^^^^ help: try: `usize`
+ |
+ = help: changing this also requires a change to the return expressions in this function
+ = note: `-D clippy::unnecessary-box-returns` implied by `-D warnings`
+
+error: boxed return of the sized type `usize`
+ --> $DIR/unnecessary_box_returns.rs:18:22
+ |
+LL | fn baz(&self) -> Box<usize> {
+ | ^^^^^^^^^^ help: try: `usize`
+ |
+ = help: changing this also requires a change to the return expressions in this function
+
+error: boxed return of the sized type `usize`
+ --> $DIR/unnecessary_box_returns.rs:25:20
+ |
+LL | fn bxed_usize() -> Box<usize> {
+ | ^^^^^^^^^^ help: try: `usize`
+ |
+ = help: changing this also requires a change to the return expressions in this function
+
+error: boxed return of the sized type `Foo`
+ --> $DIR/unnecessary_box_returns.rs:30:19
+ |
+LL | fn _bxed_foo() -> Box<Foo> {
+ | ^^^^^^^^ help: try: `Foo`
+ |
+ = help: changing this also requires a change to the return expressions in this function
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
index 22e9bd8bd..3b93800f8 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
@@ -1,12 +1,12 @@
// run-rustfix
-// aux-build: proc_macro_with_span.rs
+// aux-build: proc_macros.rs
#![warn(clippy::unnecessary_lazy_evaluations)]
#![allow(clippy::redundant_closure)]
#![allow(clippy::bind_instead_of_map)]
#![allow(clippy::map_identity)]
-extern crate proc_macro_with_span;
-use proc_macro_with_span::with_span;
+extern crate proc_macros;
+use proc_macros::with_span;
struct Deep(Option<usize>);
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
index 8726d84a2..2851c0c51 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
@@ -1,12 +1,12 @@
// run-rustfix
-// aux-build: proc_macro_with_span.rs
+// aux-build: proc_macros.rs
#![warn(clippy::unnecessary_lazy_evaluations)]
#![allow(clippy::redundant_closure)]
#![allow(clippy::bind_instead_of_map)]
#![allow(clippy::map_identity)]
-extern crate proc_macro_with_span;
-use proc_macro_with_span::with_span;
+extern crate proc_macros;
+use proc_macros::with_span;
struct Deep(Option<usize>);
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
index d37163570..b046694f8 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
@@ -1,7 +1,12 @@
// run-rustfix
-#![feature(box_syntax)]
-#![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)]
+#![allow(
+ clippy::deref_addrof,
+ dead_code,
+ unused,
+ clippy::no_effect,
+ clippy::unnecessary_struct_initialization
+)]
#![warn(clippy::unnecessary_operation)]
struct Tuple(i32);
@@ -59,7 +64,6 @@ fn main() {
5;6;get_number();
get_number();
get_number();
- get_number();
5;get_number();
42;get_number();
assert!([42, 55].len() > get_usize());
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.rs b/src/tools/clippy/tests/ui/unnecessary_operation.rs
index a14fd4bca..9ed9679e9 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.rs
@@ -1,7 +1,12 @@
// run-rustfix
-#![feature(box_syntax)]
-#![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)]
+#![allow(
+ clippy::deref_addrof,
+ dead_code,
+ unused,
+ clippy::no_effect,
+ clippy::unnecessary_struct_initialization
+)]
#![warn(clippy::unnecessary_operation)]
struct Tuple(i32);
@@ -57,7 +62,6 @@ fn main() {
*&get_number();
&get_number();
(5, 6, get_number());
- box get_number();
get_number()..;
..get_number();
5..get_number();
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.stderr b/src/tools/clippy/tests/ui/unnecessary_operation.stderr
index f66d08ecb..a1d0d9399 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.stderr
@@ -1,5 +1,5 @@
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:51:5
+ --> $DIR/unnecessary_operation.rs:56:5
|
LL | Tuple(get_number());
| ^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
@@ -7,109 +7,103 @@ LL | Tuple(get_number());
= note: `-D clippy::unnecessary-operation` implied by `-D warnings`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:52:5
+ --> $DIR/unnecessary_operation.rs:57:5
|
LL | Struct { field: get_number() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:53:5
+ --> $DIR/unnecessary_operation.rs:58:5
|
LL | Struct { ..get_struct() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_struct();`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:54:5
+ --> $DIR/unnecessary_operation.rs:59:5
|
LL | Enum::Tuple(get_number());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:55:5
+ --> $DIR/unnecessary_operation.rs:60:5
|
LL | Enum::Struct { field: get_number() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:56:5
+ --> $DIR/unnecessary_operation.rs:61:5
|
LL | 5 + get_number();
| ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:57:5
+ --> $DIR/unnecessary_operation.rs:62:5
|
LL | *&get_number();
| ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:58:5
+ --> $DIR/unnecessary_operation.rs:63:5
|
LL | &get_number();
| ^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:59:5
+ --> $DIR/unnecessary_operation.rs:64:5
|
LL | (5, 6, get_number());
| ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;6;get_number();`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:60:5
- |
-LL | box get_number();
- | ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
-
-error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:61:5
+ --> $DIR/unnecessary_operation.rs:65:5
|
LL | get_number()..;
| ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:62:5
+ --> $DIR/unnecessary_operation.rs:66:5
|
LL | ..get_number();
| ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:63:5
+ --> $DIR/unnecessary_operation.rs:67:5
|
LL | 5..get_number();
| ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:64:5
+ --> $DIR/unnecessary_operation.rs:68:5
|
LL | [42, get_number()];
| ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:65:5
+ --> $DIR/unnecessary_operation.rs:69:5
|
LL | [42, 55][get_usize()];
| ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:66:5
+ --> $DIR/unnecessary_operation.rs:70:5
|
LL | (42, get_number()).1;
| ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:67:5
+ --> $DIR/unnecessary_operation.rs:71:5
|
LL | [get_number(); 55];
| ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:68:5
+ --> $DIR/unnecessary_operation.rs:72:5
|
LL | [42; 55][get_usize()];
| ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42; 55].len() > get_usize());`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:69:5
+ --> $DIR/unnecessary_operation.rs:73:5
|
LL | / {
LL | | get_number()
@@ -117,12 +111,12 @@ LL | | };
| |______^ help: statement can be reduced to: `get_number();`
error: unnecessary operation
- --> $DIR/unnecessary_operation.rs:72:5
+ --> $DIR/unnecessary_operation.rs:76:5
|
LL | / FooString {
LL | | s: String::from("blah"),
LL | | };
| |______^ help: statement can be reduced to: `String::from("blah");`
-error: aborting due to 20 previous errors
+error: aborting due to 19 previous errors
diff --git a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed
new file mode 100644
index 000000000..b47129e4a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed
@@ -0,0 +1,73 @@
+// run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::unnecessary_struct_initialization)]
+
+struct S {
+ f: String,
+}
+
+#[derive(Clone, Copy)]
+struct T {
+ f: u32,
+}
+
+struct U {
+ f: u32,
+}
+
+impl Clone for U {
+ fn clone(&self) -> Self {
+ // Do not lint: `Self` does not implement `Copy`
+ Self { ..*self }
+ }
+}
+
+#[derive(Copy)]
+struct V {
+ f: u32,
+}
+
+impl Clone for V {
+ fn clone(&self) -> Self {
+ // Lint: `Self` implements `Copy`
+ *self
+ }
+}
+
+fn main() {
+ // Should lint: `a` would be consumed anyway
+ let a = S { f: String::from("foo") };
+ let mut b = a;
+
+ // Should lint: `b` would be consumed, and is mutable
+ let c = &mut b;
+
+ // Should not lint as `d` is not mutable
+ let d = S { f: String::from("foo") };
+ let e = &mut S { ..d };
+
+ // Should lint as `f` would be consumed anyway
+ let f = S { f: String::from("foo") };
+ let g = &f;
+
+ // Should lint: the result of an expression is mutable
+ let h = &mut *Box::new(S { f: String::from("foo") });
+
+ // Should not lint: `m` would be both alive and borrowed
+ let m = T { f: 17 };
+ let n = &T { ..m };
+
+ // Should not lint: `m` should not be modified
+ let o = &mut T { ..m };
+ o.f = 32;
+ assert_eq!(m.f, 17);
+
+ // Should not lint: `m` should not be modified
+ let o = &mut T { ..m } as *mut T;
+ unsafe { &mut *o }.f = 32;
+ assert_eq!(m.f, 17);
+
+ // Should lint: the result of an expression is mutable and temporary
+ let p = &mut *Box::new(T { f: 5 });
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs
new file mode 100644
index 000000000..63b11c626
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs
@@ -0,0 +1,77 @@
+// run-rustfix
+
+#![allow(unused)]
+#![warn(clippy::unnecessary_struct_initialization)]
+
+struct S {
+ f: String,
+}
+
+#[derive(Clone, Copy)]
+struct T {
+ f: u32,
+}
+
+struct U {
+ f: u32,
+}
+
+impl Clone for U {
+ fn clone(&self) -> Self {
+ // Do not lint: `Self` does not implement `Copy`
+ Self { ..*self }
+ }
+}
+
+#[derive(Copy)]
+struct V {
+ f: u32,
+}
+
+impl Clone for V {
+ fn clone(&self) -> Self {
+ // Lint: `Self` implements `Copy`
+ Self { ..*self }
+ }
+}
+
+fn main() {
+ // Should lint: `a` would be consumed anyway
+ let a = S { f: String::from("foo") };
+ let mut b = S { ..a };
+
+ // Should lint: `b` would be consumed, and is mutable
+ let c = &mut S { ..b };
+
+ // Should not lint as `d` is not mutable
+ let d = S { f: String::from("foo") };
+ let e = &mut S { ..d };
+
+ // Should lint as `f` would be consumed anyway
+ let f = S { f: String::from("foo") };
+ let g = &S { ..f };
+
+ // Should lint: the result of an expression is mutable
+ let h = &mut S {
+ ..*Box::new(S { f: String::from("foo") })
+ };
+
+ // Should not lint: `m` would be both alive and borrowed
+ let m = T { f: 17 };
+ let n = &T { ..m };
+
+ // Should not lint: `m` should not be modified
+ let o = &mut T { ..m };
+ o.f = 32;
+ assert_eq!(m.f, 17);
+
+ // Should not lint: `m` should not be modified
+ let o = &mut T { ..m } as *mut T;
+ unsafe { &mut *o }.f = 32;
+ assert_eq!(m.f, 17);
+
+ // Should lint: the result of an expression is mutable and temporary
+ let p = &mut T {
+ ..*Box::new(T { f: 5 })
+ };
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.stderr b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.stderr
new file mode 100644
index 000000000..ca4970577
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.stderr
@@ -0,0 +1,46 @@
+error: unnecessary struct building
+ --> $DIR/unnecessary_struct_initialization.rs:34:9
+ |
+LL | Self { ..*self }
+ | ^^^^^^^^^^^^^^^^ help: replace with: `*self`
+ |
+ = note: `-D clippy::unnecessary-struct-initialization` implied by `-D warnings`
+
+error: unnecessary struct building
+ --> $DIR/unnecessary_struct_initialization.rs:41:17
+ |
+LL | let mut b = S { ..a };
+ | ^^^^^^^^^ help: replace with: `a`
+
+error: unnecessary struct building
+ --> $DIR/unnecessary_struct_initialization.rs:44:18
+ |
+LL | let c = &mut S { ..b };
+ | ^^^^^^^^^ help: replace with: `b`
+
+error: unnecessary struct building
+ --> $DIR/unnecessary_struct_initialization.rs:52:14
+ |
+LL | let g = &S { ..f };
+ | ^^^^^^^^^ help: replace with: `f`
+
+error: unnecessary struct building
+ --> $DIR/unnecessary_struct_initialization.rs:55:18
+ |
+LL | let h = &mut S {
+ | __________________^
+LL | | ..*Box::new(S { f: String::from("foo") })
+LL | | };
+ | |_____^ help: replace with: `*Box::new(S { f: String::from("foo") })`
+
+error: unnecessary struct building
+ --> $DIR/unnecessary_struct_initialization.rs:74:18
+ |
+LL | let p = &mut T {
+ | __________________^
+LL | | ..*Box::new(T { f: 5 })
+LL | | };
+ | |_____^ help: replace with: `*Box::new(T { f: 5 })`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs b/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs
index c160e31af..431093ab3 100644
--- a/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs
@@ -1,10 +1,10 @@
-// aux-build:doc_unsafe_macros.rs
+// aux-build:proc_macros.rs
#![allow(clippy::let_unit_value)]
#![warn(clippy::unnecessary_safety_doc)]
-#[macro_use]
-extern crate doc_unsafe_macros;
+extern crate proc_macros;
+use proc_macros::external;
/// This is has no safety section, and does not need one either
pub fn destroy_the_planet() {
@@ -129,7 +129,11 @@ macro_rules! very_safe {
very_safe!();
// we don't lint code from external macros
-undocd_safe!();
+external!(
+ pub fn vey_oy() {
+ unimplemented!();
+ }
+);
fn main() {}
diff --git a/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.stderr b/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.stderr
index 72898c93f..b0f20fdac 100644
--- a/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_unsafety_doc.stderr
@@ -42,7 +42,7 @@ LL | very_safe!();
= note: this error originates in the macro `very_safe` (in Nightly builds, run with -Z macro-backtrace for more info)
error: docs for safe trait have unnecessary `# Safety` section
- --> $DIR/unnecessary_unsafety_doc.rs:147:1
+ --> $DIR/unnecessary_unsafety_doc.rs:151:1
|
LL | pub trait DocumentedSafeTraitWithImplementationHeader {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/unused_format_specs.fixed b/src/tools/clippy/tests/ui/unused_format_specs.fixed
deleted file mode 100644
index 2930722b4..000000000
--- a/src/tools/clippy/tests/ui/unused_format_specs.fixed
+++ /dev/null
@@ -1,18 +0,0 @@
-// run-rustfix
-
-#![warn(clippy::unused_format_specs)]
-#![allow(unused)]
-
-fn main() {
- let f = 1.0f64;
- println!("{}", 1.0);
- println!("{f} {f:?}");
-
- println!("{}", 1);
-}
-
-fn should_not_lint() {
- let f = 1.0f64;
- println!("{:.1}", 1.0);
- println!("{f:.w$} {f:.*?}", 3, w = 2);
-}
diff --git a/src/tools/clippy/tests/ui/unused_format_specs.rs b/src/tools/clippy/tests/ui/unused_format_specs.rs
deleted file mode 100644
index ee192a000..000000000
--- a/src/tools/clippy/tests/ui/unused_format_specs.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-// run-rustfix
-
-#![warn(clippy::unused_format_specs)]
-#![allow(unused)]
-
-fn main() {
- let f = 1.0f64;
- println!("{:.}", 1.0);
- println!("{f:.} {f:.?}");
-
- println!("{:.}", 1);
-}
-
-fn should_not_lint() {
- let f = 1.0f64;
- println!("{:.1}", 1.0);
- println!("{f:.w$} {f:.*?}", 3, w = 2);
-}
diff --git a/src/tools/clippy/tests/ui/unused_format_specs.stderr b/src/tools/clippy/tests/ui/unused_format_specs.stderr
deleted file mode 100644
index 7231c17e7..000000000
--- a/src/tools/clippy/tests/ui/unused_format_specs.stderr
+++ /dev/null
@@ -1,54 +0,0 @@
-error: empty precision specifier has no effect
- --> $DIR/unused_format_specs.rs:8:17
- |
-LL | println!("{:.}", 1.0);
- | ^
- |
- = note: a precision specifier is not required to format floats
- = note: `-D clippy::unused-format-specs` implied by `-D warnings`
-help: remove the `.`
- |
-LL - println!("{:.}", 1.0);
-LL + println!("{}", 1.0);
- |
-
-error: empty precision specifier has no effect
- --> $DIR/unused_format_specs.rs:9:18
- |
-LL | println!("{f:.} {f:.?}");
- | ^
- |
- = note: a precision specifier is not required to format floats
-help: remove the `.`
- |
-LL - println!("{f:.} {f:.?}");
-LL + println!("{f} {f:.?}");
- |
-
-error: empty precision specifier has no effect
- --> $DIR/unused_format_specs.rs:9:24
- |
-LL | println!("{f:.} {f:.?}");
- | ^
- |
- = note: a precision specifier is not required to format floats
-help: remove the `.`
- |
-LL - println!("{f:.} {f:.?}");
-LL + println!("{f:.} {f:?}");
- |
-
-error: empty precision specifier has no effect
- --> $DIR/unused_format_specs.rs:11:17
- |
-LL | println!("{:.}", 1);
- | ^
- |
-help: remove the `.`
- |
-LL - println!("{:.}", 1);
-LL + println!("{}", 1);
- |
-
-error: aborting due to 4 previous errors
-
diff --git a/src/tools/clippy/tests/ui/unused_format_specs_unfixable.stderr b/src/tools/clippy/tests/ui/unused_format_specs_unfixable.stderr
index 9f1890282..cb7156b6b 100644
--- a/src/tools/clippy/tests/ui/unused_format_specs_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/unused_format_specs_unfixable.stderr
@@ -37,11 +37,7 @@ error: format specifiers have no effect on `format_args!()`
LL | println!("{:5}.", format_args_from_macro!());
| ^^^^
|
-help: for the width to apply consider using `format!()`
- --> $DIR/unused_format_specs_unfixable.rs:16:17
- |
-LL | println!("{:5}.", format_args_from_macro!());
- | ^
+ = help: for the width to apply consider using `format!()`
help: if the current behavior is intentional, remove the format specifiers
|
LL - println!("{:5}.", format_args_from_macro!());
@@ -54,11 +50,7 @@ error: format specifiers have no effect on `format_args!()`
LL | println!("{args:5}");
| ^^^^^^^^
|
-help: for the width to apply consider using `format!()`
- --> $DIR/unused_format_specs_unfixable.rs:19:21
- |
-LL | println!("{args:5}");
- | ^
+ = help: for the width to apply consider using `format!()`
help: if the current behavior is intentional, remove the format specifiers
|
LL - println!("{args:5}");
diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed
index 0a6166571..3ac621731 100644
--- a/src/tools/clippy/tests/ui/use_self.fixed
+++ b/src/tools/clippy/tests/ui/use_self.fixed
@@ -647,3 +647,13 @@ fn msrv_1_37() {
}
}
}
+
+mod issue_10371 {
+ struct Val<const V: i32> {}
+
+ impl<const V: i32> From<Val<V>> for i32 {
+ fn from(_: Val<V>) -> Self {
+ todo!()
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs
index 39c2b431f..9dc5d1e3f 100644
--- a/src/tools/clippy/tests/ui/use_self.rs
+++ b/src/tools/clippy/tests/ui/use_self.rs
@@ -647,3 +647,13 @@ fn msrv_1_37() {
}
}
}
+
+mod issue_10371 {
+ struct Val<const V: i32> {}
+
+ impl<const V: i32> From<Val<V>> for i32 {
+ fn from(_: Val<V>) -> Self {
+ todo!()
+ }
+ }
+}
diff --git a/src/tools/clippy/tests/workspace.rs b/src/tools/clippy/tests/workspace.rs
index 95325e060..c9cbc5054 100644
--- a/src/tools/clippy/tests/workspace.rs
+++ b/src/tools/clippy/tests/workspace.rs
@@ -1,4 +1,4 @@
-#![feature(once_cell)]
+#![feature(lazy_cell)]
use std::path::PathBuf;
use std::process::Command;
diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml
index 6f50ef932..3f8f6a7b9 100644
--- a/src/tools/clippy/triagebot.toml
+++ b/src/tools/clippy/triagebot.toml
@@ -17,6 +17,7 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB
[assign.owners]
"/.github" = ["@flip1995"]
+"/util/gh-pages" = ["@xFrednet"]
"*" = [
"@flip1995",
"@Manishearth",