]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #96733 - SparrowLii:place_to_string, r=davidtwco
authorGuillaume Gomez <guillaume1.gomez@gmail.com>
Fri, 6 May 2022 18:05:40 +0000 (20:05 +0200)
committerGitHub <noreply@github.com>
Fri, 6 May 2022 18:05:40 +0000 (20:05 +0200)
turn `append_place_to_string` from recursion into iteration

This PR fixes the FIXME in the impl of `append_place_to_string` which turns `append_place_to_string` from recursion into iteration, meanwhile simplifying the code relatively.

455 files changed:
Cargo.lock
compiler/rustc_ast/src/token.rs
compiler/rustc_ast_pretty/src/pprust/state.rs
compiler/rustc_borrowck/src/borrow_set.rs
compiler/rustc_borrowck/src/lib.rs
compiler/rustc_builtin_macros/src/source_util.rs
compiler/rustc_codegen_ssa/src/back/link.rs
compiler/rustc_codegen_ssa/src/back/linker.rs
compiler/rustc_codegen_ssa/src/base.rs
compiler/rustc_codegen_ssa/src/lib.rs
compiler/rustc_const_eval/src/interpret/operand.rs
compiler/rustc_const_eval/src/interpret/place.rs
compiler/rustc_const_eval/src/interpret/validity.rs
compiler/rustc_data_structures/src/lib.rs
compiler/rustc_data_structures/src/sip128.rs
compiler/rustc_data_structures/src/stable_hasher.rs
compiler/rustc_expand/src/base.rs
compiler/rustc_feature/src/active.rs
compiler/rustc_feature/src/builtin_attrs.rs
compiler/rustc_hir/src/lang_items.rs
compiler/rustc_lint/src/lib.rs
compiler/rustc_lint_defs/src/builtin.rs
compiler/rustc_metadata/src/rmeta/decoder.rs
compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
compiler/rustc_metadata/src/rmeta/encoder.rs
compiler/rustc_metadata/src/rmeta/mod.rs
compiler/rustc_middle/src/query/mod.rs
compiler/rustc_middle/src/ty/print/mod.rs
compiler/rustc_mir_transform/src/const_prop.rs
compiler/rustc_mir_transform/src/const_prop_lint.rs
compiler/rustc_monomorphize/src/collector.rs
compiler/rustc_passes/Cargo.toml
compiler/rustc_passes/src/check_attr.rs
compiler/rustc_passes/src/debugger_visualizer.rs [new file with mode: 0644]
compiler/rustc_passes/src/lib.rs
compiler/rustc_resolve/src/late.rs
compiler/rustc_resolve/src/late/diagnostics.rs
compiler/rustc_span/src/def_id.rs
compiler/rustc_span/src/lib.rs
compiler/rustc_span/src/symbol.rs
compiler/rustc_typeck/src/check/method/probe.rs
compiler/rustc_typeck/src/coherence/inherent_impls.rs
compiler/rustc_typeck/src/coherence/orphan.rs
compiler/rustc_typeck/src/constrained_generic_params.rs
library/alloc/src/boxed.rs
library/alloc/src/collections/btree/map.rs
library/alloc/src/collections/linked_list.rs
library/alloc/src/collections/vec_deque/mod.rs
library/alloc/src/lib.rs
library/alloc/src/rc.rs
library/alloc/src/sync.rs
library/core/src/ffi/c_str.rs
library/core/src/hash/mod.rs
library/core/src/hash/sip.rs
library/core/src/internal_macros.rs
library/core/src/intrinsics.rs
library/core/src/num/int_macros.rs
library/core/src/num/uint_macros.rs
library/core/src/ptr/const_ptr.rs
library/core/src/ptr/mut_ptr.rs
library/core/tests/hash/mod.rs
library/core/tests/lib.rs
library/core/tests/num/i128.rs
library/core/tests/num/i16.rs
library/core/tests/num/i32.rs
library/core/tests/num/i64.rs
library/core/tests/num/i8.rs
library/core/tests/num/int_macros.rs
library/core/tests/num/u128.rs
library/core/tests/num/u16.rs
library/core/tests/num/u32.rs
library/core/tests/num/u64.rs
library/core/tests/num/u8.rs
library/core/tests/num/uint_macros.rs
library/proc_macro/src/lib.rs
library/std/Cargo.toml
library/std/src/collections/hash/map.rs
library/std/src/ffi/os_str.rs
library/std/src/ffi/os_str/tests.rs
library/std/src/lib.rs
library/std/src/net/ip.rs
library/std/src/sync/condvar/tests.rs
library/std/src/sys/unix/futex.rs
library/std/src/sys/unix/locks/futex_rwlock.rs
library/std/src/sys/unix/locks/mod.rs
library/std/src/sys/unix/thread_parker.rs
library/std/src/sys_common/thread_parker/mod.rs
src/doc/unstable-book/src/language-features/debugger-visualizer.md [new file with mode: 0644]
src/librustdoc/doctest.rs
src/librustdoc/html/static/css/rustdoc.css
src/librustdoc/html/static/js/main.js
src/librustdoc/html/static/js/scrape-examples.js
src/librustdoc/html/static/js/search.js
src/librustdoc/html/static/js/settings.js
src/librustdoc/html/static/js/source-script.js
src/librustdoc/html/static/js/storage.js
src/librustdoc/lib.rs
src/test/assembly/asm/global_asm.rs
src/test/debuginfo/msvc-embedded-natvis.natvis [new file with mode: 0644]
src/test/debuginfo/msvc-embedded-natvis.rs [new file with mode: 0644]
src/test/rustdoc-gui/search-tab-change-title-fn-sig.goml [new file with mode: 0644]
src/test/rustdoc-gui/search-tab-selection-if-current-is-empty.goml [deleted file]
src/test/ui/borrowck/suggest-local-var-imm-and-mut.nll.stderr
src/test/ui/borrowck/two-phase-cannot-nest-mut-self-calls.stderr
src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.base.stderr [new file with mode: 0644]
src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2015.stderr
src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2018.stderr
src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll.stderr [new file with mode: 0644]
src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll2015.stderr
src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll2018.stderr
src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.rs
src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.nll.stderr [deleted file]
src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.rs [deleted file]
src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.stderr [deleted file]
src/test/ui/error-codes/E0502.nll.stderr
src/test/ui/feature-gates/feature-gate-debugger-visualizer.rs [new file with mode: 0644]
src/test/ui/feature-gates/feature-gate-debugger-visualizer.stderr [new file with mode: 0644]
src/test/ui/incoherent-inherent-impls/auxiliary/extern-crate.rs [new file with mode: 0644]
src/test/ui/incoherent-inherent-impls/needs-has-incoherent-impls.rs [new file with mode: 0644]
src/test/ui/incoherent-inherent-impls/needs-has-incoherent-impls.stderr [new file with mode: 0644]
src/test/ui/incoherent-inherent-impls/no-attr-empty-impl.rs [new file with mode: 0644]
src/test/ui/incoherent-inherent-impls/no-attr-empty-impl.stderr [new file with mode: 0644]
src/test/ui/inline-const/const-expr-generic-err.rs [new file with mode: 0644]
src/test/ui/inline-const/const-expr-generic-err.stderr [new file with mode: 0644]
src/test/ui/inline-const/const-expr-generic-err2.rs [new file with mode: 0644]
src/test/ui/inline-const/const-expr-generic-err2.stderr [new file with mode: 0644]
src/test/ui/inline-const/const-expr-generic.rs [new file with mode: 0644]
src/test/ui/inline-const/const-match-pat-generic.rs
src/test/ui/inline-const/const-match-pat-generic.stderr
src/test/ui/invalid/invalid-debugger-visualizer-option.rs [new file with mode: 0644]
src/test/ui/invalid/invalid-debugger-visualizer-option.stderr [new file with mode: 0644]
src/test/ui/invalid/invalid-debugger-visualizer-target.rs [new file with mode: 0644]
src/test/ui/invalid/invalid-debugger-visualizer-target.stderr [new file with mode: 0644]
src/test/ui/issues/issue-61623.rs
src/test/ui/issues/issue-61623.stderr
src/test/ui/lifetimes/issue-64173-unused-lifetimes.rs [new file with mode: 0644]
src/test/ui/lifetimes/issue-64173-unused-lifetimes.stderr [new file with mode: 0644]
src/test/ui/nll/lint-no-err.rs
src/test/ui/nll/lint-no-err.stderr [deleted file]
src/test/ui/proc-macro/auxiliary/expand-expr.rs
src/test/ui/proc-macro/capture-macro-rules-invoke.stdout
src/test/ui/proc-macro/capture-unglued-token.stdout
src/test/ui/proc-macro/expand-expr.rs
src/test/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout
src/test/ui/proc-macro/issue-75734-pp-paren.stdout
src/test/ui/proc-macro/issue-78675-captured-inner-attrs.stdout
src/test/ui/proc-macro/issue-80760-empty-stmt.stdout
src/test/ui/proc-macro/nested-nonterminal-tokens.stdout
src/test/ui/proc-macro/nodelim-groups.stdout
src/test/ui/proc-macro/nonterminal-expansion.stdout
src/test/ui/proc-macro/nonterminal-token-hygiene.stdout
src/test/ui/proc-macro/parent-source-spans.rs
src/test/ui/proc-macro/parent-source-spans.stderr
src/test/ui/resolve/issue-2356.stderr
src/test/ui/suggestions/assoc_fn_without_self.rs [new file with mode: 0644]
src/test/ui/suggestions/assoc_fn_without_self.stderr [new file with mode: 0644]
src/test/ui/type-alias-impl-trait/coherence.rs
src/test/ui/type-alias-impl-trait/coherence.stderr
src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.yml
src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.yml
src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.yml
src/tools/clippy/.github/workflows/clippy.yml
src/tools/clippy/.github/workflows/clippy_bors.yml
src/tools/clippy/.github/workflows/clippy_dev.yml
src/tools/clippy/.github/workflows/deploy.yml
src/tools/clippy/.github/workflows/remark.yml
src/tools/clippy/CHANGELOG.md
src/tools/clippy/clippy_dev/Cargo.toml
src/tools/clippy/clippy_dev/src/lib.rs
src/tools/clippy/clippy_dev/src/main.rs
src/tools/clippy/clippy_dev/src/new_lint.rs
src/tools/clippy/clippy_dev/src/setup/mod.rs
src/tools/clippy/clippy_dev/src/update_lints.rs
src/tools/clippy/clippy_lints/src/attrs.rs
src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
src/tools/clippy/clippy_lints/src/bytes_count_to_len.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs
src/tools/clippy/clippy_lints/src/casts/mod.rs
src/tools/clippy/clippy_lints/src/collapsible_if.rs
src/tools/clippy/clippy_lints/src/doc.rs
src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
src/tools/clippy/clippy_lints/src/empty_drop.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs
src/tools/clippy/clippy_lints/src/eta_reduction.rs
src/tools/clippy/clippy_lints/src/format_push_string.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/functions/must_use.rs
src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs
src/tools/clippy/clippy_lints/src/identity_op.rs
src/tools/clippy/clippy_lints/src/implicit_hasher.rs
src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
src/tools/clippy/clippy_lints/src/inherent_impl.rs
src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
src/tools/clippy/clippy_lints/src/large_include_file.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/lib.register_all.rs
src/tools/clippy/clippy_lints/src/lib.register_complexity.rs
src/tools/clippy/clippy_lints/src/lib.register_lints.rs
src/tools/clippy/clippy_lints/src/lib.register_nursery.rs
src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs
src/tools/clippy/clippy_lints/src/lib.register_perf.rs
src/tools/clippy/clippy_lints/src/lib.register_restriction.rs
src/tools/clippy/clippy_lints/src/lib.register_style.rs
src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/lifetimes.rs
src/tools/clippy/clippy_lints/src/literal_representation.rs
src/tools/clippy/clippy_lints/src/loops/never_loop.rs
src/tools/clippy/clippy_lints/src/manual_bits.rs
src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
src/tools/clippy/clippy_lints/src/map_clone.rs
src/tools/clippy/clippy_lints/src/match_result_ok.rs
src/tools/clippy/clippy_lints/src/matches/infalliable_detructuring_match.rs [deleted file]
src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
src/tools/clippy/clippy_lints/src/matches/mod.rs
src/tools/clippy/clippy_lints/src/matches/needless_match.rs
src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs
src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs
src/tools/clippy/clippy_lints/src/misc_early/mod.rs
src/tools/clippy/clippy_lints/src/missing_inline.rs
src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs
src/tools/clippy/clippy_lints/src/needless_late_init.rs
src/tools/clippy/clippy_lints/src/new_without_default.rs
src/tools/clippy/clippy_lints/src/non_expressive_names.rs
src/tools/clippy/clippy_lints/src/octal_escapes.rs
src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
src/tools/clippy/clippy_lints/src/option_if_let_else.rs
src/tools/clippy/clippy_lints/src/ptr.rs
src/tools/clippy/clippy_lints/src/pub_use.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
src/tools/clippy/clippy_lints/src/renamed_lints.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/same_name_method.rs
src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs
src/tools/clippy/clippy_lints/src/strings.rs
src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
src/tools/clippy/clippy_lints/src/trait_bounds.rs
src/tools/clippy/clippy_lints/src/transmute/utils.rs
src/tools/clippy/clippy_lints/src/types/mod.rs
src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
src/tools/clippy/clippy_lints/src/unit_types/mod.rs
src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
src/tools/clippy/clippy_lints/src/use_self.rs
src/tools/clippy/clippy_lints/src/utils/author.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/utils/dump_hir.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/utils/inspector.rs [deleted file]
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
src/tools/clippy/clippy_lints/src/utils/mod.rs
src/tools/clippy/clippy_utils/src/consts.rs
src/tools/clippy/clippy_utils/src/hir_utils.rs
src/tools/clippy/clippy_utils/src/macros.rs
src/tools/clippy/clippy_utils/src/msrvs.rs
src/tools/clippy/clippy_utils/src/numeric_literal.rs
src/tools/clippy/clippy_utils/src/paths.rs
src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
src/tools/clippy/clippy_utils/src/source.rs
src/tools/clippy/clippy_utils/src/sugg.rs
src/tools/clippy/clippy_utils/src/ty.rs
src/tools/clippy/clippy_utils/src/usage.rs
src/tools/clippy/clippy_utils/src/visitors.rs
src/tools/clippy/doc/adding_lints.md
src/tools/clippy/lintcheck/src/main.rs
src/tools/clippy/rust-toolchain
src/tools/clippy/src/driver.rs
src/tools/clippy/tests/dogfood.rs
src/tools/clippy/tests/lint_message_convention.rs
src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr
src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed
src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs
src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/await_holding_invalid_type/clippy.toml [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/functions_maxlines/test.rs
src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr
src/tools/clippy/tests/ui-toml/large_include_file/clippy.toml [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/large_include_file/too_big.txt [new file with mode: 0644]
src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.stderr
src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
src/tools/clippy/tests/ui/assertions_on_constants.rs
src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
src/tools/clippy/tests/ui/auxiliary/proc_macro_with_span.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/bytes_count_to_len.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/bytes_count_to_len.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/bytes_count_to_len.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/cast.rs
src/tools/clippy/tests/ui/cast.stderr
src/tools/clippy/tests/ui/cast_alignment.rs
src/tools/clippy/tests/ui/cast_slice_different_sizes.rs
src/tools/clippy/tests/ui/cast_slice_different_sizes.stderr
src/tools/clippy/tests/ui/collapsible_else_if.fixed
src/tools/clippy/tests/ui/collapsible_else_if.rs
src/tools/clippy/tests/ui/collapsible_else_if.stderr
src/tools/clippy/tests/ui/crashes/auxiliary/ice-8681-aux.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/crashes/ice-2865.rs
src/tools/clippy/tests/ui/crashes/ice-3151.rs
src/tools/clippy/tests/ui/crashes/ice-5944.rs
src/tools/clippy/tests/ui/crashes/ice-8250.stderr
src/tools/clippy/tests/ui/crashes/ice-8681.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/crashes/ice-96721.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/crashes/ice-96721.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed
src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs
src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr
src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed
src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs
src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr
src/tools/clippy/tests/ui/deprecated.rs
src/tools/clippy/tests/ui/deprecated.stderr
src/tools/clippy/tests/ui/doc_unsafe.rs
src/tools/clippy/tests/ui/doc_unsafe.stderr
src/tools/clippy/tests/ui/drop_non_drop.stderr
src/tools/clippy/tests/ui/empty_drop.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/empty_drop.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/empty_drop.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/eta.fixed
src/tools/clippy/tests/ui/eta.rs
src/tools/clippy/tests/ui/eta.stderr
src/tools/clippy/tests/ui/extra_unused_lifetimes.rs
src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr
src/tools/clippy/tests/ui/format_push_string.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/format_push_string.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/identity_op.rs
src/tools/clippy/tests/ui/identity_op.stderr
src/tools/clippy/tests/ui/impl.rs
src/tools/clippy/tests/ui/indexing_slicing_index.stderr
src/tools/clippy/tests/ui/is_digit_ascii_radix.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/is_digit_ascii_radix.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/is_digit_ascii_radix.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/iter_overeager_cloned.fixed
src/tools/clippy/tests/ui/iter_overeager_cloned.rs
src/tools/clippy/tests/ui/iter_with_drain.fixed
src/tools/clippy/tests/ui/iter_with_drain.rs
src/tools/clippy/tests/ui/let_underscore_drop.rs
src/tools/clippy/tests/ui/let_underscore_drop.stderr
src/tools/clippy/tests/ui/let_unit.fixed
src/tools/clippy/tests/ui/let_unit.rs
src/tools/clippy/tests/ui/let_unit.stderr
src/tools/clippy/tests/ui/manual_bits.fixed
src/tools/clippy/tests/ui/manual_bits.rs
src/tools/clippy/tests/ui/manual_bits.stderr
src/tools/clippy/tests/ui/manual_non_exhaustive.rs [deleted file]
src/tools/clippy/tests/ui/manual_non_exhaustive.stderr [deleted file]
src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/manual_split_once.fixed
src/tools/clippy/tests/ui/manual_split_once.rs
src/tools/clippy/tests/ui/manual_split_once.stderr
src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed
src/tools/clippy/tests/ui/mistyped_literal_suffix.rs
src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr
src/tools/clippy/tests/ui/mut_from_ref.rs
src/tools/clippy/tests/ui/mut_from_ref.stderr
src/tools/clippy/tests/ui/needless_for_each_fixable.fixed
src/tools/clippy/tests/ui/needless_for_each_fixable.rs
src/tools/clippy/tests/ui/needless_for_each_fixable.stderr
src/tools/clippy/tests/ui/needless_late_init.rs
src/tools/clippy/tests/ui/needless_late_init.stderr
src/tools/clippy/tests/ui/needless_late_init_fixable.fixed
src/tools/clippy/tests/ui/needless_late_init_fixable.rs
src/tools/clippy/tests/ui/needless_late_init_fixable.stderr
src/tools/clippy/tests/ui/needless_match.fixed
src/tools/clippy/tests/ui/needless_match.rs
src/tools/clippy/tests/ui/needless_match.stderr
src/tools/clippy/tests/ui/needless_option_as_deref.fixed
src/tools/clippy/tests/ui/needless_option_as_deref.rs
src/tools/clippy/tests/ui/needless_option_take.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/needless_option_take.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/needless_option_take.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/needless_splitn.fixed
src/tools/clippy/tests/ui/needless_splitn.rs
src/tools/clippy/tests/ui/needless_splitn.stderr
src/tools/clippy/tests/ui/new_without_default.rs
src/tools/clippy/tests/ui/non_expressive_names.rs
src/tools/clippy/tests/ui/numbered_fields.fixed
src/tools/clippy/tests/ui/numbered_fields.rs
src/tools/clippy/tests/ui/option_if_let_else.fixed
src/tools/clippy/tests/ui/option_if_let_else.rs
src/tools/clippy/tests/ui/option_if_let_else.stderr
src/tools/clippy/tests/ui/option_take_on_temporary.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/or_then_unwrap.fixed
src/tools/clippy/tests/ui/or_then_unwrap.rs
src/tools/clippy/tests/ui/panicking_macros.rs
src/tools/clippy/tests/ui/pub_use.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/pub_use.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/redundant_pub_crate.fixed
src/tools/clippy/tests/ui/redundant_pub_crate.rs
src/tools/clippy/tests/ui/rename.fixed
src/tools/clippy/tests/ui/rename.rs
src/tools/clippy/tests/ui/rename.stderr
src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs
src/tools/clippy/tests/ui/same_functions_in_if_condition.rs
src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr
src/tools/clippy/tests/ui/shadow.rs
src/tools/clippy/tests/ui/shadow.stderr
src/tools/clippy/tests/ui/similar_names.rs
src/tools/clippy/tests/ui/similar_names.stderr
src/tools/clippy/tests/ui/single_char_lifetime_names.rs
src/tools/clippy/tests/ui/single_char_lifetime_names.stderr
src/tools/clippy/tests/ui/single_match_else.rs
src/tools/clippy/tests/ui/single_match_else.stderr
src/tools/clippy/tests/ui/stable_sort_primitive.stderr
src/tools/clippy/tests/ui/suspicious_else_formatting.rs
src/tools/clippy/tests/ui/to_digit_is_some.fixed
src/tools/clippy/tests/ui/to_digit_is_some.rs
src/tools/clippy/tests/ui/to_digit_is_some.stderr
src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs
src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr
src/tools/clippy/tests/ui/transmute_collection.rs
src/tools/clippy/tests/ui/trim_split_whitespace.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/trim_split_whitespace.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/trim_split_whitespace.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/type_repetition_in_bounds.rs
src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr
src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
src/tools/clippy/tests/ui/uninit.rs
src/tools/clippy/tests/ui/uninit.stderr
src/tools/clippy/tests/ui/unit_arg.rs
src/tools/clippy/tests/ui/unit_arg.stderr
src/tools/clippy/tests/ui/unit_hash.rs
src/tools/clippy/tests/ui/unit_hash.stderr
src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
src/tools/clippy/tests/ui/unnecessary_to_owned.rs
src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
src/tools/clippy/tests/ui/unnested_or_patterns.fixed
src/tools/clippy/tests/ui/unnested_or_patterns.rs
src/tools/clippy/tests/ui/unnested_or_patterns.stderr
src/tools/clippy/tests/ui/useless_attribute.fixed
src/tools/clippy/tests/ui/useless_attribute.rs
src/tools/clippy/tests/ui/useless_attribute.stderr
src/tools/clippy/tests/ui/wildcard_imports.fixed
src/tools/clippy/tests/ui/wildcard_imports.rs
src/tools/clippy/tests/ui/wildcard_imports.stderr
src/tools/clippy/tests/ui/wrong_self_convention.rs
src/tools/clippy/tests/ui/wrong_self_convention.stderr
src/tools/clippy/tests/ui/wrong_self_convention2.rs

index 188db89bc6c78a9007c8be40c57062202030b2e1..f5aacba20cd2363675a0f022b5f69c97f384986d 100644 (file)
@@ -655,6 +655,7 @@ dependencies = [
 name = "clippy_dev"
 version = "0.0.1"
 dependencies = [
+ "aho-corasick",
  "clap 2.34.0",
  "indoc",
  "itertools",
@@ -2072,9 +2073,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
 
 [[package]]
 name = "libc"
-version = "0.2.121"
+version = "0.2.125"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
+checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b"
 dependencies = [
  "rustc-std-workspace-core",
 ]
@@ -4184,6 +4185,7 @@ dependencies = [
  "rustc_attr",
  "rustc_data_structures",
  "rustc_errors",
+ "rustc_expand",
  "rustc_feature",
  "rustc_hir",
  "rustc_index",
index 72dd44a4b4d9833a3796bd8169d9ec66675981cf..35eca23a116258535a7b167cd03ae8ed34348d87 100644 (file)
@@ -50,11 +50,12 @@ pub enum Delimiter {
     Brace,
     /// `[ ... ]`
     Bracket,
-    /// `Ø ... Ã˜`
+    /// `/*«*/ ... /*»*/`
     /// An invisible delimiter, that may, for example, appear around tokens coming from a
     /// "macro variable" `$var`. It is important to preserve operator priorities in cases like
     /// `$var * 3` where `$var` is `1 + 2`.
-    /// Invisible delimiters might not survive roundtrip of a token stream through a string.
+    /// Invisible delimiters are not directly writable in normal Rust code except as comments.
+    /// Therefore, they might not survive a roundtrip of a token stream through a string.
     Invisible,
 }
 
index e79f4f0a0950d858fa9a6e2c7cce78b883f3b0ca..c02cdc295610fc1fc852874a213f6d1d7d940ad7 100644 (file)
@@ -590,15 +590,29 @@ fn print_mac_common(
                     self.nbsp();
                 }
                 self.word("{");
-                if !tts.is_empty() {
+                let empty = tts.is_empty();
+                if !empty {
                     self.space();
                 }
                 self.ibox(0);
                 self.print_tts(tts, convert_dollar_crate);
                 self.end();
-                let empty = tts.is_empty();
                 self.bclose(span, empty);
             }
+            Some(Delimiter::Invisible) => {
+                self.word("/*«*/");
+                let empty = tts.is_empty();
+                if !empty {
+                    self.space();
+                }
+                self.ibox(0);
+                self.print_tts(tts, convert_dollar_crate);
+                self.end();
+                if !empty {
+                    self.space();
+                }
+                self.word("/*»*/");
+            }
             Some(delim) => {
                 let token_str = self.token_kind_to_string(&token::OpenDelim(delim));
                 self.word(token_str);
@@ -772,9 +786,8 @@ fn token_kind_to_string_ext(
             token::CloseDelim(Delimiter::Bracket) => "]".into(),
             token::OpenDelim(Delimiter::Brace) => "{".into(),
             token::CloseDelim(Delimiter::Brace) => "}".into(),
-            token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible) => {
-                "".into()
-            }
+            token::OpenDelim(Delimiter::Invisible) => "/*«*/".into(),
+            token::CloseDelim(Delimiter::Invisible) => "/*»*/".into(),
             token::Pound => "#".into(),
             token::Dollar => "$".into(),
             token::Question => "?".into(),
index 4a9904891ecad14ac4cac39b2beaea351e538f7a..7c921b850582362244a89cc297c2179805e3528b 100644 (file)
@@ -167,10 +167,6 @@ pub fn build(
     crate fn get_index_of(&self, location: &Location) -> Option<BorrowIndex> {
         self.location_map.get_index_of(location).map(BorrowIndex::from)
     }
-
-    crate fn contains(&self, location: &Location) -> bool {
-        self.location_map.contains_key(location)
-    }
 }
 
 struct GatherBorrows<'a, 'tcx> {
index 533439a24aa4f8720dac45ac6b8e88fa331e1187..4661805bb1c347d5ac7eb2fff0b9a35a382d869d 100644 (file)
 use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt};
-use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT};
-use rustc_span::{Span, Symbol, DUMMY_SP};
+use rustc_session::lint::builtin::UNUSED_MUT;
+use rustc_span::{Span, Symbol};
 
 use either::Either;
 use smallvec::SmallVec;
 use std::cell::RefCell;
 use std::collections::BTreeMap;
-use std::mem;
 use std::rc::Rc;
 
 use rustc_mir_dataflow::impls::{
@@ -312,7 +311,6 @@ fn do_mir_borrowck<'a, 'tcx>(
                 locals_are_invalidated_at_exit,
                 access_place_error_reported: Default::default(),
                 reservation_error_reported: Default::default(),
-                reservation_warnings: Default::default(),
                 uninitialized_error_reported: Default::default(),
                 regioncx: regioncx.clone(),
                 used_mut: Default::default(),
@@ -344,7 +342,6 @@ fn do_mir_borrowck<'a, 'tcx>(
         fn_self_span_reported: Default::default(),
         access_place_error_reported: Default::default(),
         reservation_error_reported: Default::default(),
-        reservation_warnings: Default::default(),
         uninitialized_error_reported: Default::default(),
         regioncx: Rc::clone(&regioncx),
         used_mut: Default::default(),
@@ -377,34 +374,6 @@ fn do_mir_borrowck<'a, 'tcx>(
         &mut mbcx,
     );
 
-    // Convert any reservation warnings into lints.
-    let reservation_warnings = mem::take(&mut mbcx.reservation_warnings);
-    for (_, (place, span, location, bk, borrow)) in reservation_warnings {
-        let initial_diag = mbcx.report_conflicting_borrow(location, (place, span), bk, &borrow);
-
-        let scope = mbcx.body.source_info(location).scope;
-        let lint_root = match &mbcx.body.source_scopes[scope].local_data {
-            ClearCrossCrate::Set(data) => data.lint_root,
-            _ => tcx.hir().local_def_id_to_hir_id(def.did),
-        };
-
-        // Span and message don't matter; we overwrite them below anyway
-        mbcx.infcx.tcx.struct_span_lint_hir(
-            MUTABLE_BORROW_RESERVATION_CONFLICT,
-            lint_root,
-            DUMMY_SP,
-            |lint| {
-                let mut diag = lint.build("");
-
-                diag.message = initial_diag.styled_message().clone();
-                diag.span = initial_diag.span.clone();
-
-                mbcx.buffer_non_error_diag(diag);
-            },
-        );
-        initial_diag.cancel();
-    }
-
     // For each non-user used mutable variable, check if it's been assigned from
     // a user-declared local. If so, then put that local into the used_mut set.
     // Note that this set is expected to be small - only upvars from closures
@@ -539,11 +508,6 @@ struct MirBorrowckCtxt<'cx, 'tcx> {
     /// used to report extra information for `FnSelfUse`, to avoid
     /// unnecessarily verbose errors.
     fn_self_span_reported: FxHashSet<Span>,
-    /// Migration warnings to be reported for #56254. We delay reporting these
-    /// so that we can suppress the warning if there's a corresponding error
-    /// for the activation of the borrow.
-    reservation_warnings:
-        FxHashMap<BorrowIndex, (Place<'tcx>, Span, Location, BorrowKind, BorrowData<'tcx>)>,
     /// This field keeps track of errors reported in the checking of uninitialized variables,
     /// so that we don't report seemingly duplicate errors.
     uninitialized_error_reported: FxHashSet<PlaceRef<'tcx>>,
@@ -995,12 +959,6 @@ fn access_place(
         let conflict_error =
             self.check_access_for_conflict(location, place_span, sd, rw, flow_state);
 
-        if let (Activation(_, borrow_idx), true) = (kind.1, conflict_error) {
-            // Suppress this warning when there's an error being emitted for the
-            // same borrow: fixing the error is likely to fix the warning.
-            self.reservation_warnings.remove(&borrow_idx);
-        }
-
         if conflict_error || mutability_error {
             debug!("access_place: logging error place_span=`{:?}` kind=`{:?}`", place_span, kind);
             self.access_place_error_reported.insert((place_span.0, place_span.1));
@@ -1067,6 +1025,12 @@ fn check_access_for_conflict(
                     BorrowKind::Unique | BorrowKind::Mut { .. },
                 ) => Control::Continue,
 
+                (Reservation(_), BorrowKind::Shallow | BorrowKind::Shared) => {
+                    // This used to be a future compatibility warning (to be
+                    // disallowed on NLL). See rust-lang/rust#56254
+                    Control::Continue
+                }
+
                 (Write(WriteKind::Move), BorrowKind::Shallow) => {
                     // Handled by initialization checks.
                     Control::Continue
@@ -1095,27 +1059,6 @@ fn check_access_for_conflict(
                     Control::Break
                 }
 
-                (
-                    Reservation(WriteKind::MutableBorrow(bk)),
-                    BorrowKind::Shallow | BorrowKind::Shared,
-                ) if { tcx.migrate_borrowck() && this.borrow_set.contains(&location) } => {
-                    let bi = this.borrow_set.get_index_of(&location).unwrap();
-                    debug!(
-                        "recording invalid reservation of place: {:?} with \
-                         borrow index {:?} as warning",
-                        place_span.0, bi,
-                    );
-                    // rust-lang/rust#56254 - This was previously permitted on
-                    // the 2018 edition so we emit it as a warning. We buffer
-                    // these separately so that we only emit a warning if borrow
-                    // checking was otherwise successful.
-                    this.reservation_warnings
-                        .insert(bi, (place_span.0, place_span.1, location, bk, borrow.clone()));
-
-                    // Don't suppress actual errors.
-                    Control::Continue
-                }
-
                 (Reservation(kind) | Activation(kind, _) | Write(kind), _) => {
                     match rw {
                         Reservation(..) => {
index 2817fce463b91c5a870c71ae2d140a86dbcb7875..8bf3a0799b6384d3c74f2feae235e418153297c6 100644 (file)
@@ -3,17 +3,15 @@
 use rustc_ast::token;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast_pretty::pprust;
-use rustc_errors::PResult;
 use rustc_expand::base::{self, *};
 use rustc_expand::module::DirOwnership;
 use rustc_parse::parser::{ForceCollect, Parser};
 use rustc_parse::{self, new_parser_from_file};
 use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
 use rustc_span::symbol::Symbol;
-use rustc_span::{self, FileName, Pos, Span};
+use rustc_span::{self, Pos, Span};
 
 use smallvec::SmallVec;
-use std::path::PathBuf;
 use std::rc::Rc;
 
 // These macros all relate to the file system; they either return
@@ -104,7 +102,7 @@ pub fn expand_include<'cx>(
         return DummyResult::any(sp);
     };
     // The file will be added to the code map by the parser
-    let file = match resolve_path(cx, file.as_str(), sp) {
+    let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
         Ok(f) => f,
         Err(mut err) => {
             err.emit();
@@ -176,7 +174,7 @@ pub fn expand_include_str(
     let Some(file) = get_single_str_from_tts(cx, sp, tts, "include_str!") else {
         return DummyResult::any(sp);
     };
-    let file = match resolve_path(cx, file.as_str(), sp) {
+    let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
         Ok(f) => f,
         Err(mut err) => {
             err.emit();
@@ -210,7 +208,7 @@ pub fn expand_include_bytes(
     let Some(file) = get_single_str_from_tts(cx, sp, tts, "include_bytes!") else {
         return DummyResult::any(sp);
     };
-    let file = match resolve_path(cx, file.as_str(), sp) {
+    let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
         Ok(f) => f,
         Err(mut err) => {
             err.emit();
@@ -225,40 +223,3 @@ pub fn expand_include_bytes(
         }
     }
 }
-
-/// Resolves a `path` mentioned inside Rust code, returning an absolute path.
-///
-/// This unifies the logic used for resolving `include_X!`.
-fn resolve_path<'a>(
-    cx: &mut ExtCtxt<'a>,
-    path: impl Into<PathBuf>,
-    span: Span,
-) -> PResult<'a, PathBuf> {
-    let path = path.into();
-
-    // Relative paths are resolved relative to the file in which they are found
-    // after macro expansion (that is, they are unhygienic).
-    if !path.is_absolute() {
-        let callsite = span.source_callsite();
-        let mut result = match cx.source_map().span_to_filename(callsite) {
-            FileName::Real(name) => name
-                .into_local_path()
-                .expect("attempting to resolve a file path in an external file"),
-            FileName::DocTest(path, _) => path,
-            other => {
-                return Err(cx.struct_span_err(
-                    span,
-                    &format!(
-                        "cannot resolve relative path in non-file source `{}`",
-                        cx.source_map().filename_for_diagnostics(&other)
-                    ),
-                ));
-            }
-        };
-        result.pop();
-        result.push(path);
-        Ok(result)
-    } else {
-        Ok(path)
-    }
-}
index 6887f0666a4b037399c4ed022fe27fcd7ff095aa..04ec1e7f3c1afe5e92b22db5fe2efd04aa86c249 100644 (file)
@@ -5,7 +5,7 @@
 use rustc_data_structures::temp_dir::MaybeTempDir;
 use rustc_errors::{ErrorGuaranteed, Handler};
 use rustc_fs_util::fix_windows_verbatim_for_gcc;
-use rustc_hir::def_id::CrateNum;
+use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
 use rustc_middle::middle::dependency_format::Linkage;
 use rustc_middle::middle::exported_symbols::SymbolExportKind;
 use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
@@ -2099,8 +2099,14 @@ fn add_order_independent_options(
     // Pass optimization flags down to the linker.
     cmd.optimize();
 
+    let debugger_visualizer_paths = if sess.target.is_like_msvc {
+        collect_debugger_visualizers(tmpdir, sess, &codegen_results.crate_info)
+    } else {
+        Vec::new()
+    };
+
     // Pass debuginfo and strip flags down to the linker.
-    cmd.debuginfo(strip_value(sess));
+    cmd.debuginfo(strip_value(sess), &debugger_visualizer_paths);
 
     // We want to prevent the compiler from accidentally leaking in any system libraries,
     // so by default we tell linkers not to link to any default libraries.
@@ -2119,6 +2125,47 @@ fn add_order_independent_options(
     add_rpath_args(cmd, sess, codegen_results, out_filename);
 }
 
+// Write the debugger visualizer files for each crate to the temp directory and gather the file paths.
+fn collect_debugger_visualizers(
+    tmpdir: &Path,
+    sess: &Session,
+    crate_info: &CrateInfo,
+) -> Vec<PathBuf> {
+    let mut visualizer_paths = Vec::new();
+    let debugger_visualizers = &crate_info.debugger_visualizers;
+    let mut index = 0;
+
+    for (&cnum, visualizers) in debugger_visualizers {
+        let crate_name = if cnum == LOCAL_CRATE {
+            crate_info.local_crate_name.as_str()
+        } else {
+            crate_info.crate_name[&cnum].as_str()
+        };
+
+        for visualizer in visualizers {
+            let visualizer_out_file = tmpdir.join(format!("{}-{}.natvis", crate_name, index));
+
+            match fs::write(&visualizer_out_file, &visualizer.src) {
+                Ok(()) => {
+                    visualizer_paths.push(visualizer_out_file.clone());
+                    index += 1;
+                }
+                Err(error) => {
+                    sess.warn(
+                        format!(
+                            "Unable to write debugger visualizer file `{}`: {} ",
+                            visualizer_out_file.display(),
+                            error
+                        )
+                        .as_str(),
+                    );
+                }
+            };
+        }
+    }
+    visualizer_paths
+}
+
 /// # Native library linking
 ///
 /// User-supplied library search paths (-L on the command line). These are the same paths used to
index 50db2c22ae625aff807225c65b792a805eefa71a..2a71377d2f15b353dcb00191d58988f3c25c1ba6 100644 (file)
@@ -183,7 +183,7 @@ pub trait Linker {
     fn optimize(&mut self);
     fn pgo_gen(&mut self);
     fn control_flow_guard(&mut self);
-    fn debuginfo(&mut self, strip: Strip);
+    fn debuginfo(&mut self, strip: Strip, debugger_visualizers: &[PathBuf]);
     fn no_crt_objects(&mut self);
     fn no_default_libraries(&mut self);
     fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]);
@@ -611,7 +611,7 @@ fn pgo_gen(&mut self) {
 
     fn control_flow_guard(&mut self) {}
 
-    fn debuginfo(&mut self, strip: Strip) {
+    fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
         // MacOS linker doesn't support stripping symbols directly anymore.
         if self.sess.target.is_like_osx {
             return;
@@ -915,7 +915,7 @@ fn control_flow_guard(&mut self) {
         self.cmd.arg("/guard:cf");
     }
 
-    fn debuginfo(&mut self, strip: Strip) {
+    fn debuginfo(&mut self, strip: Strip, debugger_visualizers: &[PathBuf]) {
         match strip {
             Strip::None => {
                 // This will cause the Microsoft linker to generate a PDB file
@@ -942,6 +942,13 @@ fn debuginfo(&mut self, strip: Strip) {
                         }
                     }
                 }
+
+                // This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file
+                for path in debugger_visualizers {
+                    let mut arg = OsString::from("/NATVIS:");
+                    arg.push(path);
+                    self.cmd.arg(arg);
+                }
             }
             Strip::Debuginfo | Strip::Symbols => {
                 self.cmd.arg("/DEBUG:NONE");
@@ -1124,7 +1131,7 @@ fn pgo_gen(&mut self) {
 
     fn control_flow_guard(&mut self) {}
 
-    fn debuginfo(&mut self, _strip: Strip) {
+    fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
         // Preserve names or generate source maps depending on debug info
         self.cmd.arg(match self.sess.opts.debuginfo {
             DebugInfo::None => "-g0",
@@ -1315,7 +1322,7 @@ fn optimize(&mut self) {
 
     fn pgo_gen(&mut self) {}
 
-    fn debuginfo(&mut self, strip: Strip) {
+    fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
         match strip {
             Strip::None => {}
             Strip::Debuginfo => {
@@ -1450,7 +1457,7 @@ fn optimize(&mut self) {
 
     fn pgo_gen(&mut self) {}
 
-    fn debuginfo(&mut self, strip: Strip) {
+    fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
         match strip {
             Strip::None => {}
             Strip::Debuginfo => {
@@ -1600,7 +1607,7 @@ fn include_path(&mut self, path: &Path) {
         self.cmd.arg("-L").arg(path);
     }
 
-    fn debuginfo(&mut self, _strip: Strip) {
+    fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
         self.cmd.arg("--debug");
     }
 
@@ -1699,7 +1706,7 @@ fn include_path(&mut self, path: &Path) {
         self.cmd.arg("-L").arg(path);
     }
 
-    fn debuginfo(&mut self, _strip: Strip) {
+    fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
         self.cmd.arg("--debug");
     }
 
index 5bc95614c197c32b2a4a48b3110cce816ad72e00..7b7e09208a24a0c94cbf2afcf6e6f9d2a9bcd416 100644 (file)
@@ -847,7 +847,13 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo {
             missing_lang_items: Default::default(),
             dependency_formats: tcx.dependency_formats(()).clone(),
             windows_subsystem,
+            debugger_visualizers: Default::default(),
         };
+        let debugger_visualizers = tcx.debugger_visualizers(LOCAL_CRATE).clone();
+        if !debugger_visualizers.is_empty() {
+            info.debugger_visualizers.insert(LOCAL_CRATE, debugger_visualizers);
+        }
+
         let lang_items = tcx.lang_items();
 
         let crates = tcx.crates(());
@@ -862,7 +868,9 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo {
             info.native_libraries
                 .insert(cnum, tcx.native_libraries(cnum).iter().map(Into::into).collect());
             info.crate_name.insert(cnum, tcx.crate_name(cnum));
-            info.used_crate_source.insert(cnum, tcx.used_crate_source(cnum).clone());
+
+            let used_crate_source = tcx.used_crate_source(cnum);
+            info.used_crate_source.insert(cnum, used_crate_source.clone());
             if tcx.is_compiler_builtins(cnum) {
                 info.compiler_builtins = Some(cnum);
             }
@@ -883,6 +891,14 @@ pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo {
             let missing =
                 missing.iter().cloned().filter(|&l| lang_items::required(tcx, l)).collect();
             info.missing_lang_items.insert(cnum, missing);
+
+            // Only include debugger visualizer files from crates that will be statically linked.
+            if used_crate_source.rlib.is_some() || used_crate_source.rmeta.is_some() {
+                let debugger_visualizers = tcx.debugger_visualizers(cnum).clone();
+                if !debugger_visualizers.is_empty() {
+                    info.debugger_visualizers.insert(cnum, debugger_visualizers);
+                }
+            }
         }
 
         info
index b7bee9ab805a422d8b9657edfce8971012f35475..9e1fe588c53799234650d508a86ec34fc6b5333e 100644 (file)
@@ -35,6 +35,7 @@
 use rustc_session::cstore::{self, CrateSource};
 use rustc_session::utils::NativeLibKind;
 use rustc_span::symbol::Symbol;
+use rustc_span::DebuggerVisualizerFile;
 use std::path::{Path, PathBuf};
 
 pub mod back;
@@ -156,6 +157,7 @@ pub struct CrateInfo {
     pub missing_lang_items: FxHashMap<CrateNum, Vec<LangItem>>,
     pub dependency_formats: Lrc<Dependencies>,
     pub windows_subsystem: Option<String>,
+    pub debugger_visualizers: FxHashMap<CrateNum, Vec<DebuggerVisualizerFile>>,
 }
 
 #[derive(Encodable, Decodable)]
index f2d833b320249f735487bf915794e94a4d661bee..a8a5ac2f9d95d4b6aae2cf0d801890322edd7c42 100644 (file)
@@ -84,14 +84,18 @@ pub fn to_scalar(self) -> InterpResult<'tcx, Scalar<Tag>> {
     }
 
     #[inline]
-    pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Tag>, Scalar<Tag>)> {
+    pub fn to_scalar_or_uninit_pair(self) -> (ScalarMaybeUninit<Tag>, ScalarMaybeUninit<Tag>) {
         match self {
-            Immediate::ScalarPair(val1, val2) => Ok((val1.check_init()?, val2.check_init()?)),
-            Immediate::Scalar(..) => {
-                bug!("Got a scalar where a scalar pair was expected")
-            }
+            Immediate::ScalarPair(val1, val2) => (val1, val2),
+            Immediate::Scalar(..) => bug!("Got a scalar where a scalar pair was expected"),
         }
     }
+
+    #[inline]
+    pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Tag>, Scalar<Tag>)> {
+        let (val1, val2) = self.to_scalar_or_uninit_pair();
+        Ok((val1.check_init()?, val2.check_init()?))
+    }
 }
 
 // ScalarPair needs a type to interpret, so we often have an immediate and a type together
@@ -248,9 +252,12 @@ pub fn to_const_int(self) -> ConstInt {
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`.
     /// Returns `None` if the layout does not permit loading this as a value.
-    fn try_read_immediate_from_mplace(
+    ///
+    /// This is an internal function; call `read_immediate` instead.
+    fn read_immediate_from_mplace_raw(
         &self,
         mplace: &MPlaceTy<'tcx, M::PointerTag>,
+        force: bool,
     ) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::PointerTag>>> {
         if mplace.layout.is_unsized() {
             // Don't touch unsized
@@ -271,42 +278,61 @@ fn try_read_immediate_from_mplace(
         // case where some of the bytes are initialized and others are not. So, we need an extra
         // check that walks over the type of `mplace` to make sure it is truly correct to treat this
         // like a `Scalar` (or `ScalarPair`).
-        match mplace.layout.abi {
-            Abi::Scalar(abi::Scalar::Initialized { .. }) => {
-                let scalar = alloc.read_scalar(alloc_range(Size::ZERO, mplace.layout.size))?;
-                Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout }))
-            }
+        let scalar_layout = match mplace.layout.abi {
+            // `if` does not work nested inside patterns, making this a bit awkward to express.
+            Abi::Scalar(abi::Scalar::Initialized { value: s, .. }) => Some(s),
+            Abi::Scalar(s) if force => Some(s.primitive()),
+            _ => None,
+        };
+        if let Some(_) = scalar_layout {
+            let scalar = alloc.read_scalar(alloc_range(Size::ZERO, mplace.layout.size))?;
+            return Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout }));
+        }
+        let scalar_pair_layout = match mplace.layout.abi {
             Abi::ScalarPair(
                 abi::Scalar::Initialized { value: a, .. },
                 abi::Scalar::Initialized { value: b, .. },
-            ) => {
-                // We checked `ptr_align` above, so all fields will have the alignment they need.
-                // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
-                // which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
-                let (a_size, b_size) = (a.size(self), b.size(self));
-                let b_offset = a_size.align_to(b.align(self).abi);
-                assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields
-                let a_val = alloc.read_scalar(alloc_range(Size::ZERO, a_size))?;
-                let b_val = alloc.read_scalar(alloc_range(b_offset, b_size))?;
-                Ok(Some(ImmTy { imm: Immediate::ScalarPair(a_val, b_val), layout: mplace.layout }))
-            }
-            _ => Ok(None),
+            ) => Some((a, b)),
+            Abi::ScalarPair(a, b) if force => Some((a.primitive(), b.primitive())),
+            _ => None,
+        };
+        if let Some((a, b)) = scalar_pair_layout {
+            // We checked `ptr_align` above, so all fields will have the alignment they need.
+            // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
+            // which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
+            let (a_size, b_size) = (a.size(self), b.size(self));
+            let b_offset = a_size.align_to(b.align(self).abi);
+            assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields
+            let a_val = alloc.read_scalar(alloc_range(Size::ZERO, a_size))?;
+            let b_val = alloc.read_scalar(alloc_range(b_offset, b_size))?;
+            return Ok(Some(ImmTy {
+                imm: Immediate::ScalarPair(a_val, b_val),
+                layout: mplace.layout,
+            }));
         }
+        // Neither a scalar nor scalar pair.
+        return Ok(None);
     }
 
-    /// Try returning an immediate for the operand.
-    /// If the layout does not permit loading this as an immediate, return where in memory
-    /// we can find the data.
+    /// Try returning an immediate for the operand. If the layout does not permit loading this as an
+    /// immediate, return where in memory we can find the data.
     /// Note that for a given layout, this operation will either always fail or always
     /// succeed!  Whether it succeeds depends on whether the layout can be represented
     /// in an `Immediate`, not on which data is stored there currently.
-    pub fn try_read_immediate(
+    ///
+    /// If `force` is `true`, then even scalars with fields that can be ununit will be
+    /// read. This means the load is lossy and should not be written back!
+    /// This flag exists only for validity checking.
+    ///
+    /// This is an internal function that should not usually be used; call `read_immediate` instead.
+    pub fn read_immediate_raw(
         &self,
         src: &OpTy<'tcx, M::PointerTag>,
+        force: bool,
     ) -> InterpResult<'tcx, Result<ImmTy<'tcx, M::PointerTag>, MPlaceTy<'tcx, M::PointerTag>>> {
         Ok(match src.try_as_mplace() {
             Ok(ref mplace) => {
-                if let Some(val) = self.try_read_immediate_from_mplace(mplace)? {
+                if let Some(val) = self.read_immediate_from_mplace_raw(mplace, force)? {
                     Ok(val)
                 } else {
                     Err(*mplace)
@@ -322,7 +348,7 @@ pub fn read_immediate(
         &self,
         op: &OpTy<'tcx, M::PointerTag>,
     ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> {
-        if let Ok(imm) = self.try_read_immediate(op)? {
+        if let Ok(imm) = self.read_immediate_raw(op, /*force*/ false)? {
             Ok(imm)
         } else {
             span_bug!(self.cur_span(), "primitive read failed for type: {:?}", op.layout.ty);
index 380eb5263618b5f26b6bba3681e4d44c770d5704..df6e05bb13cde81b839894c3f2496d1a71c03123 100644 (file)
@@ -720,7 +720,7 @@ fn write_immediate_no_validate(
         }
         trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty);
 
-        // See if we can avoid an allocation. This is the counterpart to `try_read_immediate`,
+        // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
         // but not factored as a separate function.
         let mplace = match dest.place {
             Place::Local { frame, local } => {
@@ -879,7 +879,7 @@ fn copy_op_no_validate(
         }
 
         // Let us see if the layout is simple so we take a shortcut, avoid force_allocation.
-        let src = match self.try_read_immediate(src)? {
+        let src = match self.read_immediate_raw(src, /*force*/ false)? {
             Ok(src_val) => {
                 assert!(!src.layout.is_unsized(), "cannot have unsized immediates");
                 // Yay, we got a value that we can write directly.
index 71d29be97d5ec38e99b544bee0532d2c58ad4400..92e3ac04dc41813522fd6266356d9611f85876f2 100644 (file)
@@ -20,8 +20,8 @@
 use std::hash::Hash;
 
 use super::{
-    alloc_range, CheckInAllocMsg, GlobalAlloc, InterpCx, InterpResult, MPlaceTy, Machine,
-    MemPlaceMeta, OpTy, Scalar, ScalarMaybeUninit, ValueVisitor,
+    alloc_range, CheckInAllocMsg, GlobalAlloc, Immediate, InterpCx, InterpResult, MPlaceTy,
+    Machine, MemPlaceMeta, OpTy, Scalar, ScalarMaybeUninit, ValueVisitor,
 };
 
 macro_rules! throw_validation_failure {
@@ -487,6 +487,17 @@ fn read_scalar(
         ))
     }
 
+    fn read_immediate_forced(
+        &self,
+        op: &OpTy<'tcx, M::PointerTag>,
+    ) -> InterpResult<'tcx, Immediate<M::PointerTag>> {
+        Ok(*try_validation!(
+            self.ecx.read_immediate_raw(op, /*force*/ true),
+            self.path,
+            err_unsup!(ReadPointerAsBytes) => { "(potentially part of) a pointer" } expected { "plain (non-pointer) bytes" },
+        ).unwrap())
+    }
+
     /// Check if this is a value of primitive type, and if yes check the validity of the value
     /// at that type.  Return `true` if the type is indeed primitive.
     fn try_visit_primitive(
@@ -626,18 +637,19 @@ fn try_visit_primitive(
 
     fn visit_scalar(
         &mut self,
-        op: &OpTy<'tcx, M::PointerTag>,
+        scalar: ScalarMaybeUninit<M::PointerTag>,
         scalar_layout: ScalarAbi,
     ) -> InterpResult<'tcx> {
         // We check `is_full_range` in a slightly complicated way because *if* we are checking
         // number validity, then we want to ensure that `Scalar::Initialized` is indeed initialized,
         // i.e. that we go over the `check_init` below.
+        let size = scalar_layout.size(self.ecx);
         let is_full_range = match scalar_layout {
             ScalarAbi::Initialized { valid_range, .. } => {
                 if M::enforce_number_validity(self.ecx) {
                     false // not "full" since uninit is not accepted
                 } else {
-                    valid_range.is_full_for(op.layout.size)
+                    valid_range.is_full_for(size)
                 }
             }
             ScalarAbi::Union { .. } => true,
@@ -646,21 +658,19 @@ fn visit_scalar(
             // Nothing to check
             return Ok(());
         }
-        // We have something to check.
+        // We have something to check: it must at least be initialized.
         let valid_range = scalar_layout.valid_range(self.ecx);
         let WrappingRange { start, end } = valid_range;
-        let max_value = op.layout.size.unsigned_int_max();
+        let max_value = size.unsigned_int_max();
         assert!(end <= max_value);
-        // Determine the allowed range
-        let value = self.read_scalar(op)?;
         let value = try_validation!(
-            value.check_init(),
+            scalar.check_init(),
             self.path,
-            err_ub!(InvalidUninitBytes(None)) => { "{:x}", value }
+            err_ub!(InvalidUninitBytes(None)) => { "{:x}", scalar }
                 expected { "something {}", wrapping_range_format(valid_range, max_value) },
         );
         let bits = match value.try_to_int() {
-            Ok(int) => int.assert_bits(op.layout.size),
+            Ok(int) => int.assert_bits(size),
             Err(_) => {
                 // So this is a pointer then, and casting to an int failed.
                 // Can only happen during CTFE.
@@ -678,7 +688,7 @@ fn visit_scalar(
                     } else {
                         return Ok(());
                     }
-                } else if scalar_layout.valid_range(self.ecx).is_full_for(op.layout.size) {
+                } else if scalar_layout.valid_range(self.ecx).is_full_for(size) {
                     // Easy. (This is reachable if `enforce_number_validity` is set.)
                     return Ok(());
                 } else {
@@ -817,13 +827,23 @@ fn visit_value(&mut self, op: &OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx>
                 );
             }
             Abi::Scalar(scalar_layout) => {
-                self.visit_scalar(op, scalar_layout)?;
+                let scalar = self.read_immediate_forced(op)?.to_scalar_or_uninit();
+                self.visit_scalar(scalar, scalar_layout)?;
+            }
+            Abi::ScalarPair(a_layout, b_layout) => {
+                // We would validate these things as we descend into the fields,
+                // but that can miss bugs in layout computation. Layout computation
+                // is subtle due to enums having ScalarPair layout, where one field
+                // is the discriminant.
+                if cfg!(debug_assertions) {
+                    let (a, b) = self.read_immediate_forced(op)?.to_scalar_or_uninit_pair();
+                    self.visit_scalar(a, a_layout)?;
+                    self.visit_scalar(b, b_layout)?;
+                }
             }
-            Abi::ScalarPair { .. } | Abi::Vector { .. } => {
-                // These have fields that we already visited above, so we already checked
-                // all their scalar-level restrictions.
-                // There is also no equivalent to `rustc_layout_scalar_valid_range_start`
-                // that would make skipping them here an issue.
+            Abi::Vector { .. } => {
+                // No checks here, we assume layout computation gets this right.
+                // (This is harder to check since Miri does not represent these as `Immediate`.)
             }
             Abi::Aggregate { .. } => {
                 // Nothing to do.
index 5d42f8c9306d9782e5564096c195c0c77fa82ea9..76ae17f28c6132c8f2d5618062c2162736d31337 100644 (file)
@@ -17,6 +17,7 @@
 #![feature(generators)]
 #![feature(let_else)]
 #![feature(hash_raw_entry)]
+#![feature(hasher_prefixfree_extras)]
 #![feature(maybe_uninit_uninit_array)]
 #![feature(min_specialization)]
 #![feature(never_type)]
index 6e5c0617bf34cb089c3942c5ca4831d874e7129b..abd25f46ad54fa2d60b065e6fddcbc71b74dcb7f 100644 (file)
@@ -462,6 +462,14 @@ fn write(&mut self, msg: &[u8]) {
         self.slice_write(msg);
     }
 
+    #[inline]
+    fn write_str(&mut self, s: &str) {
+        // This hasher works byte-wise, and `0xFF` cannot show up in a `str`,
+        // so just hashing the one extra byte is enough to be prefix-free.
+        self.write(s.as_bytes());
+        self.write_u8(0xFF);
+    }
+
     fn finish(&self) -> u64 {
         panic!("SipHasher128 cannot provide valid 64 bit hashes")
     }
index 25353290fd50c4943c018554102791388342163d..c8bb4fc5e6af6f2cbccfd9aa78b705e2bbda15a5 100644 (file)
@@ -73,6 +73,17 @@ fn write(&mut self, bytes: &[u8]) {
         self.state.write(bytes);
     }
 
+    #[inline]
+    fn write_str(&mut self, s: &str) {
+        self.state.write_str(s);
+    }
+
+    #[inline]
+    fn write_length_prefix(&mut self, len: usize) {
+        // Our impl for `usize` will extend it if needed.
+        self.write_usize(len);
+    }
+
     #[inline]
     fn write_u8(&mut self, i: u8) {
         self.state.write_u8(i);
index ae1b50a4176873ef2900182fa247039af2a9ecbd..2b30ec601a0ceccfa4dea30965f47740a3243e51 100644 (file)
@@ -10,7 +10,7 @@
 use rustc_attr::{self as attr, Deprecation, Stability};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::{self, Lrc};
-use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
+use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, PResult};
 use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
 use rustc_lint_defs::BuiltinLintDiagnostics;
 use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS};
@@ -20,7 +20,7 @@
 use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId};
 use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::{FileName, Span, DUMMY_SP};
 use smallvec::{smallvec, SmallVec};
 
 use std::default::Default;
@@ -1136,6 +1136,43 @@ pub fn check_unused_macros(&mut self) {
     }
 }
 
+/// Resolves a `path` mentioned inside Rust code, returning an absolute path.
+///
+/// This unifies the logic used for resolving `include_X!`.
+pub fn resolve_path(
+    parse_sess: &ParseSess,
+    path: impl Into<PathBuf>,
+    span: Span,
+) -> PResult<'_, PathBuf> {
+    let path = path.into();
+
+    // Relative paths are resolved relative to the file in which they are found
+    // after macro expansion (that is, they are unhygienic).
+    if !path.is_absolute() {
+        let callsite = span.source_callsite();
+        let mut result = match parse_sess.source_map().span_to_filename(callsite) {
+            FileName::Real(name) => name
+                .into_local_path()
+                .expect("attempting to resolve a file path in an external file"),
+            FileName::DocTest(path, _) => path,
+            other => {
+                return Err(parse_sess.span_diagnostic.struct_span_err(
+                    span,
+                    &format!(
+                        "cannot resolve relative path in non-file source `{}`",
+                        parse_sess.source_map().filename_for_diagnostics(&other)
+                    ),
+                ));
+            }
+        };
+        result.pop();
+        result.push(path);
+        Ok(result)
+    } else {
+        Ok(path)
+    }
+}
+
 /// Extracts a string literal from the macro expanded version of `expr`,
 /// returning a diagnostic error of `err_msg` if `expr` is not a string literal.
 /// The returned bool indicates whether an applicable suggestion has already been
index 9159d60463c4c003e5cf8ba0a83db1b441f79b7e..5c07d9121cc54c015331a4b035930f747ca09098 100644 (file)
@@ -358,6 +358,8 @@ pub fn set(&self, features: &mut Features, span: Span) {
     (active, custom_inner_attributes, "1.30.0", Some(54726), None),
     /// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`.
     (active, custom_test_frameworks, "1.30.0", Some(50297), None),
+    /// Allows using `#[debugger_visualizer]`.
+    (active, debugger_visualizer, "1.62.0", Some(95939), None),
     /// Allows declarative macros 2.0 (`macro`).
     (active, decl_macro, "1.17.0", Some(39412), None),
     /// Allows rustc to inject a default alloc_error_handler
index e588385cfca03d6da6c72f3812a26e4d1ab329ca..33d2e82b24a26c06d8f7586de88c3b14990fa183 100644 (file)
@@ -379,6 +379,12 @@ pub struct BuiltinAttribute {
     // Unstable attributes:
     // ==========================================================================
 
+    // RFC #3191: #[debugger_visualizer] support
+    gated!(
+        debugger_visualizer, Normal, template!(List: r#"natvis_file = "...""#),
+        DuplicatesOk, experimental!(debugger_visualizer)
+    ),
+
     // Linking:
     gated!(naked, Normal, template!(Word), WarnFollowing, naked_functions, experimental!(naked)),
     gated!(
@@ -644,6 +650,11 @@ pub struct BuiltinAttribute {
         rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing,
         "#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."
     ),
+    rustc_attr!(
+        rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing,
+        "#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \
+         the given type by annotating all impl items with #[rustc_allow_incoherent_impl]."
+    ),
     BuiltinAttribute {
         name: sym::rustc_diagnostic_item,
         type_: Normal,
index b3c22d4ec213d3864f0aaa3972b0ef6603fecf85..9e5d781892487b0fccf657ac8c354a8e7836df39 100644 (file)
@@ -327,8 +327,6 @@ pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
     Range,                   sym::Range,               range_struct,               Target::Struct,         GenericRequirement::None;
     RangeToInclusive,        sym::RangeToInclusive,    range_to_inclusive_struct,  Target::Struct,         GenericRequirement::None;
     RangeTo,                 sym::RangeTo,             range_to_struct,            Target::Struct,         GenericRequirement::None;
-
-    CStr,                    sym::CStr,                c_str,                      Target::Struct,         GenericRequirement::None;
 }
 
 pub enum GenericRequirement {
index 54b08dfe840bf4584882ad9e2392849243d79162..028c14366c6adefa7d5abc482403fe475617643d 100644 (file)
@@ -490,6 +490,11 @@ macro_rules! register_passes {
         "converted into hard error, see RFC 2972 \
          <https://github.com/rust-lang/rfcs/blob/master/text/2972-constrained-naked.md> for more information",
     );
+    store.register_removed(
+        "mutable_borrow_reservation_conflict",
+        "now allowed, see issue #59159 \
+         <https://github.com/rust-lang/rust/issues/59159> for more information",
+    );
 }
 
 fn register_internals(store: &mut LintStore) {
index a42e3d5d9578502a216ce6624e676498139ac99c..34412795aeff0d7b1e3a2ffe187ef84f4dd1ca16 100644 (file)
     };
 }
 
-declare_lint! {
-    /// The `mutable_borrow_reservation_conflict` lint detects the reservation
-    /// of a two-phased borrow that conflicts with other shared borrows.
-    ///
-    /// ### Example
-    ///
-    /// ```rust
-    /// let mut v = vec![0, 1, 2];
-    /// let shared = &v;
-    /// v.push(shared.len());
-    /// ```
-    ///
-    /// {{produces}}
-    ///
-    /// ### Explanation
-    ///
-    /// This is a [future-incompatible] lint to transition this to a hard error
-    /// in the future. See [issue #59159] for a complete description of the
-    /// problem, and some possible solutions.
-    ///
-    /// [issue #59159]: https://github.com/rust-lang/rust/issues/59159
-    /// [future-incompatible]: ../index.md#future-incompatible-lints
-    pub MUTABLE_BORROW_RESERVATION_CONFLICT,
-    Warn,
-    "reservation of a two-phased borrow conflicts with other shared borrows",
-    @future_incompatible = FutureIncompatibleInfo {
-        reason: FutureIncompatibilityReason::Custom(
-            "this borrowing pattern was not meant to be accepted, \
-            and may become a hard error in the future"
-        ),
-        reference: "issue #59159 <https://github.com/rust-lang/rust/issues/59159>",
-    };
-}
-
 declare_lint! {
     /// The `soft_unstable` lint detects unstable features that were
     /// unintentionally allowed on stable.
         META_VARIABLE_MISUSE,
         DEPRECATED_IN_FUTURE,
         AMBIGUOUS_ASSOCIATED_ITEMS,
-        MUTABLE_BORROW_RESERVATION_CONFLICT,
         INDIRECT_STRUCTURAL_MATCH,
         POINTER_STRUCTURAL_MATCH,
         NONTRIVIAL_STRUCTURAL_MATCH,
index 1edb62e189f9768c3b8dbc16b8dfbee25621ccb4..b25522cfd96a80f06ae5f540ec2f1d763cbf33f4 100644 (file)
@@ -1023,6 +1023,10 @@ fn get_expn_that_defined(self, id: DefIndex, sess: &Session) -> ExpnId {
         self.root.tables.expn_that_defined.get(self, id).unwrap().decode((self, sess))
     }
 
+    fn get_debugger_visualizers(self) -> Vec<rustc_span::DebuggerVisualizerFile> {
+        self.root.debugger_visualizers.decode(self).collect::<Vec<_>>()
+    }
+
     /// Iterates over all the stability attributes in the given crate.
     fn get_lib_features(self, tcx: TyCtxt<'tcx>) -> &'tcx [(Symbol, Option<Symbol>)] {
         tcx.arena.alloc_from_iter(self.root.lib_features.decode(self))
index da1dd6af65a5dd72a27ec00a1d00a5324882119f..c00c6ce2f71dca7eb75a00fabbb0e81d55276126 100644 (file)
@@ -233,6 +233,7 @@ fn into_args(self) -> (DefId, SimplifiedType) {
     }
 
     used_crate_source => { Lrc::clone(&cdata.source) }
+    debugger_visualizers => { cdata.get_debugger_visualizers() }
 
     exported_symbols => {
         let syms = cdata.exported_symbols(tcx);
index b46ea955a3a82541fd44e6795c6ca88bc67d6dc3..b2eafa035db830da3cb9ddc151ce8539aec75f3b 100644 (file)
@@ -35,7 +35,9 @@
 use rustc_session::config::CrateType;
 use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib};
 use rustc_span::symbol::{sym, Ident, Symbol};
-use rustc_span::{self, ExternalSource, FileName, SourceFile, Span, SyntaxContext};
+use rustc_span::{
+    self, DebuggerVisualizerFile, ExternalSource, FileName, SourceFile, Span, SyntaxContext,
+};
 use rustc_span::{
     hygiene::{ExpnIndex, HygieneEncodeContext, MacroKind},
     RealFileName,
@@ -672,6 +674,10 @@ fn encode_crate_root(&mut self) -> Lazy<CrateRoot<'tcx>> {
         let tables = self.tables.encode(&mut self.opaque);
         let tables_bytes = self.position() - i;
 
+        i = self.position();
+        let debugger_visualizers = self.encode_debugger_visualizers();
+        let debugger_visualizers_bytes = self.position() - i;
+
         // Encode exported symbols info. This is prefetched in `encode_metadata` so we encode
         // this as late as possible to give the prefetching as much time as possible to complete.
         i = self.position();
@@ -717,6 +723,7 @@ fn encode_crate_root(&mut self) -> Lazy<CrateRoot<'tcx>> {
             has_panic_handler: tcx.has_panic_handler(LOCAL_CRATE),
             has_default_lib_allocator,
             proc_macro_data,
+            debugger_visualizers,
             compiler_builtins: tcx.sess.contains_name(&attrs, sym::compiler_builtins),
             needs_allocator: tcx.sess.contains_name(&attrs, sym::needs_allocator),
             needs_panic_runtime: tcx.sess.contains_name(&attrs, sym::needs_panic_runtime),
@@ -757,25 +764,26 @@ fn encode_crate_root(&mut self) -> Lazy<CrateRoot<'tcx>> {
             }
 
             eprintln!("metadata stats:");
-            eprintln!("             dep bytes: {}", dep_bytes);
-            eprintln!("     lib feature bytes: {}", lib_feature_bytes);
-            eprintln!("       lang item bytes: {}", lang_item_bytes);
-            eprintln!(" diagnostic item bytes: {}", diagnostic_item_bytes);
-            eprintln!("          native bytes: {}", native_lib_bytes);
-            eprintln!("      source_map bytes: {}", source_map_bytes);
-            eprintln!("          traits bytes: {}", traits_bytes);
-            eprintln!("           impls bytes: {}", impls_bytes);
-            eprintln!("incoherent_impls bytes: {}", incoherent_impls_bytes);
-            eprintln!("    exp. symbols bytes: {}", exported_symbols_bytes);
-            eprintln!("  def-path table bytes: {}", def_path_table_bytes);
-            eprintln!(" def-path hashes bytes: {}", def_path_hash_map_bytes);
-            eprintln!(" proc-macro-data-bytes: {}", proc_macro_data_bytes);
-            eprintln!("             mir bytes: {}", mir_bytes);
-            eprintln!("            item bytes: {}", item_bytes);
-            eprintln!("           table bytes: {}", tables_bytes);
-            eprintln!("         hygiene bytes: {}", hygiene_bytes);
-            eprintln!("            zero bytes: {}", zero_bytes);
-            eprintln!("           total bytes: {}", total_bytes);
+            eprintln!("                  dep bytes: {}", dep_bytes);
+            eprintln!("          lib feature bytes: {}", lib_feature_bytes);
+            eprintln!("            lang item bytes: {}", lang_item_bytes);
+            eprintln!("      diagnostic item bytes: {}", diagnostic_item_bytes);
+            eprintln!("               native bytes: {}", native_lib_bytes);
+            eprintln!(" debugger visualizers bytes: {}", debugger_visualizers_bytes);
+            eprintln!("           source_map bytes: {}", source_map_bytes);
+            eprintln!("               traits bytes: {}", traits_bytes);
+            eprintln!("                impls bytes: {}", impls_bytes);
+            eprintln!("     incoherent_impls bytes: {}", incoherent_impls_bytes);
+            eprintln!("         exp. symbols bytes: {}", exported_symbols_bytes);
+            eprintln!("       def-path table bytes: {}", def_path_table_bytes);
+            eprintln!("      def-path hashes bytes: {}", def_path_hash_map_bytes);
+            eprintln!("      proc-macro-data-bytes: {}", proc_macro_data_bytes);
+            eprintln!("                  mir bytes: {}", mir_bytes);
+            eprintln!("                 item bytes: {}", item_bytes);
+            eprintln!("                table bytes: {}", tables_bytes);
+            eprintln!("              hygiene bytes: {}", hygiene_bytes);
+            eprintln!("                 zero bytes: {}", zero_bytes);
+            eprintln!("                total bytes: {}", total_bytes);
         }
 
         root
@@ -1716,6 +1724,11 @@ fn encode_proc_macros(&mut self) -> Option<ProcMacroData> {
         }
     }
 
+    fn encode_debugger_visualizers(&mut self) -> Lazy<[DebuggerVisualizerFile]> {
+        empty_proc_macro!(self);
+        self.lazy(self.tcx.debugger_visualizers(LOCAL_CRATE).iter())
+    }
+
     fn encode_crate_deps(&mut self) -> Lazy<[CrateDep]> {
         empty_proc_macro!(self);
 
index cdbed90e6b98e8623bf4a2a9e25f7998b64ea2a5..a0fd9ef4f87d95ea0df4e285b900558e3497bb63 100644 (file)
@@ -219,6 +219,7 @@ macro_rules! Lazy {
     proc_macro_data: Option<ProcMacroData>,
 
     tables: LazyTables<'tcx>,
+    debugger_visualizers: Lazy<[rustc_span::DebuggerVisualizerFile]>,
 
     exported_symbols: Lazy!([(ExportedSymbol<'tcx>, SymbolExportInfo)]),
 
index cc80ab8f16e9c9f7ff007f930c349948eca3a50e..e439d128dbc7773358b8058af5e4d447d6c0080e 100644 (file)
         desc { "looking at the source for a crate" }
         separate_provide_extern
     }
+    /// Returns the debugger visualizers defined for this crate.
+    query debugger_visualizers(_: CrateNum) -> Vec<rustc_span::DebuggerVisualizerFile> {
+        storage(ArenaCacheSelector<'tcx>)
+        desc { "looking up the debugger visualizers for this crate" }
+        separate_provide_extern
+    }
     query postorder_cnums(_: ()) -> &'tcx [CrateNum] {
         eval_always
         desc { "generating a postorder list of CrateNums" }
index 3fe68f723ec14ead710ab75b8b82025fb9712365..a1d1b6b3a785bcc6e522d6e7363140e4a4fcdb90 100644 (file)
@@ -136,6 +136,10 @@ fn default_print_def_path(
                     match key.disambiguated_data.data {
                         // Closures' own generics are only captures, don't print them.
                         DefPathData::ClosureExpr => {}
+                        // This covers both `DefKind::AnonConst` and `DefKind::InlineConst`.
+                        // Anon consts doesn't have their own generics, and inline consts' own
+                        // generics are their inferred types, so don't print them.
+                        DefPathData::AnonConst => {}
 
                         // If we have any generic arguments to print, we do that
                         // on top of the same path, but without its own generics.
index 691f4fb0e54259a29e5532a8f154274d76e59cc8..f7535d338da40dd05caa6e7a8e037498d2e94257 100644 (file)
@@ -415,7 +415,7 @@ fn get_const(&self, place: Place<'tcx>) -> Option<OpTy<'tcx>> {
 
         // Try to read the local as an immediate so that if it is representable as a scalar, we can
         // handle it as such, but otherwise, just return the value as is.
-        Some(match self.ecx.try_read_immediate(&op) {
+        Some(match self.ecx.read_immediate_raw(&op, /*force*/ false) {
             Ok(Ok(imm)) => imm.into(),
             _ => op,
         })
@@ -709,8 +709,8 @@ fn replace_with_const(
             return;
         }
 
-        // FIXME> figure out what to do when try_read_immediate fails
-        let imm = self.use_ecx(|this| this.ecx.try_read_immediate(value));
+        // FIXME> figure out what to do when read_immediate_raw fails
+        let imm = self.use_ecx(|this| this.ecx.read_immediate_raw(value, /*force*/ false));
 
         if let Some(Ok(imm)) = imm {
             match *imm {
index 4945c10c9aaa900aa35f826435a2dd7f083ebd85..aa898cfd3ba5ef3e5f14529e1cb3973599378348 100644 (file)
@@ -412,7 +412,7 @@ fn get_const(&self, place: Place<'tcx>) -> Option<OpTy<'tcx>> {
 
         // Try to read the local as an immediate so that if it is representable as a scalar, we can
         // handle it as such, but otherwise, just return the value as is.
-        Some(match self.ecx.try_read_immediate(&op) {
+        Some(match self.ecx.read_immediate_raw(&op, /*force*/ false) {
             Ok(Ok(imm)) => imm.into(),
             _ => op,
         })
index 1828aecb375c4fdcc9943cb5542e8908f7adf104..18f32b04fadca5aee2e066896992cefbd1986066 100644 (file)
@@ -445,12 +445,9 @@ fn collect_items_rec<'tcx>(
                             // depend on any other items.
                         }
                         hir::InlineAsmOperand::SymFn { anon_const } => {
-                            let def_id = tcx.hir().body_owner_def_id(anon_const.body).to_def_id();
-                            if let Ok(val) = tcx.const_eval_poly(def_id) {
-                                rustc_data_structures::stack::ensure_sufficient_stack(|| {
-                                    collect_const_value(tcx, val, &mut neighbors);
-                                });
-                            }
+                            let fn_ty =
+                                tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id);
+                            visit_fn_use(tcx, fn_ty, false, *op_sp, &mut neighbors);
                         }
                         hir::InlineAsmOperand::SymStatic { path: _, def_id } => {
                             let instance = Instance::mono(tcx, *def_id);
index 39e578bce7ef67eecb9287da9fc56f77770bd800..a3ef1981e84f9cb79b2ca396f564c2820e2071e4 100644 (file)
@@ -9,6 +9,7 @@ rustc_middle = { path = "../rustc_middle" }
 rustc_attr = { path = "../rustc_attr" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
+rustc_expand = { path = "../rustc_expand" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
 rustc_parse = { path = "../rustc_parse" }
index a9444972130a85a0bef02baaf23bffb0189ef75b..b297012f19f2f17475a6e2807ef82cd825cce38e 100644 (file)
@@ -100,6 +100,7 @@ fn check_attributes(
                 sym::allow_internal_unstable => {
                     self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs)
                 }
+                sym::debugger_visualizer => self.check_debugger_visualizer(&attr, target),
                 sym::rustc_allow_const_fn_unstable => {
                     self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
                 }
@@ -124,6 +125,9 @@ fn check_attributes(
                 sym::rustc_allow_incoherent_impl => {
                     self.check_allow_incoherent_impl(&attr, span, target)
                 }
+                sym::rustc_has_incoherent_inherent_impls => {
+                    self.check_has_incoherent_inherent_impls(&attr, span, target)
+                }
                 sym::rustc_const_unstable
                 | sym::rustc_const_stable
                 | sym::unstable
@@ -1095,7 +1099,6 @@ fn check_pass_by_value(&self, attr: &Attribute, span: Span, target: Target) -> b
         }
     }
 
-    /// Warns against some misuses of `#[pass_by_value]`
     fn check_allow_incoherent_impl(&self, attr: &Attribute, span: Span, target: Target) -> bool {
         match target {
             Target::Method(MethodKind::Inherent) => true,
@@ -1113,6 +1116,30 @@ fn check_allow_incoherent_impl(&self, attr: &Attribute, span: Span, target: Targ
         }
     }
 
+    fn check_has_incoherent_inherent_impls(
+        &self,
+        attr: &Attribute,
+        span: Span,
+        target: Target,
+    ) -> bool {
+        match target {
+            Target::Trait | Target::Struct | Target::Enum | Target::Union | Target::ForeignTy => {
+                true
+            }
+            _ => {
+                self.tcx
+                    .sess
+                    .struct_span_err(
+                        attr.span,
+                        "`rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits.",
+                    )
+                    .span_label(span, "only adts, extern types and traits are supported")
+                    .emit();
+                false
+            }
+        }
+    }
+
     /// Warns against some misuses of `#[must_use]`
     fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
         let node = self.tcx.hir().get(hir_id);
@@ -1860,6 +1887,65 @@ fn check_allow_internal_unstable(
         }
     }
 
+    /// Checks if the items on the `#[debugger_visualizer]` attribute are valid.
+    fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) -> bool {
+        match target {
+            Target::Mod => {}
+            _ => {
+                self.tcx
+                    .sess
+                    .struct_span_err(attr.span, "attribute should be applied to a module")
+                    .emit();
+                return false;
+            }
+        }
+
+        let hints = match attr.meta_item_list() {
+            Some(meta_item_list) => meta_item_list,
+            None => {
+                self.emit_debugger_visualizer_err(attr);
+                return false;
+            }
+        };
+
+        let hint = match hints.len() {
+            1 => &hints[0],
+            _ => {
+                self.emit_debugger_visualizer_err(attr);
+                return false;
+            }
+        };
+
+        if !hint.has_name(sym::natvis_file) {
+            self.emit_debugger_visualizer_err(attr);
+            return false;
+        }
+
+        let meta_item = match hint.meta_item() {
+            Some(meta_item) => meta_item,
+            None => {
+                self.emit_debugger_visualizer_err(attr);
+                return false;
+            }
+        };
+
+        match (meta_item.name_or_empty(), meta_item.value_str()) {
+            (sym::natvis_file, Some(_)) => true,
+            (_, _) => {
+                self.emit_debugger_visualizer_err(attr);
+                false
+            }
+        }
+    }
+
+    fn emit_debugger_visualizer_err(&self, attr: &Attribute) {
+        self.tcx
+            .sess
+            .struct_span_err(attr.span, "invalid argument")
+            .note(r#"expected: `natvis_file = "..."`"#)
+            .emit();
+    }
+
     /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
     /// (Allows proc_macro functions)
     fn check_rustc_allow_const_fn_unstable(
diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs
new file mode 100644 (file)
index 0000000..f89092c
--- /dev/null
@@ -0,0 +1,137 @@
+//! Detecting usage of the `#[debugger_visualizer]` attribute.
+
+use hir::CRATE_HIR_ID;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_expand::base::resolve_path;
+use rustc_hir as hir;
+use rustc_hir::def_id::CrateNum;
+use rustc_hir::itemlikevisit::ItemLikeVisitor;
+use rustc_hir::{HirId, Target};
+use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::TyCtxt;
+use rustc_span::def_id::LOCAL_CRATE;
+use rustc_span::{sym, DebuggerVisualizerFile, DebuggerVisualizerType};
+
+use std::sync::Arc;
+
+struct DebuggerVisualizerCollector<'tcx> {
+    debugger_visualizers: FxHashSet<DebuggerVisualizerFile>,
+    tcx: TyCtxt<'tcx>,
+}
+
+impl<'v, 'tcx> ItemLikeVisitor<'v> for DebuggerVisualizerCollector<'tcx> {
+    fn visit_item(&mut self, item: &hir::Item<'_>) {
+        let target = Target::from_item(item);
+        match target {
+            Target::Mod => {
+                self.check_for_debugger_visualizer(item.hir_id());
+            }
+            _ => {}
+        }
+    }
+
+    fn visit_trait_item(&mut self, _: &hir::TraitItem<'_>) {}
+
+    fn visit_impl_item(&mut self, _: &hir::ImplItem<'_>) {}
+
+    fn visit_foreign_item(&mut self, _: &hir::ForeignItem<'_>) {}
+}
+
+impl<'tcx> DebuggerVisualizerCollector<'tcx> {
+    fn new(tcx: TyCtxt<'tcx>) -> DebuggerVisualizerCollector<'tcx> {
+        DebuggerVisualizerCollector { tcx, debugger_visualizers: FxHashSet::default() }
+    }
+
+    fn check_for_debugger_visualizer(&mut self, hir_id: HirId) {
+        let attrs = self.tcx.hir().attrs(hir_id);
+        for attr in attrs {
+            if attr.has_name(sym::debugger_visualizer) {
+                let list = match attr.meta_item_list() {
+                    Some(list) => list,
+                    _ => continue,
+                };
+
+                let meta_item = match list.len() {
+                    1 => match list[0].meta_item() {
+                        Some(meta_item) => meta_item,
+                        _ => continue,
+                    },
+                    _ => continue,
+                };
+
+                let file = match (meta_item.name_or_empty(), meta_item.value_str()) {
+                    (sym::natvis_file, Some(value)) => {
+                        match resolve_path(&self.tcx.sess.parse_sess, value.as_str(), attr.span) {
+                            Ok(file) => file,
+                            Err(mut err) => {
+                                err.emit();
+                                continue;
+                            }
+                        }
+                    }
+                    (_, _) => continue,
+                };
+
+                if file.is_file() {
+                    let contents = match std::fs::read(&file) {
+                        Ok(contents) => contents,
+                        Err(err) => {
+                            self.tcx
+                                .sess
+                                .struct_span_err(
+                                    attr.span,
+                                    &format!(
+                                        "Unable to read contents of file `{}`. {}",
+                                        file.display(),
+                                        err
+                                    ),
+                                )
+                                .emit();
+                            continue;
+                        }
+                    };
+
+                    self.debugger_visualizers.insert(DebuggerVisualizerFile::new(
+                        Arc::from(contents),
+                        DebuggerVisualizerType::Natvis,
+                    ));
+                } else {
+                    self.tcx
+                        .sess
+                        .struct_span_err(
+                            attr.span,
+                            &format!("{} is not a valid file", file.display()),
+                        )
+                        .emit();
+                }
+            }
+        }
+    }
+}
+
+/// Traverses and collects the debugger visualizers for a specific crate.
+fn debugger_visualizers<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> Vec<DebuggerVisualizerFile> {
+    assert_eq!(cnum, LOCAL_CRATE);
+
+    // Initialize the collector.
+    let mut collector = DebuggerVisualizerCollector::new(tcx);
+
+    // Collect debugger visualizers in this crate.
+    tcx.hir().visit_all_item_likes(&mut collector);
+
+    // Collect debugger visualizers on the crate attributes.
+    collector.check_for_debugger_visualizer(CRATE_HIR_ID);
+
+    // Extract out the found debugger_visualizer items.
+    let DebuggerVisualizerCollector { debugger_visualizers, .. } = collector;
+
+    let mut visualizers = debugger_visualizers.into_iter().collect::<Vec<_>>();
+
+    // Sort the visualizers so we always get a deterministic query result.
+    visualizers.sort();
+    visualizers
+}
+
+pub fn provide(providers: &mut Providers) {
+    providers.debugger_visualizers = debugger_visualizers;
+}
index 71d49d8b7ea9ca442179f0c7f0a6c47d88c1fabf..d9d08488d2869c85028f3a570b26967266edd027 100644 (file)
@@ -26,6 +26,7 @@
 mod check_attr;
 mod check_const;
 pub mod dead;
+mod debugger_visualizer;
 mod diagnostic_items;
 pub mod entry;
 pub mod hir_id_validator;
@@ -47,6 +48,7 @@ pub fn provide(providers: &mut Providers) {
     check_attr::provide(providers);
     check_const::provide(providers);
     dead::provide(providers);
+    debugger_visualizer::provide(providers);
     diagnostic_items::provide(providers);
     entry::provide(providers);
     lang_items::provide(providers);
index 1bdf53cf84fedc41a6b3194b9fc3fd9a55f2fd6a..723e66e9ef61807d42ed927cdf72970d2157ecab 100644 (file)
@@ -486,6 +486,9 @@ struct DiagnosticMetadata<'ast> {
     current_where_predicate: Option<&'ast WherePredicate>,
 
     current_type_path: Option<&'ast Ty>,
+
+    /// The current impl items (used to suggest).
+    current_impl_items: Option<&'ast [P<AssocItem>]>,
 }
 
 struct LateResolutionVisitor<'a, 'b, 'ast> {
@@ -1637,7 +1640,9 @@ fn resolve_item(&mut self, item: &'ast Item) {
                 items: ref impl_items,
                 ..
             }) => {
+                self.diagnostic_metadata.current_impl_items = Some(impl_items);
                 self.resolve_implementation(generics, of_trait, &self_ty, item.id, impl_items);
+                self.diagnostic_metadata.current_impl_items = None;
             }
 
             ItemKind::Trait(box Trait { ref generics, ref bounds, ref items, .. }) => {
@@ -3100,6 +3105,13 @@ fn resolve_anon_const(&mut self, constant: &'ast AnonConst, is_repeat: IsRepeatE
         );
     }
 
+    fn resolve_inline_const(&mut self, constant: &'ast AnonConst) {
+        debug!("resolve_anon_const {constant:?}");
+        self.with_constant_rib(IsRepeatExpr::No, HasGenericParams::Yes, None, |this| {
+            visit::walk_anon_const(this, constant);
+        });
+    }
+
     fn resolve_expr(&mut self, expr: &'ast Expr, parent: Option<&'ast Expr>) {
         // First, record candidate traits for this expression if it could
         // result in the invocation of a method call.
@@ -3256,7 +3268,7 @@ fn resolve_expr(&mut self, expr: &'ast Expr, parent: Option<&'ast Expr>) {
                 });
             }
             ExprKind::ConstBlock(ref ct) => {
-                self.resolve_anon_const(ct, IsRepeatExpr::No);
+                self.resolve_inline_const(ct);
             }
             ExprKind::Index(ref elem, ref idx) => {
                 self.resolve_expr(elem, Some(expr));
index 3076cc1131700d8df6f0f4707f37023d54fffe95..4f07d0076f140452226a08dd73d22d4a7a8d4c5c 100644 (file)
@@ -6,7 +6,7 @@
 use crate::{Module, ModuleKind, ModuleOrUniformRoot};
 use crate::{PathResult, PathSource, Segment};
 
-use rustc_ast::visit::FnKind;
+use rustc_ast::visit::{FnCtxt, FnKind};
 use rustc_ast::{
     self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
     NodeId, Path, Ty, TyKind,
@@ -144,15 +144,22 @@ pub(crate) fn smart_resolve_report_errors(
         let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));
 
         // Make the base error.
+        struct BaseError<'a> {
+            msg: String,
+            fallback_label: String,
+            span: Span,
+            could_be_expr: bool,
+            suggestion: Option<(Span, &'a str, String)>,
+        }
         let mut expected = source.descr_expected();
         let path_str = Segment::names_to_string(path);
         let item_str = path.last().unwrap().ident;
-        let (base_msg, fallback_label, base_span, could_be_expr) = if let Some(res) = res {
-            (
-                format!("expected {}, found {} `{}`", expected, res.descr(), path_str),
-                format!("not a {}", expected),
+        let base_error = if let Some(res) = res {
+            BaseError {
+                msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str),
+                fallback_label: format!("not a {expected}"),
                 span,
-                match res {
+                could_be_expr: match res {
                     Res::Def(DefKind::Fn, _) => {
                         // Verify whether this is a fn call or an Fn used as a type.
                         self.r
@@ -171,22 +178,49 @@ pub(crate) fn smart_resolve_report_errors(
                     | Res::Local(_) => true,
                     _ => false,
                 },
-            )
+                suggestion: None,
+            }
         } else {
             let item_span = path.last().unwrap().ident.span;
-            let (mod_prefix, mod_str) = if path.len() == 1 {
-                (String::new(), "this scope".to_string())
+            let (mod_prefix, mod_str, suggestion) = if path.len() == 1 {
+                debug!(?self.diagnostic_metadata.current_impl_items);
+                debug!(?self.diagnostic_metadata.current_function);
+                let suggestion = if let Some(items) = self.diagnostic_metadata.current_impl_items
+                    && let Some((fn_kind, _)) = self.diagnostic_metadata.current_function
+                    && self.current_trait_ref.is_none()
+                    && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt()
+                    && let Some(item) = items.iter().find(|i| {
+                        if let AssocItemKind::Fn(fn_) = &i.kind
+                            && !fn_.sig.decl.has_self()
+                            && i.ident.name == item_str.name
+                        {
+                            debug!(?item_str.name);
+                            debug!(?fn_.sig.decl.inputs);
+                            return true
+                        }
+                        false
+                    })
+                {
+                    Some((
+                        item_span,
+                        "consider using the associated function",
+                        format!("Self::{}", item.ident)
+                    ))
+                } else {
+                    None
+                };
+                (String::new(), "this scope".to_string(), suggestion)
             } else if path.len() == 2 && path[0].ident.name == kw::PathRoot {
                 if self.r.session.edition() > Edition::Edition2015 {
                     // In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude
                     // which overrides all other expectations of item type
                     expected = "crate";
-                    (String::new(), "the list of imported crates".to_string())
+                    (String::new(), "the list of imported crates".to_string(), None)
                 } else {
-                    (String::new(), "the crate root".to_string())
+                    (String::new(), "the crate root".to_string(), None)
                 }
             } else if path.len() == 2 && path[0].ident.name == kw::Crate {
-                (String::new(), "the crate root".to_string())
+                (String::new(), "the crate root".to_string(), None)
             } else {
                 let mod_path = &path[..path.len() - 1];
                 let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), None) {
@@ -194,22 +228,28 @@ pub(crate) fn smart_resolve_report_errors(
                     _ => None,
                 }
                 .map_or_else(String::new, |res| format!("{} ", res.descr()));
-                (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)))
+                (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), None)
             };
-            (
-                format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str),
-                if path_str == "async" && expected.starts_with("struct") {
+            BaseError {
+                msg: format!("cannot find {expected} `{item_str}` in {mod_prefix}{mod_str}"),
+                fallback_label: if path_str == "async" && expected.starts_with("struct") {
                     "`async` blocks are only allowed in Rust 2018 or later".to_string()
                 } else {
-                    format!("not found in {}", mod_str)
+                    format!("not found in {mod_str}")
                 },
-                item_span,
-                false,
-            )
+                span: item_span,
+                could_be_expr: false,
+                suggestion,
+            }
         };
 
         let code = source.error_code(res.is_some());
-        let mut err = self.r.session.struct_span_err_with_code(base_span, &base_msg, code);
+        let mut err =
+            self.r.session.struct_span_err_with_code(base_error.span, &base_error.msg, code);
+
+        if let Some(sugg) = base_error.suggestion {
+            err.span_suggestion_verbose(sugg.0, sugg.1, sugg.2, Applicability::MaybeIncorrect);
+        }
 
         if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal {
             err.multipart_suggestion(
@@ -269,7 +309,7 @@ pub(crate) fn smart_resolve_report_errors(
             }
         }
 
-        self.detect_assoct_type_constraint_meant_as_path(base_span, &mut err);
+        self.detect_assoct_type_constraint_meant_as_path(base_error.span, &mut err);
 
         // Emit special messages for unresolved `Self` and `self`.
         if is_self_type(path, ns) {
@@ -471,7 +511,7 @@ pub(crate) fn smart_resolve_report_errors(
                 source,
                 res,
                 &path_str,
-                &fallback_label,
+                &base_error.fallback_label,
             ) {
                 // We do this to avoid losing a secondary span when we override the main error span.
                 self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span);
@@ -479,8 +519,9 @@ pub(crate) fn smart_resolve_report_errors(
             }
         }
 
-        let is_macro = base_span.from_expansion() && base_span.desugaring_kind().is_none();
-        if !self.type_ascription_suggestion(&mut err, base_span) {
+        let is_macro =
+            base_error.span.from_expansion() && base_error.span.desugaring_kind().is_none();
+        if !self.type_ascription_suggestion(&mut err, base_error.span) {
             let mut fallback = false;
             if let (
                 PathSource::Trait(AliasPossibility::Maybe),
@@ -493,7 +534,7 @@ pub(crate) fn smart_resolve_report_errors(
                     let spans: Vec<Span> = bounds
                         .iter()
                         .map(|bound| bound.span())
-                        .filter(|&sp| sp != base_span)
+                        .filter(|&sp| sp != base_error.span)
                         .collect();
 
                     let start_span = bounds.iter().map(|bound| bound.span()).next().unwrap();
@@ -515,7 +556,7 @@ pub(crate) fn smart_resolve_report_errors(
                         multi_span.push_span_label(sp, msg);
                     }
                     multi_span.push_span_label(
-                        base_span,
+                        base_error.span,
                         "expected this type to be a trait...".to_string(),
                     );
                     err.span_help(
@@ -525,14 +566,14 @@ pub(crate) fn smart_resolve_report_errors(
                     );
                     if bounds.iter().all(|bound| match bound {
                         ast::GenericBound::Outlives(_) => true,
-                        ast::GenericBound::Trait(tr, _) => tr.span == base_span,
+                        ast::GenericBound::Trait(tr, _) => tr.span == base_error.span,
                     }) {
                         let mut sugg = vec![];
-                        if base_span != start_span {
-                            sugg.push((start_span.until(base_span), String::new()));
+                        if base_error.span != start_span {
+                            sugg.push((start_span.until(base_error.span), String::new()));
                         }
-                        if base_span != end_span {
-                            sugg.push((base_span.shrink_to_hi().to(end_span), String::new()));
+                        if base_error.span != end_span {
+                            sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new()));
                         }
 
                         err.multipart_suggestion(
@@ -550,7 +591,7 @@ pub(crate) fn smart_resolve_report_errors(
                 fallback = true;
                 match self.diagnostic_metadata.current_let_binding {
                     Some((pat_sp, Some(ty_sp), None))
-                        if ty_sp.contains(base_span) && could_be_expr =>
+                        if ty_sp.contains(base_error.span) && base_error.could_be_expr =>
                     {
                         err.span_suggestion_short(
                             pat_sp.between(ty_sp),
@@ -568,7 +609,7 @@ pub(crate) fn smart_resolve_report_errors(
             }
             if fallback {
                 // Fallback label.
-                err.span_label(base_span, fallback_label);
+                err.span_label(base_error.span, base_error.fallback_label);
             }
         }
         if let Some(err_code) = &err.code {
index d5f806308cf41bbca3b16ea67cd7f1fe77278fd5..6b529d5e0837b03d686fd70b8795ad93c641bc75 100644 (file)
@@ -279,6 +279,7 @@ pub fn as_local(self) -> Option<LocalDefId> {
     }
 
     #[inline]
+    #[track_caller]
     pub fn expect_local(self) -> LocalDefId {
         self.as_local().unwrap_or_else(|| panic!("DefId::expect_local: `{:?}` isn't local", self))
     }
index 8bde53b5fd381fc96ff9431aa3eab9772d4b57f0..7357cebf62eb9dc67d20d49a9a566af1e8a13476 100644 (file)
@@ -70,6 +70,7 @@
 use std::ops::{Add, Range, Sub};
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
+use std::sync::Arc;
 
 use md5::Digest;
 use md5::Md5;
@@ -1199,6 +1200,28 @@ fn hash_len(&self) -> usize {
     }
 }
 
+#[derive(HashStable_Generic)]
+#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)]
+pub enum DebuggerVisualizerType {
+    Natvis,
+}
+
+/// A single debugger visualizer file.
+#[derive(HashStable_Generic)]
+#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)]
+pub struct DebuggerVisualizerFile {
+    /// The complete debugger visualizer source.
+    pub src: Arc<[u8]>,
+    /// Indicates which visualizer type this targets.
+    pub visualizer_type: DebuggerVisualizerType,
+}
+
+impl DebuggerVisualizerFile {
+    pub fn new(src: Arc<[u8]>, visualizer_type: DebuggerVisualizerType) -> Self {
+        DebuggerVisualizerFile { src, visualizer_type }
+    }
+}
+
 /// A single source in the [`SourceMap`].
 #[derive(Clone)]
 pub struct SourceFile {
index c1299c94c4bb395395f04632dd0edc9ed4c3c222..d6885bebc320eb3d0fe03fca6e9ed16223922a33 100644 (file)
         debug_struct,
         debug_trait_builder,
         debug_tuple,
+        debugger_visualizer,
         decl_macro,
         declare_lint_pass,
         decode,
         native_link_modifiers_bundle,
         native_link_modifiers_verbatim,
         native_link_modifiers_whole_archive,
+        natvis_file,
         ne,
         nearbyintf32,
         nearbyintf64,
         rustc_error,
         rustc_evaluate_where_clauses,
         rustc_expected_cgu_reuse,
+        rustc_has_incoherent_inherent_impls,
         rustc_if_this_changed,
         rustc_inherit_overflow_checks,
         rustc_insignificant_dtor,
index e04cc42b6d7e5f56631bde132508a0dffbc6148d..3a36686613b7c69331c1afa12b6d80c5b7793bd2 100644 (file)
@@ -27,6 +27,7 @@
 use rustc_span::lev_distance::{
     find_best_match_for_name_with_substrings, lev_distance_with_substrings,
 };
+use rustc_span::symbol::sym;
 use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP};
 use rustc_trait_selection::autoderef::{self, Autoderef};
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
@@ -642,16 +643,22 @@ fn assemble_probe(&mut self, self_ty: &Canonical<'tcx, QueryResponse<'tcx, Ty<'t
 
                 self.assemble_inherent_candidates_from_object(generalized_self_ty);
                 self.assemble_inherent_impl_candidates_for_type(p.def_id());
+                if self.tcx.has_attr(p.def_id(), sym::rustc_has_incoherent_inherent_impls) {
+                    self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty);
+                }
             }
             ty::Adt(def, _) => {
                 let def_id = def.did();
                 self.assemble_inherent_impl_candidates_for_type(def_id);
-                if Some(def_id) == self.tcx.lang_items().c_str() {
+                if self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) {
                     self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty);
                 }
             }
             ty::Foreign(did) => {
                 self.assemble_inherent_impl_candidates_for_type(did);
+                if self.tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) {
+                    self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty);
+                }
             }
             ty::Param(p) => {
                 self.assemble_inherent_candidates_from_param(p);
index 5ad0c4ac52d49c45f5dd614ce9bd50bebdd0ea50..de29a851e2fdfa21aec103cbb3c94d2e1512c040 100644 (file)
@@ -55,18 +55,13 @@ fn visit_item(&mut self, item: &hir::Item<'_>) {
         let self_ty = self.tcx.type_of(item.def_id);
         match *self_ty.kind() {
             ty::Adt(def, _) => {
-                let def_id = def.did();
-                if !def_id.is_local() && Some(def_id) == self.tcx.lang_items().c_str() {
-                    self.check_primitive_impl(item.def_id, self_ty, items, ty.span)
-                } else {
-                    self.check_def_id(item, def_id);
-                }
+                self.check_def_id(item, self_ty, def.did());
             }
             ty::Foreign(did) => {
-                self.check_def_id(item, did);
+                self.check_def_id(item, self_ty, did);
             }
             ty::Dynamic(data, ..) if data.principal_def_id().is_some() => {
-                self.check_def_id(item, data.principal_def_id().unwrap());
+                self.check_def_id(item, self_ty, data.principal_def_id().unwrap());
             }
             ty::Dynamic(..) => {
                 struct_span_err!(
@@ -124,14 +119,67 @@ fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {}
     fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {}
 }
 
+const INTO_CORE: &str = "consider moving this inherent impl into `core` if possible";
+const INTO_DEFINING_CRATE: &str =
+    "consider moving this inherent impl into the crate defining the type if possible";
+const ADD_ATTR_TO_TY: &str = "alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type \
+     and `#[rustc_allow_incoherent_impl]` to the relevant impl items";
+const ADD_ATTR: &str =
+    "alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items";
+
 impl<'tcx> InherentCollect<'tcx> {
-    fn check_def_id(&mut self, item: &hir::Item<'_>, def_id: DefId) {
+    fn check_def_id(&mut self, item: &hir::Item<'_>, self_ty: Ty<'tcx>, def_id: DefId) {
+        let impl_def_id = item.def_id;
         if let Some(def_id) = def_id.as_local() {
             // Add the implementation to the mapping from implementation to base
             // type def ID, if there is a base type for this implementation and
             // the implementation does not have any associated traits.
             let vec = self.impls_map.inherent_impls.entry(def_id).or_default();
-            vec.push(item.def_id.to_def_id());
+            vec.push(impl_def_id.to_def_id());
+            return;
+        }
+
+        if self.tcx.features().rustc_attrs {
+            let hir::ItemKind::Impl(&hir::Impl { items, .. }) = item.kind else {
+                bug!("expected `impl` item: {:?}", item);
+            };
+
+            if !self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) {
+                struct_span_err!(
+                    self.tcx.sess,
+                    item.span,
+                    E0390,
+                    "cannot define inherent `impl` for a type outside of the crate where the type is defined",
+                )
+                .help(INTO_DEFINING_CRATE)
+                .span_help(item.span, ADD_ATTR_TO_TY)
+                .emit();
+                return;
+            }
+
+            for impl_item in items {
+                if !self
+                    .tcx
+                    .has_attr(impl_item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl)
+                {
+                    struct_span_err!(
+                        self.tcx.sess,
+                        item.span,
+                        E0390,
+                        "cannot define inherent `impl` for a type outside of the crate where the type is defined",
+                    )
+                    .help(INTO_DEFINING_CRATE)
+                    .span_help(impl_item.span, ADD_ATTR)
+                    .emit();
+                    return;
+                }
+            }
+
+            if let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsPlaceholders) {
+                self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id);
+            } else {
+                bug!("unexpected self type: {:?}", self_ty);
+            }
         } else {
             struct_span_err!(
                 self.tcx.sess,
@@ -153,9 +201,6 @@ fn check_primitive_impl(
         items: &[hir::ImplItemRef],
         span: Span,
     ) {
-        const INTO_CORE: &str = "consider moving this inherent impl into `core` if possible";
-        const ADD_ATTR: &str =
-            "alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items";
         if !self.tcx.hir().rustc_coherence_is_core() {
             if self.tcx.features().rustc_attrs {
                 for item in items {
index 19e68f0b14f4f23934dba64e53f2a41e36a521d7..f57986a985cdea63face6a3d361ba95a0abd0663 100644 (file)
@@ -44,6 +44,59 @@ fn orphan_check_impl(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGua
     };
     let sp = tcx.sess.source_map().guess_head_span(item.span);
     let tr = impl_.of_trait.as_ref().unwrap();
+
+    // Ensure no opaque types are present in this impl header. See issues #76202 and #86411 for examples,
+    // and #84660 where it would otherwise allow unsoundness.
+    if trait_ref.has_opaque_types() {
+        trace!("{:#?}", item);
+        // First we find the opaque type in question.
+        for ty in trait_ref.substs {
+            for ty in ty.walk() {
+                let ty::subst::GenericArgKind::Type(ty) = ty.unpack() else { continue };
+                let ty::Opaque(def_id, _) = *ty.kind() else { continue };
+                trace!(?def_id);
+
+                // Then we search for mentions of the opaque type's type alias in the HIR
+                struct SpanFinder<'tcx> {
+                    sp: Span,
+                    def_id: DefId,
+                    tcx: TyCtxt<'tcx>,
+                }
+                impl<'v, 'tcx> hir::intravisit::Visitor<'v> for SpanFinder<'tcx> {
+                    #[instrument(level = "trace", skip(self, _id))]
+                    fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) {
+                        // You can't mention an opaque type directly, so we look for type aliases
+                        if let hir::def::Res::Def(hir::def::DefKind::TyAlias, def_id) = path.res {
+                            // And check if that type alias's type contains the opaque type we're looking for
+                            for arg in self.tcx.type_of(def_id).walk() {
+                                if let GenericArgKind::Type(ty) = arg.unpack() {
+                                    if let ty::Opaque(def_id, _) = *ty.kind() {
+                                        if def_id == self.def_id {
+                                            // Finally we update the span to the mention of the type alias
+                                            self.sp = path.span;
+                                            return;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                        hir::intravisit::walk_path(self, path)
+                    }
+                }
+
+                let mut visitor = SpanFinder { sp, def_id, tcx };
+                hir::intravisit::walk_item(&mut visitor, item);
+                let reported = tcx
+                    .sess
+                    .struct_span_err(visitor.sp, "cannot implement trait on type alias impl trait")
+                    .span_note(tcx.def_span(def_id), "type alias impl trait defined here")
+                    .emit();
+                return Err(reported);
+            }
+        }
+        span_bug!(sp, "opaque type not found, but `has_opaque_types` is set")
+    }
+
     match traits::orphan_check(tcx, item.def_id.to_def_id()) {
         Ok(()) => {}
         Err(err) => emit_orphan_check_error(
@@ -143,58 +196,6 @@ fn orphan_check_impl(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGua
         }
     }
 
-    // Ensure no opaque types are present in this impl header. See issues #76202 and #86411 for examples,
-    // and #84660 where it would otherwise allow unsoundness.
-    if trait_ref.has_opaque_types() {
-        trace!("{:#?}", item);
-        // First we find the opaque type in question.
-        for ty in trait_ref.substs {
-            for ty in ty.walk() {
-                let ty::subst::GenericArgKind::Type(ty) = ty.unpack() else { continue };
-                let ty::Opaque(def_id, _) = *ty.kind() else { continue };
-                trace!(?def_id);
-
-                // Then we search for mentions of the opaque type's type alias in the HIR
-                struct SpanFinder<'tcx> {
-                    sp: Span,
-                    def_id: DefId,
-                    tcx: TyCtxt<'tcx>,
-                }
-                impl<'v, 'tcx> hir::intravisit::Visitor<'v> for SpanFinder<'tcx> {
-                    #[instrument(level = "trace", skip(self, _id))]
-                    fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) {
-                        // You can't mention an opaque type directly, so we look for type aliases
-                        if let hir::def::Res::Def(hir::def::DefKind::TyAlias, def_id) = path.res {
-                            // And check if that type alias's type contains the opaque type we're looking for
-                            for arg in self.tcx.type_of(def_id).walk() {
-                                if let GenericArgKind::Type(ty) = arg.unpack() {
-                                    if let ty::Opaque(def_id, _) = *ty.kind() {
-                                        if def_id == self.def_id {
-                                            // Finally we update the span to the mention of the type alias
-                                            self.sp = path.span;
-                                            return;
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                        hir::intravisit::walk_path(self, path)
-                    }
-                }
-
-                let mut visitor = SpanFinder { sp, def_id, tcx };
-                hir::intravisit::walk_item(&mut visitor, item);
-                let reported = tcx
-                    .sess
-                    .struct_span_err(visitor.sp, "cannot implement trait on type alias impl trait")
-                    .span_note(tcx.def_span(def_id), "type alias impl trait defined here")
-                    .emit();
-                return Err(reported);
-            }
-        }
-        span_bug!(sp, "opaque type not found, but `has_opaque_types` is set")
-    }
-
     Ok(())
 }
 
index 909c99adab5d2d91f6162e86d206896e145f8e3b..6f764a952c02274041fa4ce12a26b3e65f843084 100644 (file)
@@ -59,7 +59,7 @@ struct ParameterCollector {
 impl<'tcx> TypeVisitor<'tcx> for ParameterCollector {
     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         match *t.kind() {
-            ty::Projection(..) | ty::Opaque(..) if !self.include_nonconstraining => {
+            ty::Projection(..) if !self.include_nonconstraining => {
                 // projections are not injective
                 return ControlFlow::CONTINUE;
             }
index 639e7f213eaae253ccd4d88d1cb3816aec3cc7a5..c07536f0d0ce1c997a3d415d8886fec4237ec1cd 100644 (file)
@@ -1369,6 +1369,12 @@ fn write_i128(&mut self, i: i128) {
     fn write_isize(&mut self, i: isize) {
         (**self).write_isize(i)
     }
+    fn write_length_prefix(&mut self, len: usize) {
+        (**self).write_length_prefix(len)
+    }
+    fn write_str(&mut self, s: &str) {
+        (**self).write_str(s)
+    }
 }
 
 #[cfg(not(no_global_oom_handling))]
index c178d3e3b03c918cfa83f7b36e10f776a57dbe00..264c217c9ef235faa2e96b2b5e485f868a84f3d7 100644 (file)
@@ -1990,7 +1990,7 @@ fn extend_one(&mut self, (&k, &v): (&'a K, &'a V)) {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<K: Hash, V: Hash> Hash for BTreeMap<K, V> {
     fn hash<H: Hasher>(&self, state: &mut H) {
-        self.len().hash(state);
+        state.write_length_prefix(self.len());
         for elt in self {
             elt.hash(state);
         }
index 736b38370ab87929a5b9b60efec2bf5cd2de4142..67dc4f30f3179c9d6681f900443cf224f2ac8f5a 100644 (file)
@@ -1944,7 +1944,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: Hash> Hash for LinkedList<T> {
     fn hash<H: Hasher>(&self, state: &mut H) {
-        self.len().hash(state);
+        state.write_length_prefix(self.len());
         for elt in self {
             elt.hash(state);
         }
index 5f1a6848ae62a324c2f410e58b15797ecb808653..04900ead579a14510ae7b23004c3d47c18399c53 100644 (file)
@@ -2899,7 +2899,7 @@ fn cmp(&self, other: &Self) -> Ordering {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: Hash, A: Allocator> Hash for VecDeque<T, A> {
     fn hash<H: Hasher>(&self, state: &mut H) {
-        self.len().hash(state);
+        state.write_length_prefix(self.len());
         // It's not possible to use Hash::hash_slice on slices
         // returned by as_slices method as their length can vary
         // in otherwise identical deques.
index 4d2dc4ecee0b80f566a983750dd3731ce73afa7c..ecebc7ed9ac850692661f014e4df15f024b04199 100644 (file)
 #![feature(extend_one)]
 #![feature(fmt_internals)]
 #![feature(fn_traits)]
+#![feature(hasher_prefixfree_extras)]
 #![feature(inplace_iteration)]
 #![feature(iter_advance_by)]
 #![feature(layout_for_ptr)]
index 4bcc78ae0f4b2df78e9c2ce586631666be80b88a..52957456473650f0fdf26fe067a479145d83899a 100644 (file)
@@ -374,19 +374,26 @@ pub fn new(value: T) -> Rc<T> {
         }
     }
 
-    /// Constructs a new `Rc<T>` using a closure `data_fn` that has access to a
-    /// weak reference to the constructing `Rc<T>`.
+    /// Constructs a new `Rc<T>` while giving you a `Weak<T>` to the allocation,
+    /// to allow you to construct a `T` which holds a weak pointer to itself.
     ///
     /// Generally, a structure circularly referencing itself, either directly or
-    /// indirectly, should not hold a strong reference to prevent a memory leak.
-    /// In `data_fn`, initialization of `T` can make use of the weak reference
-    /// by cloning and storing it inside `T` for use at a later time.
+    /// indirectly, should not hold a strong reference to itself to prevent a memory leak.
+    /// Using this function, you get access to the weak pointer during the
+    /// initialization of `T`, before the `Rc<T>` is created, such that you can
+    /// clone and store it inside the `T`.
+    ///
+    /// `new_cyclic` first allocates the managed allocation for the `Rc<T>`,
+    /// then calls your closure, giving it a `Weak<T>` to this allocation,
+    /// and only afterwards completes the construction of the `Rc<T>` by placing
+    /// the `T` returned from your closure into the allocation.
     ///
     /// Since the new `Rc<T>` is not fully-constructed until `Rc<T>::new_cyclic`
-    /// returns, calling [`upgrade`] on the weak reference inside `data_fn` will
+    /// returns, calling [`upgrade`] on the weak reference inside your closure will
     /// fail and result in a `None` value.
     ///
     /// # Panics
+    ///
     /// If `data_fn` panics, the panic is propagated to the caller, and the
     /// temporary [`Weak<T>`] is dropped normally.
     ///
@@ -403,7 +410,12 @@ pub fn new(value: T) -> Rc<T> {
     /// impl Gadget {
     ///     /// Construct a reference counted Gadget.
     ///     fn new() -> Rc<Self> {
-    ///         Rc::new_cyclic(|me| Gadget { me: me.clone() })
+    ///         // `me` is a `Weak<Gadget>` pointing at the new allocation of the
+    ///         // `Rc` we're constructing.
+    ///         Rc::new_cyclic(|me| {
+    ///             // Create the actual struct here.
+    ///             Gadget { me: me.clone() }
+    ///         })
     ///     }
     ///
     ///     /// Return a reference counted pointer to Self.
index 1e2caddcacb19ed5c0719e6932eda6d67ce657ad..06aecd9cc1efdbd958cbce4a4248485980582ba4 100644 (file)
@@ -25,7 +25,7 @@
 #[cfg(not(no_global_oom_handling))]
 use core::slice::from_raw_parts_mut;
 use core::sync::atomic;
-use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst};
+use core::sync::atomic::Ordering::{Acquire, Relaxed, Release};
 
 #[cfg(not(no_global_oom_handling))]
 use crate::alloc::handle_alloc_error;
@@ -351,23 +351,31 @@ pub fn new(data: T) -> Arc<T> {
         unsafe { Self::from_inner(Box::leak(x).into()) }
     }
 
-    /// Constructs a new `Arc<T>` using a closure `data_fn` that has access to
-    /// a weak reference to the constructing `Arc<T>`.
+    /// Constructs a new `Arc<T>` while giving you a `Weak<T>` to the allocation,
+    /// to allow you to construct a `T` which holds a weak pointer to itself.
     ///
     /// Generally, a structure circularly referencing itself, either directly or
-    /// indirectly, should not hold a strong reference to prevent a memory leak.
-    /// In `data_fn`, initialization of `T` can make use of the weak reference
-    /// by cloning and storing it inside `T` for use at a later time.
+    /// indirectly, should not hold a strong reference to itself to prevent a memory leak.
+    /// Using this function, you get access to the weak pointer during the
+    /// initialization of `T`, before the `Arc<T>` is created, such that you can
+    /// clone and store it inside the `T`.
     ///
-    /// Since the new `Arc<T>` is not fully-constructed until
-    /// `Arc<T>::new_cyclic` returns, calling [`upgrade`] on the weak
-    /// reference inside `data_fn` will fail and result in a `None` value.
+    /// `new_cyclic` first allocates the managed allocation for the `Arc<T>`,
+    /// then calls your closure, giving it a `Weak<T>` to this allocation,
+    /// and only afterwards completes the construction of the `Arc<T>` by placing
+    /// the `T` returned from your closure into the allocation.
+    ///
+    /// Since the new `Arc<T>` is not fully-constructed until `Arc<T>::new_cyclic`
+    /// returns, calling [`upgrade`] on the weak reference inside your closure will
+    /// fail and result in a `None` value.
     ///
     /// # Panics
+    ///
     /// If `data_fn` panics, the panic is propagated to the caller, and the
     /// temporary [`Weak<T>`] is dropped normally.
     ///
     /// # Example
+    ///
     /// ```
     /// # #![allow(dead_code)]
     /// use std::sync::{Arc, Weak};
@@ -379,7 +387,12 @@ pub fn new(data: T) -> Arc<T> {
     /// impl Gadget {
     ///     /// Construct a reference counted Gadget.
     ///     fn new() -> Arc<Self> {
-    ///         Arc::new_cyclic(|me| Gadget { me: me.clone() })
+    ///         // `me` is a `Weak<Gadget>` pointing at the new allocation of the
+    ///         // `Arc` we're constructing.
+    ///         Arc::new_cyclic(|me| {
+    ///             // Create the actual struct here.
+    ///             Gadget { me: me.clone() }
+    ///         })
     ///     }
     ///
     ///     /// Return a reference counted pointer to Self.
@@ -971,7 +984,7 @@ pub fn downgrade(this: &Self) -> Weak<T> {
     #[must_use]
     #[stable(feature = "arc_counts", since = "1.15.0")]
     pub fn weak_count(this: &Self) -> usize {
-        let cnt = this.inner().weak.load(SeqCst);
+        let cnt = this.inner().weak.load(Acquire);
         // If the weak count is currently locked, the value of the
         // count was 0 just before taking the lock.
         if cnt == usize::MAX { 0 } else { cnt - 1 }
@@ -1001,7 +1014,7 @@ pub fn weak_count(this: &Self) -> usize {
     #[must_use]
     #[stable(feature = "arc_counts", since = "1.15.0")]
     pub fn strong_count(this: &Self) -> usize {
-        this.inner().strong.load(SeqCst)
+        this.inner().strong.load(Acquire)
     }
 
     /// Increments the strong reference count on the `Arc<T>` associated with the
@@ -1963,7 +1976,7 @@ pub fn upgrade(&self) -> Option<Arc<T>> {
     #[must_use]
     #[stable(feature = "weak_counts", since = "1.41.0")]
     pub fn strong_count(&self) -> usize {
-        if let Some(inner) = self.inner() { inner.strong.load(SeqCst) } else { 0 }
+        if let Some(inner) = self.inner() { inner.strong.load(Acquire) } else { 0 }
     }
 
     /// Gets an approximation of the number of `Weak` pointers pointing to this
@@ -1982,8 +1995,8 @@ pub fn strong_count(&self) -> usize {
     pub fn weak_count(&self) -> usize {
         self.inner()
             .map(|inner| {
-                let weak = inner.weak.load(SeqCst);
-                let strong = inner.strong.load(SeqCst);
+                let weak = inner.weak.load(Acquire);
+                let strong = inner.strong.load(Acquire);
                 if strong == 0 {
                     0
                 } else {
index 15d9d013997bebba375db4391854498633fe6d61..ca335d53c7caa4cab2471906db5b87580b2c69e2 100644 (file)
@@ -77,7 +77,7 @@
 #[derive(Hash)]
 #[cfg_attr(not(test), rustc_diagnostic_item = "CStr")]
 #[unstable(feature = "core_c_str", issue = "94079")]
-#[cfg_attr(not(bootstrap), lang = "CStr")]
+#[cfg_attr(not(bootstrap), rustc_has_incoherent_inherent_impls)]
 // FIXME:
 // `fn from` in `impl From<&CStr> for Box<CStr>` current implementation relies
 // on `CStr` being layout-compatible with `[u8]`.
index 45c9df0c930b900baca7c161504ea2b0f794703a..9d64c786d67b5111167a92e3b92d81c4733690fc 100644 (file)
@@ -333,6 +333,12 @@ pub trait Hasher {
     ///
     /// println!("Hash is {:x}!", hasher.finish());
     /// ```
+    ///
+    /// # Note to Implementers
+    ///
+    /// You generally should not do length-prefixing as part of implementing
+    /// this method.  It's up to the [`Hash`] implementation to call
+    /// [`Hasher::write_length_prefix`] before sequences that need it.
     #[stable(feature = "rust1", since = "1.0.0")]
     fn write(&mut self, bytes: &[u8]);
 
@@ -409,6 +415,127 @@ fn write_i128(&mut self, i: i128) {
     fn write_isize(&mut self, i: isize) {
         self.write_usize(i as usize)
     }
+
+    /// Writes a length prefix into this hasher, as part of being prefix-free.
+    ///
+    /// If you're implementing [`Hash`] for a custom collection, call this before
+    /// writing its contents to this `Hasher`.  That way
+    /// `(collection![1, 2, 3], collection![4, 5])` and
+    /// `(collection![1, 2], collection![3, 4, 5])` will provide different
+    /// sequences of values to the `Hasher`
+    ///
+    /// The `impl<T> Hash for [T]` includes a call to this method, so if you're
+    /// hashing a slice (or array or vector) via its `Hash::hash` method,
+    /// you should **not** call this yourself.
+    ///
+    /// This method is only for providing domain separation.  If you want to
+    /// hash a `usize` that represents part of the *data*, then it's important
+    /// that you pass it to [`Hasher::write_usize`] instead of to this method.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(hasher_prefixfree_extras)]
+    /// # // Stubs to make the `impl` below pass the compiler
+    /// # struct MyCollection<T>(Option<T>);
+    /// # impl<T> MyCollection<T> {
+    /// #     fn len(&self) -> usize { todo!() }
+    /// # }
+    /// # impl<'a, T> IntoIterator for &'a MyCollection<T> {
+    /// #     type Item = T;
+    /// #     type IntoIter = std::iter::Empty<T>;
+    /// #     fn into_iter(self) -> Self::IntoIter { todo!() }
+    /// # }
+    ///
+    /// use std::hash::{Hash, Hasher};
+    /// impl<T: Hash> Hash for MyCollection<T> {
+    ///     fn hash<H: Hasher>(&self, state: &mut H) {
+    ///         state.write_length_prefix(self.len());
+    ///         for elt in self {
+    ///             elt.hash(state);
+    ///         }
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// # Note to Implementers
+    ///
+    /// If you've decided that your `Hasher` is willing to be susceptible to
+    /// Hash-DoS attacks, then you might consider skipping hashing some or all
+    /// of the `len` provided in the name of increased performance.
+    #[inline]
+    #[unstable(feature = "hasher_prefixfree_extras", issue = "96762")]
+    fn write_length_prefix(&mut self, len: usize) {
+        self.write_usize(len);
+    }
+
+    /// Writes a single `str` into this hasher.
+    ///
+    /// If you're implementing [`Hash`], you generally do not need to call this,
+    /// as the `impl Hash for str` does, so you should prefer that instead.
+    ///
+    /// This includes the domain separator for prefix-freedom, so you should
+    /// **not** call `Self::write_length_prefix` before calling this.
+    ///
+    /// # Note to Implementers
+    ///
+    /// There are at least two reasonable default ways to implement this.
+    /// Which one will be the default is not yet decided, so for now
+    /// you probably want to override it specifically.
+    ///
+    /// ## The general answer
+    ///
+    /// It's always correct to implement this with a length prefix:
+    ///
+    /// ```
+    /// # #![feature(hasher_prefixfree_extras)]
+    /// # struct Foo;
+    /// # impl std::hash::Hasher for Foo {
+    /// # fn finish(&self) -> u64 { unimplemented!() }
+    /// # fn write(&mut self, _bytes: &[u8]) { unimplemented!() }
+    /// fn write_str(&mut self, s: &str) {
+    ///     self.write_length_prefix(s.len());
+    ///     self.write(s.as_bytes());
+    /// }
+    /// # }
+    /// ```
+    ///
+    /// And, if your `Hasher` works in `usize` chunks, this is likely a very
+    /// efficient way to do it, as anything more complicated may well end up
+    /// slower than just running the round with the length.
+    ///
+    /// ## If your `Hasher` works byte-wise
+    ///
+    /// One nice thing about `str` being UTF-8 is that the `b'\xFF'` byte
+    /// never happens.  That means that you can append that to the byte stream
+    /// being hashed and maintain prefix-freedom:
+    ///
+    /// ```
+    /// # #![feature(hasher_prefixfree_extras)]
+    /// # struct Foo;
+    /// # impl std::hash::Hasher for Foo {
+    /// # fn finish(&self) -> u64 { unimplemented!() }
+    /// # fn write(&mut self, _bytes: &[u8]) { unimplemented!() }
+    /// fn write_str(&mut self, s: &str) {
+    ///     self.write(s.as_bytes());
+    ///     self.write_u8(0xff);
+    /// }
+    /// # }
+    /// ```
+    ///
+    /// This does require that your implementation not add extra padding, and
+    /// thus generally requires that you maintain a buffer, running a round
+    /// only once that buffer is full (or `finish` is called).
+    ///
+    /// That's because if `write` pads data out to a fixed chunk size, it's
+    /// likely that it does it in such a way that `"a"` and `"a\x00"` would
+    /// end up hashing the same sequence of things, introducing conflicts.
+    #[inline]
+    #[unstable(feature = "hasher_prefixfree_extras", issue = "96762")]
+    fn write_str(&mut self, s: &str) {
+        self.write(s.as_bytes());
+        self.write_u8(0xff);
+    }
 }
 
 #[stable(feature = "indirect_hasher_impl", since = "1.22.0")]
@@ -455,6 +582,12 @@ fn write_i128(&mut self, i: i128) {
     fn write_isize(&mut self, i: isize) {
         (**self).write_isize(i)
     }
+    fn write_length_prefix(&mut self, len: usize) {
+        (**self).write_length_prefix(len)
+    }
+    fn write_str(&mut self, s: &str) {
+        (**self).write_str(s)
+    }
 }
 
 /// A trait for creating instances of [`Hasher`].
@@ -709,8 +842,7 @@ fn hash<H: Hasher>(&self, state: &mut H) {
     impl Hash for str {
         #[inline]
         fn hash<H: Hasher>(&self, state: &mut H) {
-            state.write(self.as_bytes());
-            state.write_u8(0xff)
+            state.write_str(self);
         }
     }
 
@@ -767,7 +899,7 @@ macro_rules! last_type {
     impl<T: Hash> Hash for [T] {
         #[inline]
         fn hash<H: Hasher>(&self, state: &mut H) {
-            self.len().hash(state);
+            state.write_length_prefix(self.len());
             Hash::hash_slice(self, state)
         }
     }
index b9443e30074b00fc9ef5cc288120e6f5abba425f..9d7daf1f1a075f9cb9a390eafc8fabcb2d44487f 100644 (file)
@@ -233,6 +233,11 @@ fn write(&mut self, msg: &[u8]) {
         self.0.hasher.write(msg)
     }
 
+    #[inline]
+    fn write_str(&mut self, s: &str) {
+        self.0.hasher.write_str(s);
+    }
+
     #[inline]
     fn finish(&self) -> u64 {
         self.0.hasher.finish()
@@ -246,6 +251,11 @@ fn write(&mut self, msg: &[u8]) {
         self.hasher.write(msg)
     }
 
+    #[inline]
+    fn write_str(&mut self, s: &str) {
+        self.hasher.write_str(s);
+    }
+
     #[inline]
     fn finish(&self) -> u64 {
         self.hasher.finish()
@@ -307,6 +317,14 @@ fn write(&mut self, msg: &[u8]) {
         self.ntail = left;
     }
 
+    #[inline]
+    fn write_str(&mut self, s: &str) {
+        // This hasher works byte-wise, and `0xFF` cannot show up in a `str`,
+        // so just hashing the one extra byte is enough to be prefix-free.
+        self.write(s.as_bytes());
+        self.write_u8(0xFF);
+    }
+
     #[inline]
     fn finish(&self) -> u64 {
         let mut state = self.state;
index 7ef78e0b48af161015ef27375dee5755afcdc9df..5d4c9ba73951a6d0026699b5cb7d8bd323dcd389 100644 (file)
@@ -190,7 +190,7 @@ extern "rust-call" fn call_once(self, ($( $arg, )*): ($( $ArgTy, )*)) -> $Return
 ///
 /// # Example
 ///
-/// ```
+/// ```ignore(cannot-test-this-because-non-exported-macro)
 /// cfg_if! {
 ///     if #[cfg(unix)] {
 ///         fn foo() { /* unix specific functionality */ }
index bddbe2b9b0df104da8c8c9b500835c2fe86969ad..4e32c514d5f68fbadbbe12732b48ac5f5658eeab 100644 (file)
@@ -981,7 +981,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
     ///
     /// Turning a pointer into a `usize`:
     ///
-    /// ```
+    /// ```no_run
     /// let ptr = &0;
     /// let ptr_num_transmute = unsafe {
     ///     std::mem::transmute::<&i32, usize>(ptr)
index 1bf447347408d0ee6e7eea68e5626b868e274ad9..b5c7982a5a871170a21dcdb1395fc827313a2bc0 100644 (file)
@@ -2015,7 +2015,12 @@ pub const fn rem_euclid(self, rhs: Self) -> Self {
         ///
         /// # Panics
         ///
-        /// This function will panic if `rhs` is 0 or the division results in overflow.
+        /// This function will panic if `rhs` is zero.
+        ///
+        /// ## Overflow behavior
+        ///
+        /// On overflow, this function will panic if overflow checks are enabled (default in debug
+        /// mode) and wrap if overflow checks are disabled (default in release mode).
         ///
         /// # Examples
         ///
@@ -2050,7 +2055,12 @@ pub const fn div_floor(self, rhs: Self) -> Self {
         ///
         /// # Panics
         ///
-        /// This function will panic if `rhs` is 0 or the division results in overflow.
+        /// This function will panic if `rhs` is zero.
+        ///
+        /// ## Overflow behavior
+        ///
+        /// On overflow, this function will panic if overflow checks are enabled (default in debug
+        /// mode) and wrap if overflow checks are disabled (default in release mode).
         ///
         /// # Examples
         ///
@@ -2088,7 +2098,12 @@ pub const fn div_ceil(self, rhs: Self) -> Self {
         ///
         /// # Panics
         ///
-        /// This function will panic if `rhs` is 0 or the operation results in overflow.
+        /// This function will panic if `rhs` is zero.
+        ///
+        /// ## Overflow behavior
+        ///
+        /// On overflow, this function will panic if overflow checks are enabled (default in debug
+        /// mode) and wrap if overflow checks are disabled (default in release mode).
         ///
         /// # Examples
         ///
@@ -2157,7 +2172,6 @@ pub const fn next_multiple_of(self, rhs: Self) -> Self {
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
-        #[rustc_inherit_overflow_checks]
         pub const fn checked_next_multiple_of(self, rhs: Self) -> Option<Self> {
             // This would otherwise fail when calculating `r` when self == T::MIN.
             if rhs == -1 {
index ce52e4773ce1f4388ab289786f02c0c194727348..048d6bafcdecd018dae127660878b36dfe565023 100644 (file)
@@ -2020,7 +2020,7 @@ pub const fn rem_euclid(self, rhs: Self) -> Self {
         ///
         /// # Panics
         ///
-        /// This function will panic if `rhs` is 0.
+        /// This function will panic if `rhs` is zero.
         ///
         /// # Examples
         ///
@@ -2034,7 +2034,6 @@ pub const fn rem_euclid(self, rhs: Self) -> Self {
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline(always)]
-        #[rustc_inherit_overflow_checks]
         pub const fn div_floor(self, rhs: Self) -> Self {
             self / rhs
         }
@@ -2043,7 +2042,12 @@ pub const fn div_floor(self, rhs: Self) -> Self {
         ///
         /// # Panics
         ///
-        /// This function will panic if `rhs` is 0.
+        /// This function will panic if `rhs` is zero.
+        ///
+        /// ## Overflow behavior
+        ///
+        /// On overflow, this function will panic if overflow checks are enabled (default in debug
+        /// mode) and wrap if overflow checks are disabled (default in release mode).
         ///
         /// # Examples
         ///
@@ -2073,7 +2077,12 @@ pub const fn div_ceil(self, rhs: Self) -> Self {
         ///
         /// # Panics
         ///
-        /// This function will panic if `rhs` is 0 or the operation results in overflow.
+        /// This function will panic if `rhs` is zero.
+        ///
+        /// ## Overflow behavior
+        ///
+        /// On overflow, this function will panic if overflow checks are enabled (default in debug
+        /// mode) and wrap if overflow checks are disabled (default in release mode).
         ///
         /// # Examples
         ///
@@ -2097,7 +2106,7 @@ pub const fn next_multiple_of(self, rhs: Self) -> Self {
         }
 
         /// Calculates the smallest value greater than or equal to `self` that
-        /// is a multiple of `rhs`. Returns `None` is `rhs` is zero or the
+        /// is a multiple of `rhs`. Returns `None` if `rhs` is zero or the
         /// operation would result in overflow.
         ///
         /// # Examples
@@ -2115,7 +2124,6 @@ pub const fn next_multiple_of(self, rhs: Self) -> Self {
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
-        #[rustc_inherit_overflow_checks]
         pub const fn checked_next_multiple_of(self, rhs: Self) -> Option<Self> {
             match try_opt!(self.checked_rem(rhs)) {
                 0 => Some(self),
index 7ef2e95542bba6b3a0ec1e7fb92491cc88c240dc..45964c3a444fec86f6b28231cbab12bf3f7cdcf8 100644 (file)
@@ -518,7 +518,7 @@ pub const fn wrapping_offset(self, count: isize) -> *const T
     }
 
     /// Calculates the distance between two pointers. The returned value is in
-    /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
+    /// units of T: the distance in bytes divided by `mem::size_of::<T>()`.
     ///
     /// This function is the inverse of [`offset`].
     ///
index 56f9c84f5af6f3341db11472f75528917fc9b76e..ff5207c1a06e1cd09e48d915ff435f5ebcc06fd7 100644 (file)
@@ -696,7 +696,7 @@ pub const fn guaranteed_eq(self, other: *mut T) -> bool
     }
 
     /// Calculates the distance between two pointers. The returned value is in
-    /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
+    /// units of T: the distance in bytes divided by `mem::size_of::<T>()`.
     ///
     /// This function is the inverse of [`offset`].
     ///
index a173e461c606f6e69d5ccbb170555fdc726c326d..5bc6aac1778f50a62249da8c9a5778d0b1fd09ab 100644 (file)
@@ -20,6 +20,10 @@ fn write(&mut self, buf: &[u8]) {
             self.hash += *byte as u64;
         }
     }
+    fn write_str(&mut self, s: &str) {
+        self.write(s.as_bytes());
+        self.write_u8(0xFF);
+    }
     fn finish(&self) -> u64 {
         self.hash
     }
index 21d600ac557b876d1a89b55c8a5b02920714a2e4..31b764a6e2d52d9c08e9453502de313cc36acc2a 100644 (file)
@@ -36,6 +36,7 @@
 #![feature(future_join)]
 #![feature(future_poll_fn)]
 #![feature(array_from_fn)]
+#![feature(hasher_prefixfree_extras)]
 #![feature(hashmap_internals)]
 #![feature(try_find)]
 #![feature(inline_const)]
index 72c0b225991f6bf116a020b6f8edf69d744bad81..1ddd20f33d0b17cec89d08e393422cbb0dd0b5ce 100644 (file)
@@ -1 +1 @@
-int_module!(i128, i128);
+int_module!(i128);
index f5544b914b73dc70b8415fd636c2069def6b9fa1..c7aa9fff964ede3deee081616195518c3e9e1232 100644 (file)
@@ -1 +1 @@
-int_module!(i16, i16);
+int_module!(i16);
index 4acc760ffac9907e0d22509c140a679a82009572..efd5b1596a80d25e09d605e8f088ec8c34c94509 100644 (file)
@@ -1,4 +1,4 @@
-int_module!(i32, i32);
+int_module!(i32);
 
 #[test]
 fn test_arith_operation() {
index fa4d2ab6638d7b4dae008c54def2fc1863e13c36..93d23c10adf7e7fb3c5672a7addb81d39c02ed56 100644 (file)
@@ -1 +1 @@
-int_module!(i64, i64);
+int_module!(i64);
index ccec6915fe090c62eb55c2cccca7b38de51e9cfc..887d4f17d25ffd204d1e15ab5da12f2440b82ff9 100644 (file)
@@ -1 +1 @@
-int_module!(i8, i8);
+int_module!(i8);
index d2d655ea2c75015959b518a695d6b4dd8f63240a..8b84a78e6be08823a33a1abb9ad07f7724778d73 100644 (file)
@@ -1,9 +1,9 @@
 macro_rules! int_module {
-    ($T:ident, $T_i:ident) => {
+    ($T:ident) => {
         #[cfg(test)]
         mod tests {
             use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
-            use core::$T_i::*;
+            use core::$T::*;
 
             use crate::num;
 
index 716d1836f2c0ef17ef234aed869fd45f0b5df2f9..a7b0f9effefb97f0431350b1bec9ca3875f7abd1 100644 (file)
@@ -1 +1 @@
-uint_module!(u128, u128);
+uint_module!(u128);
index 435b914224c5a01e1eb2272adcd3e3e4681c5f59..010596a34a56cbce1ffe2f96ec065d073289e75c 100644 (file)
@@ -1 +1 @@
-uint_module!(u16, u16);
+uint_module!(u16);
index 71dc005dea3705090382ba77a6314cd4a8dd8073..687d3bbaa907ffe7e9da11e7edda52eee8447fda 100644 (file)
@@ -1 +1 @@
-uint_module!(u32, u32);
+uint_module!(u32);
index b498ebc52042e500592421dac3e3bf6ba4b10b5a..ee55071e949960142d374cfd21b24a01f2201cdb 100644 (file)
@@ -1 +1 @@
-uint_module!(u64, u64);
+uint_module!(u64);
index 68e938be704ac785cef52f518b356a51c3d2c1dc..12b038ce0f75cab97dceafa4f534eff2b4c06b20 100644 (file)
@@ -1 +1 @@
-uint_module!(u8, u8);
+uint_module!(u8);
index 49f8f1f13fad46ffc0a79fd228b5e4f8bbf0f5e2..93ae620c23302413da250988dd8b0592d8c19d8a 100644 (file)
@@ -1,9 +1,9 @@
 macro_rules! uint_module {
-    ($T:ident, $T_i:ident) => {
+    ($T:ident) => {
         #[cfg(test)]
         mod tests {
             use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
-            use core::$T_i::*;
+            use core::$T::*;
             use std::str::FromStr;
 
             use crate::num;
index f1c5eaad868e99d495224fd2cee4111f4e9c17b6..6f7c6305afc14ec91e1be195cd68c6f4dc3a1735 100644 (file)
@@ -703,11 +703,12 @@ pub enum Delimiter {
     /// `[ ... ]`
     #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
     Bracket,
-    /// `Ø ... Ã˜`
+    /// `/*«*/ ... /*»*/`
     /// An invisible delimiter, that may, for example, appear around tokens coming from a
     /// "macro variable" `$var`. It is important to preserve operator priorities in cases like
     /// `$var * 3` where `$var` is `1 + 2`.
-    /// Invisible delimiters might not survive roundtrip of a token stream through a string.
+    /// Invisible delimiters are not directly writable in normal Rust code except as comments.
+    /// Therefore, they might not survive a roundtrip of a token stream through a string.
     #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
     None,
 }
index f2d6583206035e9c966ffcd0887130cf37898893..d0d7d480fe8e23d1fe77bce06829fda56987c072 100644 (file)
@@ -15,7 +15,7 @@ cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] }
 panic_unwind = { path = "../panic_unwind", optional = true }
 panic_abort = { path = "../panic_abort" }
 core = { path = "../core" }
-libc = { version = "0.2.116", default-features = false, features = ['rustc-dep-of-std'] }
+libc = { version = "0.2.125", default-features = false, features = ['rustc-dep-of-std'] }
 compiler_builtins = { version = "0.1.71" }
 profiler_builtins = { path = "../profiler_builtins", optional = true }
 unwind = { path = "../unwind" }
index 0c638192264e6d60fee5125ccfa73211b1d68d1f..e38368790e69a10693aa1e7f8e5311c8bd79edd5 100644 (file)
@@ -3006,11 +3006,19 @@ fn default() -> DefaultHasher {
 
 #[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
 impl Hasher for DefaultHasher {
+    // The underlying `SipHasher13` doesn't override the other
+    // `write_*` methods, so it's ok not to forward them here.
+
     #[inline]
     fn write(&mut self, msg: &[u8]) {
         self.0.write(msg)
     }
 
+    #[inline]
+    fn write_str(&mut self, s: &str) {
+        self.0.write_str(s);
+    }
+
     #[inline]
     fn finish(&self) -> u64 {
         self.0.finish()
index 9b5e5d6c0cc4b1d377896ba1ba8d7dc444a2bfe3..dd316bdb2c6ce5476a31b95a23801ba248b92232 100644 (file)
@@ -1222,6 +1222,23 @@ pub(crate) fn display(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result
     }
 }
 
+#[unstable(feature = "slice_concat_ext", issue = "27747")]
+impl<S: Borrow<OsStr>> alloc::slice::Join<&OsStr> for [S] {
+    type Output = OsString;
+
+    fn join(slice: &Self, sep: &OsStr) -> OsString {
+        let Some(first) = slice.first() else {
+            return OsString::new();
+        };
+        let first = first.borrow().to_owned();
+        slice[1..].iter().fold(first, |mut a, b| {
+            a.push(sep);
+            a.push(b.borrow());
+            a
+        })
+    }
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl Borrow<OsStr> for OsString {
     #[inline]
index 283f2b577e89607bdd9940cc8b4c4d4300b35888..d7926749aae2090416729d0aec53db4bf0a6b276 100644 (file)
@@ -84,6 +84,20 @@ fn test_os_string_reserve_exact() {
     assert!(os_string.capacity() >= 33)
 }
 
+#[test]
+fn test_os_string_join() {
+    let strings = [OsStr::new("hello"), OsStr::new("dear"), OsStr::new("world")];
+    assert_eq!("hello", strings[..1].join(OsStr::new(" ")));
+    assert_eq!("hello dear world", strings.join(OsStr::new(" ")));
+    assert_eq!("hellodearworld", strings.join(OsStr::new("")));
+    assert_eq!("hello.\n dear.\n world", strings.join(OsStr::new(".\n ")));
+
+    assert_eq!("dear world", strings[1..].join(&OsString::from(" ")));
+
+    let strings_abc = [OsString::from("a"), OsString::from("b"), OsString::from("c")];
+    assert_eq!("a b c", strings_abc.join(OsStr::new(" ")));
+}
+
 #[test]
 fn test_os_string_default() {
     let os_string: OsString = Default::default();
index 97c30c422827e3f105e50263a99aa742f6729995..c394865d886e85bdc8e120c7753006897b6b9965 100644 (file)
 #![feature(intra_doc_pointers)]
 #![feature(lang_items)]
 #![feature(let_chains)]
+#![feature(let_else)]
 #![feature(linkage)]
 #![feature(min_specialization)]
 #![feature(must_not_suspend)]
 #![feature(exact_size_is_empty)]
 #![feature(extend_one)]
 #![feature(float_minimum_maximum)]
+#![feature(hasher_prefixfree_extras)]
 #![feature(hashmap_internals)]
 #![feature(int_error_internals)]
 #![feature(maybe_uninit_slice)]
 #![feature(toowned_clone_into)]
 #![feature(try_reserve_kind)]
 #![feature(vec_into_raw_parts)]
+#![feature(slice_concat_trait)]
 //
 // Library features (unwind):
 #![feature(panic_unwind)]
index 036f2919976074653f589471a2f24469e873e920..f629a1a0f991e0135537b55b1770172d37e72810 100644 (file)
@@ -138,6 +138,7 @@ pub struct Ipv4Addr {
 ///
 /// To convert from an IPv4 address to an IPv4-mapped IPv6 address, use [`Ipv4Addr::to_ipv6_mapped`].
 /// Use [`Ipv6Addr::to_ipv4`] to convert an IPv4-mapped IPv6 address to the canonical IPv4 address.
+/// Note that this will also convert the IPv6 loopback address `::1` to `0.0.0.1`.
 ///
 /// [IETF RFC 4291 Section 2.5.5.2]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2
 ///
@@ -1673,7 +1674,9 @@ pub const fn to_ipv4_mapped(&self) -> Option<Ipv4Addr> {
     /// or an [IPv4-mapped] address as defined in [IETF RFC 4291 section 2.5.5.2],
     /// otherwise returns [`None`].
     ///
-    /// `::a.b.c.d` and `::ffff:a.b.c.d` become `a.b.c.d`
+    /// Note that this will return an [`IPv4` address] for the IPv6 loopback address `::1`.
+    ///
+    /// `::a.b.c.d` and `::ffff:a.b.c.d` become `a.b.c.d`. `::1` becomes `0.0.0.1`.
     /// All addresses *not* starting with either all zeroes or `::ffff` will return `None`.
     ///
     /// [`IPv4` address]: Ipv4Addr
index f7a00676daaed7e7de26fbfd4a9ed1c4824287c3..24f467f0b03d7a3c750348e9b969b3ae72cbd799 100644 (file)
@@ -188,24 +188,3 @@ fn wait_timeout_wake() {
         break;
     }
 }
-
-#[test]
-#[should_panic]
-#[cfg(all(unix, not(target_os = "linux"), not(target_os = "android")))]
-fn two_mutexes() {
-    let m = Arc::new(Mutex::new(()));
-    let m2 = m.clone();
-    let c = Arc::new(Condvar::new());
-    let c2 = c.clone();
-
-    let mut g = m.lock().unwrap();
-    let _t = thread::spawn(move || {
-        let _g = m2.lock().unwrap();
-        c2.notify_one();
-    });
-    g = c.wait(g).unwrap();
-    drop(g);
-
-    let m = Mutex::new(());
-    let _ = c.wait(m.lock().unwrap()).unwrap();
-}
index c12ee169e797aa78c904d3e5d125e16ceed4145d..678c6f0d6ead1b6ab299a196ba10974517d551d1 100644 (file)
@@ -1,7 +1,10 @@
 #![cfg(any(
     target_os = "linux",
     target_os = "android",
-    all(target_os = "emscripten", target_feature = "atomics")
+    all(target_os = "emscripten", target_feature = "atomics"),
+    target_os = "freebsd",
+    target_os = "openbsd",
+    target_os = "dragonfly",
 ))]
 
 use crate::sync::atomic::AtomicU32;
@@ -12,7 +15,7 @@
 /// Returns directly if the futex doesn't hold the expected value.
 ///
 /// Returns false on timeout, and true in all other cases.
-#[cfg(any(target_os = "linux", target_os = "android"))]
+#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
 pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
     use super::time::Timespec;
     use crate::ptr::null;
@@ -30,18 +33,43 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -
             return true;
         }
 
-        // Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT to be able to give an
-        // absolute time rather than a relative time.
         let r = unsafe {
-            libc::syscall(
-                libc::SYS_futex,
-                futex as *const AtomicU32,
-                libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG,
-                expected,
-                timespec.as_ref().map_or(null(), |t| &t.t as *const libc::timespec),
-                null::<u32>(), // This argument is unused for FUTEX_WAIT_BITSET.
-                !0u32,         // A full bitmask, to make it behave like a regular FUTEX_WAIT.
-            )
+            cfg_if::cfg_if! {
+                if #[cfg(target_os = "freebsd")] {
+                    // FreeBSD doesn't have futex(), but it has
+                    // _umtx_op(UMTX_OP_WAIT_UINT_PRIVATE), which is nearly
+                    // identical. It supports absolute timeouts through a flag
+                    // in the _umtx_time struct.
+                    let umtx_timeout = timespec.map(|t| libc::_umtx_time {
+                        _timeout: t.t,
+                        _flags: libc::UMTX_ABSTIME,
+                        _clockid: libc::CLOCK_MONOTONIC as u32,
+                    });
+                    let umtx_timeout_ptr = umtx_timeout.as_ref().map_or(null(), |t| t as *const _);
+                    let umtx_timeout_size = umtx_timeout.as_ref().map_or(0, |t| crate::mem::size_of_val(t));
+                    libc::_umtx_op(
+                        futex as *const AtomicU32 as *mut _,
+                        libc::UMTX_OP_WAIT_UINT_PRIVATE,
+                        expected as libc::c_ulong,
+                        crate::ptr::invalid_mut(umtx_timeout_size),
+                        umtx_timeout_ptr as *mut _,
+                    )
+                } else if #[cfg(any(target_os = "linux", target_os = "android"))] {
+                    // Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT to be able to give an
+                    // absolute time rather than a relative time.
+                    libc::syscall(
+                        libc::SYS_futex,
+                        futex as *const AtomicU32,
+                        libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG,
+                        expected,
+                        timespec.as_ref().map_or(null(), |t| &t.t as *const libc::timespec),
+                        null::<u32>(), // This argument is unused for FUTEX_WAIT_BITSET.
+                        !0u32,         // A full bitmask, to make it behave like a regular FUTEX_WAIT.
+                    )
+                } else {
+                    compile_error!("unknown target_os");
+                }
+            }
         };
 
         match (r < 0).then(super::os::errno) {
@@ -56,31 +84,133 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -
 ///
 /// Returns true if this actually woke up such a thread,
 /// or false if no thread was waiting on this futex.
+///
+/// On some platforms, this always returns false.
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn futex_wake(futex: &AtomicU32) -> bool {
+    let ptr = futex as *const AtomicU32;
+    let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG;
+    unsafe { libc::syscall(libc::SYS_futex, ptr, op, 1) > 0 }
+}
+
+/// Wake up all threads that are waiting on futex_wait on this futex.
 #[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn futex_wake_all(futex: &AtomicU32) {
+    let ptr = futex as *const AtomicU32;
+    let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG;
+    unsafe {
+        libc::syscall(libc::SYS_futex, ptr, op, i32::MAX);
+    }
+}
+
+// FreeBSD doesn't tell us how many threads are woken up, so this always returns false.
+#[cfg(target_os = "freebsd")]
 pub fn futex_wake(futex: &AtomicU32) -> bool {
+    use crate::ptr::null_mut;
     unsafe {
-        libc::syscall(
-            libc::SYS_futex,
-            futex as *const AtomicU32,
-            libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG,
+        libc::_umtx_op(
+            futex as *const AtomicU32 as *mut _,
+            libc::UMTX_OP_WAKE_PRIVATE,
             1,
-        ) > 0
+            null_mut(),
+            null_mut(),
+        )
+    };
+    false
+}
+
+#[cfg(target_os = "freebsd")]
+pub fn futex_wake_all(futex: &AtomicU32) {
+    use crate::ptr::null_mut;
+    unsafe {
+        libc::_umtx_op(
+            futex as *const AtomicU32 as *mut _,
+            libc::UMTX_OP_WAKE_PRIVATE,
+            i32::MAX as libc::c_ulong,
+            null_mut(),
+            null_mut(),
+        )
+    };
+}
+
+#[cfg(target_os = "openbsd")]
+pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
+    use crate::convert::TryInto;
+    use crate::ptr::{null, null_mut};
+    let timespec = timeout.and_then(|d| {
+        Some(libc::timespec {
+            // Sleep forever if the timeout is longer than fits in a timespec.
+            tv_sec: d.as_secs().try_into().ok()?,
+            // This conversion never truncates, as subsec_nanos is always <1e9.
+            tv_nsec: d.subsec_nanos() as _,
+        })
+    });
+
+    let r = unsafe {
+        libc::futex(
+            futex as *const AtomicU32 as *mut u32,
+            libc::FUTEX_WAIT,
+            expected as i32,
+            timespec.as_ref().map_or(null(), |t| t as *const libc::timespec),
+            null_mut(),
+        )
+    };
+
+    r == 0 || super::os::errno() != libc::ETIMEDOUT
+}
+
+#[cfg(target_os = "openbsd")]
+pub fn futex_wake(futex: &AtomicU32) -> bool {
+    use crate::ptr::{null, null_mut};
+    unsafe {
+        libc::futex(futex as *const AtomicU32 as *mut u32, libc::FUTEX_WAKE, 1, null(), null_mut())
+            > 0
     }
 }
 
-/// Wake up all threads that are waiting on futex_wait on this futex.
-#[cfg(any(target_os = "linux", target_os = "android"))]
+#[cfg(target_os = "openbsd")]
 pub fn futex_wake_all(futex: &AtomicU32) {
+    use crate::ptr::{null, null_mut};
     unsafe {
-        libc::syscall(
-            libc::SYS_futex,
-            futex as *const AtomicU32,
-            libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG,
+        libc::futex(
+            futex as *const AtomicU32 as *mut u32,
+            libc::FUTEX_WAKE,
             i32::MAX,
+            null(),
+            null_mut(),
         );
     }
 }
 
+#[cfg(target_os = "dragonfly")]
+pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
+    use crate::convert::TryFrom;
+
+    // A timeout of 0 means infinite.
+    // We round smaller timeouts up to 1 millisecond.
+    // Overflows are rounded up to an infinite timeout.
+    let timeout_ms =
+        timeout.and_then(|d| Some(i32::try_from(d.as_millis()).ok()?.max(1))).unwrap_or(0);
+
+    let r = unsafe {
+        libc::umtx_sleep(futex as *const AtomicU32 as *const i32, expected as i32, timeout_ms)
+    };
+
+    r == 0 || super::os::errno() != libc::ETIMEDOUT
+}
+
+// DragonflyBSD doesn't tell us how many threads are woken up, so this always returns false.
+#[cfg(target_os = "dragonfly")]
+pub fn futex_wake(futex: &AtomicU32) -> bool {
+    unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, 1) };
+    false
+}
+
+#[cfg(target_os = "dragonfly")]
+pub fn futex_wake_all(futex: &AtomicU32) {
+    unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, i32::MAX) };
+}
+
 #[cfg(target_os = "emscripten")]
 extern "C" {
     fn emscripten_futex_wake(addr: *const AtomicU32, count: libc::c_int) -> libc::c_int;
index e42edb2585834da8d7fb6c94d4b0898055ad7f0a..57f6d58840db18eb3800fc216a49ce4e09beba36 100644 (file)
@@ -284,6 +284,10 @@ fn wake_writer_or_readers(&self, mut state: u32) {
     fn wake_writer(&self) -> bool {
         self.writer_notify.fetch_add(1, Release);
         futex_wake(&self.writer_notify)
+        // Note that FreeBSD and DragonFlyBSD don't tell us whether they woke
+        // up any threads or not, and always return `false` here. That still
+        // results in correct behaviour: it just means readers get woken up as
+        // well in case both readers and writers were waiting.
     }
 
     /// Spin for a while, but stop directly at the given condition.
index 3e39c8b9b23e7a00ab22c9c317d49d97e73d45a2..04c5c489fc9b94bb44fc97a40e265e88416bdb28 100644 (file)
@@ -3,6 +3,9 @@
         target_os = "linux",
         target_os = "android",
         all(target_os = "emscripten", target_feature = "atomics"),
+        target_os = "freebsd",
+        target_os = "openbsd",
+        target_os = "dragonfly",
     ))] {
         mod futex;
         mod futex_rwlock;
index fd83f2f73d6d66d8ccff49bf8265e5fe5946dc3d..cf37c01598bf30efb9c86600559654dc494b9f96 100644 (file)
@@ -3,7 +3,10 @@
 #![cfg(not(any(
     target_os = "linux",
     target_os = "android",
-    all(target_os = "emscripten", target_feature = "atomics")
+    all(target_os = "emscripten", target_feature = "atomics"),
+    target_os = "freebsd",
+    target_os = "openbsd",
+    target_os = "dragonfly",
 )))]
 
 use crate::cell::UnsafeCell;
index ea0204cd357ba5605b5b253a89dd8945e43e743a..c789a388e05adf77e5992c424a1db6d25813616c 100644 (file)
@@ -3,6 +3,9 @@
         target_os = "linux",
         target_os = "android",
         all(target_arch = "wasm32", target_feature = "atomics"),
+        target_os = "freebsd",
+        target_os = "openbsd",
+        target_os = "dragonfly",
     ))] {
         mod futex;
         pub use futex::Parker;
diff --git a/src/doc/unstable-book/src/language-features/debugger-visualizer.md b/src/doc/unstable-book/src/language-features/debugger-visualizer.md
new file mode 100644 (file)
index 0000000..4ab482f
--- /dev/null
@@ -0,0 +1,25 @@
+# `debugger_visualizer`
+
+The tracking issue for this feature is: [#95939]
+
+[#95939]: https://github.com/rust-lang/rust/issues/95939
+
+------------------------
+
+The `debugger_visualizer` attribute can be used to instruct the compiler
+to embed a debugger visualizer file into the PDB/ELF generated by `rustc`.
+
+## Examples
+
+``` rust,ignore (partial-example)
+#![feature(debugger_visualizer)]
+#![debugger_visualizer(natvis_file = "foo.natvis")]
+struct Foo {
+
+}
+```
+
+## Limitations
+
+Currently, this feature only supports embedding Natvis files on `-windows-msvc`
+targets when using the MSVC linker via the `natvis_file` meta item.
index 82e367427ef6ff757d9a6838e3a03cb4c828bf1e..93ccf60a1de455278d4fe6db8c2fea8891611638 100644 (file)
@@ -1225,16 +1225,6 @@ fn nested_visit_map(&mut self) -> Self::Map {
 
     fn visit_item(&mut self, item: &'hir hir::Item<'_>) {
         let name = match &item.kind {
-            hir::ItemKind::Macro(ref macro_def, _) => {
-                // FIXME(#88038): Non exported macros have historically not been tested,
-                // but we really ought to start testing them.
-                let def_id = item.def_id.to_def_id();
-                if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) {
-                    intravisit::walk_item(self, item);
-                    return;
-                }
-                item.ident.to_string()
-            }
             hir::ItemKind::Impl(impl_) => {
                 rustc_hir_pretty::id_to_string(&self.map, impl_.self_ty.hir_id)
             }
index 81c12be8e83c0cdf7d4f99264465c608e4da4dc5..eb3ce37e313d065ef6387c0a03e57a0194ac8d16 100644 (file)
@@ -1333,6 +1333,11 @@ pre.rust {
        border-top: 2px solid;
 }
 
+#titles > button:first-child:last-child {
+       margin-right: 1px;
+       width: calc(100% - 1px);
+}
+
 #titles > button:not(:last-child) {
        margin-right: 1px;
        width: calc(33.3% - 1px);
@@ -1401,6 +1406,18 @@ pre.rust {
        cursor: pointer;
 }
 
+@keyframes rotating {
+       from {
+               transform: rotate(0deg);
+       }
+       to {
+               transform: rotate(360deg);
+       }
+}
+#settings-menu.rotate img {
+       animation: rotating 2s linear infinite;
+}
+
 #help-button {
        font-family: "Fira Sans", Arial, sans-serif;
        text-align: center;
index 1dfd9c762c46ef0b8be771cf229c2520de1b10f9..66a7d484f33b0f5523674a87ed744b91c986645a 100644 (file)
@@ -7,6 +7,8 @@
 /* global onEach, onEachLazy, removeClass */
 /* global switchTheme, useSystemTheme */
 
+"use strict";
+
 if (!String.prototype.startsWith) {
     String.prototype.startsWith = function(searchString, position) {
         position = position || 0;
@@ -292,17 +294,18 @@ function loadCss(cssFileName) {
 }
 
 (function() {
-    "use strict";
-
     function loadScript(url) {
         const script = document.createElement('script');
         script.src = url;
         document.head.append(script);
     }
 
-
     getSettingsButton().onclick = event => {
+        addClass(getSettingsButton(), "rotate");
         event.preventDefault();
+        // Sending request for the CSS and the JS files at the same time so it will
+        // hopefully be loaded when the JS will generate the settings content.
+        loadCss("settings");
         loadScript(window.settingsJS);
     };
 
index 491b3950ae6eb8fbccadeb142b0e6175492296f1..70fcef522129eabc7b03d71ab5939759b3bab302 100644 (file)
@@ -4,6 +4,8 @@
 /* eslint prefer-arrow-callback: "error" */
 /* global addClass, hasClass, removeClass, onEachLazy */
 
+"use strict";
+
 (function () {
     // Number of lines shown when code viewer is not expanded
     const MAX_LINES = 10;
index 60ad431ba7a993b44107b5e7bf1c7388cbf43fb3..02370a1243a0cc4c6b04a977a29fe51ccd56f7bd 100644 (file)
@@ -5,7 +5,9 @@
 /* global addClass, getNakedUrl, getSettingValue, hasOwnPropertyRustdoc, initSearch, onEach */
 /* global onEachLazy, removeClass, searchState, browserSupportsHistoryApi */
 
-(function () {
+"use strict";
+
+(function() {
 // This mapping table should match the discriminants of
 // `rustdoc::formats::item_type::ItemType` type in Rust.
 const itemTypes = [
@@ -43,26 +45,33 @@ const TY_KEYWORD = itemTypes.indexOf("keyword");
 
 // In the search display, allows to switch between tabs.
 function printTab(nb) {
-    if (nb === 0 || nb === 1 || nb === 2) {
-        searchState.currentTab = nb;
-    }
-    let nb_copy = nb;
+    let iter = 0;
+    let foundCurrentTab = false;
+    let foundCurrentResultSet = false;
     onEachLazy(document.getElementById("titles").childNodes, elem => {
-        if (nb_copy === 0) {
+        if (nb === iter) {
             addClass(elem, "selected");
+            foundCurrentTab = true;
         } else {
             removeClass(elem, "selected");
         }
-        nb_copy -= 1;
+        iter += 1;
     });
+    iter = 0;
     onEachLazy(document.getElementById("results").childNodes, elem => {
-        if (nb === 0) {
+        if (nb === iter) {
             addClass(elem, "active");
+            foundCurrentResultSet = true;
         } else {
             removeClass(elem, "active");
         }
-        nb -= 1;
+        iter += 1;
     });
+    if (foundCurrentTab && foundCurrentResultSet) {
+        searchState.currentTab = nb;
+    } else if (nb != 0) {
+        printTab(0);
+    }
 }
 
 /**
@@ -1407,18 +1416,12 @@ window.initSearch = rawSearchIndex => {
                     for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
                         row = searchIndex[i];
                         in_returned = checkReturned(row, elem, parsedQuery.typeFilter);
-                        addIntoResults(results_returned, row.id, i, -1, in_returned);
+                        addIntoResults(results_others, row.id, i, -1, in_returned);
                     }
                 }
             } else if (parsedQuery.foundElems > 0) {
-                let container = results_others;
-                // In the special case where only a "returned" information is available, we want to
-                // put the information into the "results_returned" dict.
-                if (parsedQuery.returned.length !== 0 && parsedQuery.elems.length === 0) {
-                    container = results_returned;
-                }
                 for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
-                    handleArgs(searchIndex[i], i, container);
+                    handleArgs(searchIndex[i], i, results_others);
                 }
             }
         }
@@ -1723,12 +1726,26 @@ window.initSearch = rawSearchIndex => {
             `${typeFilter}</h1> in ${crates} </div>`;
         if (results.query.error !== null) {
             output += `<h3>Query parser error: "${results.query.error}".</h3>`;
+            output += '<div id="titles">' +
+                makeTabHeader(0, "In Names", ret_others[1]) +
+                "</div>";
+            currentTab = 0;
+        } else if (results.query.foundElems <= 1 && results.query.returned.length === 0) {
+            output += `<div id="titles">` +
+                makeTabHeader(0, "In Names", ret_others[1]) +
+                makeTabHeader(1, "In Parameters", ret_in_args[1]) +
+                makeTabHeader(2, "In Return Types", ret_returned[1]) +
+                "</div>";
+        } else {
+            const signatureTabTitle =
+                results.query.elems.length === 0 ? "In Function Return Types" :
+                results.query.returned.length === 0 ? "In Function Parameters" :
+                "In Function Signatures";
+            output += '<div id="titles">' +
+                makeTabHeader(0, signatureTabTitle, ret_others[1]) +
+                "</div>";
+            currentTab = 0;
         }
-        output += `<div id="titles">` +
-            makeTabHeader(0, "In Names", ret_others[1]) +
-            makeTabHeader(1, "In Parameters", ret_in_args[1]) +
-            makeTabHeader(2, "In Return Types", ret_returned[1]) +
-            "</div>";
 
         const resultsElem = document.createElement("div");
         resultsElem.id = "results";
@@ -1743,12 +1760,16 @@ window.initSearch = rawSearchIndex => {
         }
         search.appendChild(resultsElem);
         // Reset focused elements.
-        searchState.focusedByTab = [null, null, null];
         searchState.showResults(search);
         const elems = document.getElementById("titles").childNodes;
-        elems[0].onclick = () => { printTab(0); };
-        elems[1].onclick = () => { printTab(1); };
-        elems[2].onclick = () => { printTab(2); };
+        searchState.focusedByTab = [];
+        let i = 0;
+        for (const elem of elems) {
+            const j = i;
+            elem.onclick = () => { printTab(j); };
+            searchState.focusedByTab.push(null);
+            i += 1;
+        }
         printTab(currentTab);
     }
 
index 43b24245ab2e8b362b257a8af67d70633c758fa9..e447d09ab6b97711822b9bb7c2b2d06b3983d4db 100644 (file)
@@ -3,10 +3,12 @@
 /* eslint prefer-const: "error" */
 /* eslint prefer-arrow-callback: "error" */
 // Local js definitions:
-/* global getSettingValue, getVirtualKey, updateLocalStorage, updateSystemTheme, loadCss */
+/* global getSettingValue, getVirtualKey, updateLocalStorage, updateSystemTheme */
 /* global addClass, removeClass, onEach, onEachLazy, NOT_DISPLAYED_ID */
 /* global MAIN_ID, getVar, getSettingsButton, switchDisplayedElement, getNotDisplayedElem */
 
+"use strict";
+
 (function () {
     const isSettingsPage = window.location.pathname.endsWith("/settings.html");
 
             },
         ];
 
-        // First, we add the settings.css file.
-        loadCss("settings");
-
         // Then we build the DOM.
         const el = document.createElement("section");
         el.id = "settings";
         if (!isSettingsPage) {
             switchDisplayedElement(settingsMenu);
         }
+        removeClass(getSettingsButton(), "rotate");
     }, 0);
 })();
index 290bf40a8f5775eba89dff2fe0089f9915e6593c..f788d41d2ded4a1290f89302e465d895536d76e1 100644 (file)
@@ -9,7 +9,10 @@
 // Local js definitions:
 /* global addClass, getCurrentValue, hasClass, onEachLazy, removeClass, browserSupportsHistoryApi */
 /* global updateLocalStorage */
-(function () {
+
+"use strict";
+
+(function() {
 
 function getCurrentFilePath() {
     const parts = window.location.pathname.split("/");
index 913fc278eb38771215553708b0d471c84e314f80..052731e99aed28c452e8b18e8750543e64c1bcc3 100644 (file)
@@ -3,6 +3,8 @@
 /* eslint prefer-const: "error" */
 /* eslint prefer-arrow-callback: "error" */
 
+"use strict";
+
 const darkThemes = ["dark", "ayu"];
 window.currentTheme = document.getElementById("themeStyle");
 window.mainTheme = document.getElementById("mainThemeStyle");
index 641b0f1b3648fa6c8938d11ce52168ea352b9b58..dfc70237821a1676211c3a33241adb377884f92f 100644 (file)
@@ -95,7 +95,7 @@
 ///
 /// Example:
 ///
-/// ```
+/// ```ignore(cannot-test-this-because-non-exported-macro)
 /// let letters = map!{"a" => "b", "c" => "d"};
 /// ```
 ///
index b76ce7ac387facd52e042e716c52f5589505e680..4cf73b40faf0b070f4ac9b79e8bd051dc2990341 100644 (file)
@@ -2,6 +2,7 @@
 // only-linux
 // assembly-output: emit-asm
 // compile-flags: -C llvm-args=--x86-asm-syntax=intel
+// compile-flags: -C symbol-mangling-version=v0
 
 #![feature(asm_const, asm_sym)]
 #![crate_type = "rlib"]
@@ -24,3 +25,7 @@ fn my_func() {}
 global_asm!("call {}", sym my_func);
 // CHECK: lea rax, [rip + MY_STATIC]
 global_asm!("lea rax, [rip + {}]", sym MY_STATIC);
+// CHECK: call _RNvCsiubXh4Yz005_10global_asm6foobar
+global_asm!("call {}", sym foobar);
+// CHECK: _RNvCsiubXh4Yz005_10global_asm6foobar:
+fn foobar() { loop {} }
diff --git a/src/test/debuginfo/msvc-embedded-natvis.natvis b/src/test/debuginfo/msvc-embedded-natvis.natvis
new file mode 100644 (file)
index 0000000..201d014
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
+  <Type Name="msvc_embedded_natvis::Point">
+    <DisplayString>({x}, {y})</DisplayString>
+    <Expand>
+      <Item Name="[x]">x</Item>
+      <Item Name="[y]">y</Item>
+    </Expand>
+  </Type>
+
+  <Type Name="msvc_embedded_natvis::Line">
+    <DisplayString>({a}, {b})</DisplayString>
+    <Expand>
+      <Item Name="[a]">a</Item>
+      <Item Name="[b]">b</Item>
+    </Expand>
+  </Type>
+</AutoVisualizer>
diff --git a/src/test/debuginfo/msvc-embedded-natvis.rs b/src/test/debuginfo/msvc-embedded-natvis.rs
new file mode 100644 (file)
index 0000000..f714fb2
--- /dev/null
@@ -0,0 +1,64 @@
+// only-cdb
+// compile-flags:-g
+
+// === CDB TESTS ==================================================================================
+
+// cdb-command: g
+
+// cdb-command: .nvlist
+// cdb-check:    [...].exe (embedded NatVis "[...]msvc_embedded_natvis-0.natvis")
+
+// cdb-command: dx point_a
+// cdb-check:point_a          : (0, 0) [Type: msvc_embedded_natvis::Point]
+// cdb-check:    [<Raw View>]     [Type: msvc_embedded_natvis::Point]
+// cdb-check:    [x]              : 0 [Type: int]
+// cdb-check:    [y]              : 0 [Type: int]
+
+// cdb-command: dx point_b
+// cdb-check:point_b          : (5, 8) [Type: msvc_embedded_natvis::Point]
+// cdb-check:    [<Raw View>]     [Type: msvc_embedded_natvis::Point]
+// cdb-check:    [x]              : 5 [Type: int]
+// cdb-check:    [y]              : 8 [Type: int]
+
+// cdb-command: dx line
+// cdb-check:line             : ((0, 0), (5, 8)) [Type: msvc_embedded_natvis::Line]
+// cdb-check:    [<Raw View>]     [Type: msvc_embedded_natvis::Line]
+// cdb-check:    [a]              : (0, 0) [Type: msvc_embedded_natvis::Point]
+// cdb-check:    [b]              : (5, 8) [Type: msvc_embedded_natvis::Point]
+
+#![feature(debugger_visualizer)]
+#![debugger_visualizer(natvis_file = "msvc-embedded-natvis.natvis")]
+
+pub struct Point {
+    x: i32,
+    y: i32,
+}
+
+impl Point {
+    pub fn new(x: i32, y: i32) -> Point {
+        Point { x: x, y: y }
+    }
+}
+
+pub struct Line {
+    a: Point,
+    b: Point,
+}
+
+impl Line {
+    pub fn new(a: Point, b: Point) -> Line {
+        Line { a: a, b: b }
+    }
+}
+
+fn main() {
+    let point_a = Point::new(0, 0);
+    let point_b = Point::new(5, 8);
+    let line = Line::new(point_a, point_b);
+
+    zzz(); // #break
+}
+
+fn zzz() {
+    ()
+}
diff --git a/src/test/rustdoc-gui/search-tab-change-title-fn-sig.goml b/src/test/rustdoc-gui/search-tab-change-title-fn-sig.goml
new file mode 100644 (file)
index 0000000..763927f
--- /dev/null
@@ -0,0 +1,64 @@
+// Checks that the search tab results work correctly with function signature syntax
+// First, try a search-by-name
+goto: file://|DOC_PATH|/test_docs/index.html
+write: (".search-input", "Foo")
+// Waiting for the search results to appear...
+wait-for: "#titles"
+assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#titles > button:nth-of-type(1)", "In Names", STARTS_WITH)
+assert: "input.search-input:focus"
+// Use left-right keys
+press-key: "ArrowDown"
+assert: "#results > .search-results.active > a:nth-of-type(1):focus"
+press-key: "ArrowRight"
+wait-for-attribute: ("#titles > button:nth-of-type(2)", {"class": "selected"})
+press-key: "ArrowRight"
+wait-for-attribute: ("#titles > button:nth-of-type(3)", {"class": "selected"})
+press-key: "ArrowRight"
+wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+press-key: "ArrowLeft"
+wait-for-attribute: ("#titles > button:nth-of-type(3)", {"class": "selected"})
+
+// Now try search-by-return
+goto: file://|DOC_PATH|/test_docs/index.html
+write: (".search-input", "-> String")
+// Waiting for the search results to appear...
+wait-for: "#titles"
+assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#titles > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH)
+assert: "input.search-input:focus"
+// Use left-right keys
+press-key: "ArrowDown"
+assert: "#results > .search-results.active > a:nth-of-type(1):focus"
+press-key: "ArrowRight"
+wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+press-key: "ArrowRight"
+wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+press-key: "ArrowRight"
+wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+press-key: "ArrowLeft"
+wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+
+// Try with a search-by-return with no results
+goto: file://|DOC_PATH|/test_docs/index.html
+write: (".search-input", "-> Something")
+// Waiting for the search results to appear...
+wait-for: "#titles"
+assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#titles > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH)
+
+// Try with a search-by-parameter
+goto: file://|DOC_PATH|/test_docs/index.html
+write: (".search-input", "usize pattern")
+// Waiting for the search results to appear...
+wait-for: "#titles"
+assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#titles > button:nth-of-type(1)", "In Function Parameters", STARTS_WITH)
+
+// Try with a search-by-parameter-and-return
+goto: file://|DOC_PATH|/test_docs/index.html
+write: (".search-input", "pattern -> str")
+// Waiting for the search results to appear...
+wait-for: "#titles"
+assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#titles > button:nth-of-type(1)", "In Function Signatures", STARTS_WITH)
diff --git a/src/test/rustdoc-gui/search-tab-selection-if-current-is-empty.goml b/src/test/rustdoc-gui/search-tab-selection-if-current-is-empty.goml
deleted file mode 100644 (file)
index 52b3cea..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-// Checks that the first non-empty search result tab is selected if the default/currently selected
-// one is empty.
-goto: file://|DOC_PATH|/test_docs/index.html
-write: (".search-input", "Foo")
-// Waiting for the search results to appear...
-wait-for: "#titles"
-assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
-
-// To go back to the original "state"
-goto: file://|DOC_PATH|/test_docs/index.html
-write: (".search-input", "-> String")
-// Waiting for the search results to appear...
-wait-for: "#titles"
-// With this search, only the last tab shouldn't be empty so it should be selected.
-assert-attribute: ("#titles > button:nth-of-type(3)", {"class": "selected"})
-
-// To go back to the original "state"
-goto: file://|DOC_PATH|/test_docs/index.html
-write: (".search-input", "-> Something")
-// Waiting for the search results to appear...
-wait-for: "#titles"
-// With this search, all the tabs are empty so the first one should remain selected.
-assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
index 2ba0b6b28aaaa55eb01b1e9b9e511a1451a4c0b4..eb934e7b72b084a6266998d1617113b2a1d586a8 100644 (file)
@@ -7,23 +7,12 @@ LL |             self.foo(self.bar());
    |             |    |   mutable borrow occurs here
    |             |    immutable borrow later used by call
    |             immutable borrow occurs here
-   |
-help: try adding a local storing this argument...
-  --> $DIR/suggest-local-var-imm-and-mut.rs:12:22
-   |
-LL |             self.foo(self.bar());
-   |                      ^^^^^^^^^^
-help: ...and then using that local as the argument to this call
-  --> $DIR/suggest-local-var-imm-and-mut.rs:12:13
-   |
-LL |             self.foo(self.bar());
-   |             ^^^^^^^^^^^^^^^^^^^^
 
 error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
-  --> $DIR/suggest-local-var-imm-and-mut.rs:24:39
+  --> $DIR/suggest-local-var-imm-and-mut.rs:24:29
    |
 LL |             Self::foo(self, Self::bar(self));
-   |             --------- ----            ^^^^ mutable borrow occurs here
+   |             --------- ----  ^^^^^^^^^^^^^^^ mutable borrow occurs here
    |             |         |
    |             |         immutable borrow occurs here
    |             immutable borrow later used by call
index 85c7159952ffa59ea353345627765e3b0d1e497d..a89bb941532b62cd54a8477cc5382336a60523d3 100644 (file)
@@ -13,23 +13,6 @@ LL | |
 LL | |         0
 LL | |     });
    | |______- immutable borrow occurs here
-   |
-help: try adding a local storing this argument...
-  --> $DIR/two-phase-cannot-nest-mut-self-calls.rs:16:9
-   |
-LL |         vec.push(2);
-   |         ^^^^^^^^^^^
-help: ...and then using that local as the argument to this call
-  --> $DIR/two-phase-cannot-nest-mut-self-calls.rs:14:5
-   |
-LL | /     vec.get({
-LL | |
-LL | |         vec.push(2);
-LL | |
-LL | |
-LL | |         0
-LL | |     });
-   | |______^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.base.stderr b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.base.stderr
new file mode 100644 (file)
index 0000000..cbbbde6
--- /dev/null
@@ -0,0 +1,25 @@
+error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
+  --> $DIR/two-phase-reservation-sharing-interference-2.rs:19:5
+   |
+LL |     let shared = &v;
+   |                  -- immutable borrow occurs here
+LL | 
+LL |     v.extend(shared);
+   |     ^^------^^^^^^^^
+   |     | |
+   |     | immutable borrow later used by call
+   |     mutable borrow occurs here
+
+error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
+  --> $DIR/two-phase-reservation-sharing-interference-2.rs:27:5
+   |
+LL |     v.extend(&v);
+   |     ^^------^--^
+   |     | |      |
+   |     | |      immutable borrow occurs here
+   |     | immutable borrow later used by call
+   |     mutable borrow occurs here
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0502`.
index 6cff53399ca7f12ebb08fa0dba026bc2b3e54fac..69c3d7915e43f0f09521101c897d98a57d62e8bb 100644 (file)
@@ -20,22 +20,6 @@ LL |     v.extend(&v);
    |     | immutable borrow later used by call
    |     mutable borrow occurs here
 
-warning: cannot borrow `v` as mutable because it is also borrowed as immutable
-  --> $DIR/two-phase-reservation-sharing-interference-2.rs:40:5
-   |
-LL |     let shared = &v;
-   |                  -- immutable borrow occurs here
-LL | 
-LL |     v.push(shared.len());
-   |     ^^^^^^^------------^
-   |     |      |
-   |     |      immutable borrow later used here
-   |     mutable borrow occurs here
-   |
-   = note: `#[warn(mutable_borrow_reservation_conflict)]` on by default
-   = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future
-   = note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159>
-
-error: aborting due to 2 previous errors; 1 warning emitted
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0502`.
index 6cff53399ca7f12ebb08fa0dba026bc2b3e54fac..69c3d7915e43f0f09521101c897d98a57d62e8bb 100644 (file)
@@ -20,22 +20,6 @@ LL |     v.extend(&v);
    |     | immutable borrow later used by call
    |     mutable borrow occurs here
 
-warning: cannot borrow `v` as mutable because it is also borrowed as immutable
-  --> $DIR/two-phase-reservation-sharing-interference-2.rs:40:5
-   |
-LL |     let shared = &v;
-   |                  -- immutable borrow occurs here
-LL | 
-LL |     v.push(shared.len());
-   |     ^^^^^^^------------^
-   |     |      |
-   |     |      immutable borrow later used here
-   |     mutable borrow occurs here
-   |
-   = note: `#[warn(mutable_borrow_reservation_conflict)]` on by default
-   = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future
-   = note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159>
-
-error: aborting due to 2 previous errors; 1 warning emitted
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0502`.
diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll.stderr b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll.stderr
new file mode 100644 (file)
index 0000000..cbbbde6
--- /dev/null
@@ -0,0 +1,25 @@
+error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
+  --> $DIR/two-phase-reservation-sharing-interference-2.rs:19:5
+   |
+LL |     let shared = &v;
+   |                  -- immutable borrow occurs here
+LL | 
+LL |     v.extend(shared);
+   |     ^^------^^^^^^^^
+   |     | |
+   |     | immutable borrow later used by call
+   |     mutable borrow occurs here
+
+error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
+  --> $DIR/two-phase-reservation-sharing-interference-2.rs:27:5
+   |
+LL |     v.extend(&v);
+   |     ^^------^--^
+   |     | |      |
+   |     | |      immutable borrow occurs here
+   |     | immutable borrow later used by call
+   |     mutable borrow occurs here
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0502`.
index 0ae6fe78c6ae1c928f82621e5aa77b12108b7360..69c3d7915e43f0f09521101c897d98a57d62e8bb 100644 (file)
@@ -5,9 +5,9 @@ LL |     let shared = &v;
    |                  -- immutable borrow occurs here
 LL | 
 LL |     v.extend(shared);
-   |     ^^^^^^^^^------^
-   |     |        |
-   |     |        immutable borrow later used here
+   |     ^^------^^^^^^^^
+   |     | |
+   |     | immutable borrow later used by call
    |     mutable borrow occurs here
 
 error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
@@ -20,18 +20,6 @@ LL |     v.extend(&v);
    |     | immutable borrow later used by call
    |     mutable borrow occurs here
 
-error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
-  --> $DIR/two-phase-reservation-sharing-interference-2.rs:40:5
-   |
-LL |     let shared = &v;
-   |                  -- immutable borrow occurs here
-LL | 
-LL |     v.push(shared.len());
-   |     ^^^^^^^------------^
-   |     |      |
-   |     |      immutable borrow later used here
-   |     mutable borrow occurs here
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0502`.
index 0ae6fe78c6ae1c928f82621e5aa77b12108b7360..69c3d7915e43f0f09521101c897d98a57d62e8bb 100644 (file)
@@ -5,9 +5,9 @@ LL |     let shared = &v;
    |                  -- immutable borrow occurs here
 LL | 
 LL |     v.extend(shared);
-   |     ^^^^^^^^^------^
-   |     |        |
-   |     |        immutable borrow later used here
+   |     ^^------^^^^^^^^
+   |     | |
+   |     | immutable borrow later used by call
    |     mutable borrow occurs here
 
 error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
@@ -20,18 +20,6 @@ LL |     v.extend(&v);
    |     | immutable borrow later used by call
    |     mutable borrow occurs here
 
-error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
-  --> $DIR/two-phase-reservation-sharing-interference-2.rs:40:5
-   |
-LL |     let shared = &v;
-   |                  -- immutable borrow occurs here
-LL | 
-LL |     v.push(shared.len());
-   |     ^^^^^^^------------^
-   |     |      |
-   |     |      immutable borrow later used here
-   |     mutable borrow occurs here
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0502`.
index 14f687c23780c080eb1685c9ff47f435b210840d..3e125869ef1e9272c27fe6336210b1117651d2d2 100644 (file)
@@ -1,50 +1,39 @@
-// Test for #56254, we previously allowed the last example on the 2018
-// edition. Make sure that we now emit a warning in that case and an error for
-// everyone else.
+// Test for #56254. The last example originally failed with the ast checker, was
+// accidentally allowed under migrate/nll, then linted against in migrate mode
+// but disallowed under NLL. Now, we accept it everywhere.
 
 //ignore-compare-mode-nll
 //ignore-compare-mode-polonius
 
-//revisions: migrate2015 migrate2018 nll2015 nll2018
+//revisions: base nll
 
 //[migrate2018] edition:2018
 //[nll2018] edition:2018
 
-#![cfg_attr(any(nll2015, nll2018), feature(nll))]
+#![cfg_attr(nll, feature(nll))]
 
 fn double_conflicts() {
     let mut v = vec![0, 1, 2];
     let shared = &v;
 
     v.extend(shared);
-    //[migrate2015]~^ ERROR cannot borrow `v` as mutable
-    //[nll2015]~^^ ERROR cannot borrow `v` as mutable
-    //[migrate2018]~^^^ ERROR cannot borrow `v` as mutable
-    //[nll2018]~^^^^ ERROR cannot borrow `v` as mutable
+    //[base]~^ ERROR cannot borrow `v` as mutable
+    //[nll]~^^ ERROR cannot borrow `v` as mutable
 }
 
 fn activation_conflict() {
     let mut v = vec![0, 1, 2];
 
     v.extend(&v);
-    //[migrate2015]~^ ERROR cannot borrow `v` as mutable
-    //[nll2015]~^^ ERROR cannot borrow `v` as mutable
-    //[migrate2018]~^^^ ERROR cannot borrow `v` as mutable
-    //[nll2018]~^^^^ ERROR cannot borrow `v` as mutable
+    //[base]~^ ERROR cannot borrow `v` as mutable
+    //[nll]~^^ ERROR cannot borrow `v` as mutable
 }
 
-fn reservation_conflict() {
+fn reservation_allowed() {
     let mut v = vec![0, 1, 2];
     let shared = &v;
 
     v.push(shared.len());
-    //[nll2015]~^ ERROR cannot borrow `v` as mutable
-    //[nll2018]~^^ ERROR cannot borrow `v` as mutable
-    //[migrate2015]~^^^ WARNING cannot borrow `v` as mutable
-    //[migrate2015]~| WARNING may become a hard error in the future
-
-    //[migrate2018]~^^^^^^ WARNING cannot borrow `v` as mutable
-    //[migrate2018]~| WARNING may become a hard error in the future
 }
 
 fn main() {}
diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.nll.stderr b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.nll.stderr
deleted file mode 100644 (file)
index 52e8de3..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
-  --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:13:9
-   |
-LL |         let shared = &v;
-   |                      -- immutable borrow occurs here
-LL | 
-LL |         v.push(shared.len());
-   |         ^^^^^^^------------^
-   |         |      |
-   |         |      immutable borrow later used here
-   |         mutable borrow occurs here
-
-error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
-  --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:24:9
-   |
-LL |         let shared = &v;
-   |                      -- immutable borrow occurs here
-LL | 
-LL |         v.push(shared.len());
-   |         ^^^^^^^------------^
-   |         |      |
-   |         |      immutable borrow later used here
-   |         mutable borrow occurs here
-
-error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
-  --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:37:9
-   |
-LL |         let shared = &v;
-   |                      -- immutable borrow occurs here
-LL | 
-LL |         v.push(shared.len());
-   |         ^^^^^^^------------^
-   |         |      |
-   |         |      immutable borrow later used here
-   |         mutable borrow occurs here
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0502`.
diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.rs b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.rs
deleted file mode 100644 (file)
index 0e1d77a..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-// Check that the future-compat-lint for the reservation conflict is
-// handled like any other lint.
-
-// edition:2018
-
-mod future_compat_allow {
-    #![allow(mutable_borrow_reservation_conflict)]
-
-    fn reservation_conflict() {
-        let mut v = vec![0, 1, 2];
-        let shared = &v;
-
-        v.push(shared.len());
-    }
-}
-
-mod future_compat_warn {
-    #![warn(mutable_borrow_reservation_conflict)]
-
-    fn reservation_conflict() {
-        let mut v = vec![0, 1, 2];
-        let shared = &v;
-
-        v.push(shared.len());
-        //~^ WARNING cannot borrow `v` as mutable
-        //~| WARNING may become a hard error in the future
-    }
-}
-
-mod future_compat_deny {
-    #![deny(mutable_borrow_reservation_conflict)]
-
-    fn reservation_conflict() {
-        let mut v = vec![0, 1, 2];
-        let shared = &v;
-
-        v.push(shared.len());
-        //~^ ERROR cannot borrow `v` as mutable
-        //~| WARNING may become a hard error in the future
-    }
-}
-
-fn main() {}
diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.stderr b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.stderr
deleted file mode 100644 (file)
index aab21c9..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-warning: cannot borrow `v` as mutable because it is also borrowed as immutable
-  --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:24:9
-   |
-LL |         let shared = &v;
-   |                      -- immutable borrow occurs here
-LL | 
-LL |         v.push(shared.len());
-   |         ^^^^^^^------------^
-   |         |      |
-   |         |      immutable borrow later used here
-   |         mutable borrow occurs here
-   |
-note: the lint level is defined here
-  --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:18:13
-   |
-LL |     #![warn(mutable_borrow_reservation_conflict)]
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future
-   = note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159>
-
-error: cannot borrow `v` as mutable because it is also borrowed as immutable
-  --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:37:9
-   |
-LL |         let shared = &v;
-   |                      -- immutable borrow occurs here
-LL | 
-LL |         v.push(shared.len());
-   |         ^^^^^^^------------^
-   |         |      |
-   |         |      immutable borrow later used here
-   |         mutable borrow occurs here
-   |
-note: the lint level is defined here
-  --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:31:13
-   |
-LL |     #![deny(mutable_borrow_reservation_conflict)]
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future
-   = note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159>
-
-error: aborting due to previous error; 1 warning emitted
-
index a3c7ef761898938a7df9d942fe0fc41bd27c3958..94cc89754db5fc615509c571e3794e7aab64672c 100644 (file)
@@ -1,10 +1,10 @@
 error[E0502]: cannot borrow `*a` as mutable because it is also borrowed as immutable
-  --> $DIR/E0502.rs:4:9
+  --> $DIR/E0502.rs:4:5
    |
 LL |     let ref y = a;
    |         ----- immutable borrow occurs here
 LL |     bar(a);
-   |         ^ mutable borrow occurs here
+   |     ^^^^^^ mutable borrow occurs here
 LL |     y.use_ref();
    |     ----------- immutable borrow later used here
 
diff --git a/src/test/ui/feature-gates/feature-gate-debugger-visualizer.rs b/src/test/ui/feature-gates/feature-gate-debugger-visualizer.rs
new file mode 100644 (file)
index 0000000..3f9eb27
--- /dev/null
@@ -0,0 +1,3 @@
+#![debugger_visualizer(natvis_file = "../foo.natvis")] //~ ERROR the `#[debugger_visualizer]` attribute is an experimental feature
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-debugger-visualizer.stderr b/src/test/ui/feature-gates/feature-gate-debugger-visualizer.stderr
new file mode 100644 (file)
index 0000000..721b2b1
--- /dev/null
@@ -0,0 +1,12 @@
+error[E0658]: the `#[debugger_visualizer]` attribute is an experimental feature
+  --> $DIR/feature-gate-debugger-visualizer.rs:1:1
+   |
+LL | #![debugger_visualizer(natvis_file = "../foo.natvis")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #95939 <https://github.com/rust-lang/rust/issues/95939> for more information
+   = help: add `#![feature(debugger_visualizer)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/incoherent-inherent-impls/auxiliary/extern-crate.rs b/src/test/ui/incoherent-inherent-impls/auxiliary/extern-crate.rs
new file mode 100644 (file)
index 0000000..22f0d91
--- /dev/null
@@ -0,0 +1,9 @@
+#![feature(rustc_attrs)]
+
+#[rustc_has_incoherent_inherent_impls]
+pub struct StructWithAttr;
+pub struct StructNoAttr;
+
+#[rustc_has_incoherent_inherent_impls]
+pub enum EnumWithAttr {}
+pub enum EnumNoAttr {}
diff --git a/src/test/ui/incoherent-inherent-impls/needs-has-incoherent-impls.rs b/src/test/ui/incoherent-inherent-impls/needs-has-incoherent-impls.rs
new file mode 100644 (file)
index 0000000..0f7282b
--- /dev/null
@@ -0,0 +1,40 @@
+// aux-build:extern-crate.rs
+#![feature(rustc_attrs)]
+extern crate extern_crate;
+
+impl extern_crate::StructWithAttr {
+    //~^ ERROR cannot define inherent `impl` for a type outside of the crate
+    fn foo() {}
+}
+impl extern_crate::StructWithAttr {
+    #[rustc_allow_incoherent_impl]
+    fn bar() {}
+}
+impl extern_crate::StructNoAttr {
+    //~^ ERROR cannot define inherent `impl` for a type outside of the crate
+    fn foo() {}
+}
+impl extern_crate::StructNoAttr {
+    //~^ ERROR cannot define inherent `impl` for a type outside of the crate
+    #[rustc_allow_incoherent_impl]
+    fn bar() {}
+}
+impl extern_crate::EnumWithAttr {
+    //~^ ERROR cannot define inherent `impl` for a type outside of the crate
+    fn foo() {}
+}
+impl extern_crate::EnumWithAttr {
+    #[rustc_allow_incoherent_impl]
+    fn bar() {}
+}
+impl extern_crate::EnumNoAttr {
+    //~^ ERROR cannot define inherent `impl` for a type outside of the crate
+    fn foo() {}
+}
+impl extern_crate::EnumNoAttr {
+    //~^ ERROR cannot define inherent `impl` for a type outside of the crate
+    #[rustc_allow_incoherent_impl]
+    fn bar() {}
+}
+
+fn main() {}
diff --git a/src/test/ui/incoherent-inherent-impls/needs-has-incoherent-impls.stderr b/src/test/ui/incoherent-inherent-impls/needs-has-incoherent-impls.stderr
new file mode 100644 (file)
index 0000000..8f70825
--- /dev/null
@@ -0,0 +1,115 @@
+error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+  --> $DIR/needs-has-incoherent-impls.rs:5:1
+   |
+LL | / impl extern_crate::StructWithAttr {
+LL | |
+LL | |     fn foo() {}
+LL | | }
+   | |_^
+   |
+   = help: consider moving this inherent impl into the crate defining the type if possible
+help: alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items
+  --> $DIR/needs-has-incoherent-impls.rs:7:5
+   |
+LL |     fn foo() {}
+   |     ^^^^^^^^^^^
+
+error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+  --> $DIR/needs-has-incoherent-impls.rs:13:1
+   |
+LL | / impl extern_crate::StructNoAttr {
+LL | |
+LL | |     fn foo() {}
+LL | | }
+   | |_^
+   |
+   = help: consider moving this inherent impl into the crate defining the type if possible
+help: alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items
+  --> $DIR/needs-has-incoherent-impls.rs:13:1
+   |
+LL | / impl extern_crate::StructNoAttr {
+LL | |
+LL | |     fn foo() {}
+LL | | }
+   | |_^
+
+error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+  --> $DIR/needs-has-incoherent-impls.rs:17:1
+   |
+LL | / impl extern_crate::StructNoAttr {
+LL | |
+LL | |     #[rustc_allow_incoherent_impl]
+LL | |     fn bar() {}
+LL | | }
+   | |_^
+   |
+   = help: consider moving this inherent impl into the crate defining the type if possible
+help: alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items
+  --> $DIR/needs-has-incoherent-impls.rs:17:1
+   |
+LL | / impl extern_crate::StructNoAttr {
+LL | |
+LL | |     #[rustc_allow_incoherent_impl]
+LL | |     fn bar() {}
+LL | | }
+   | |_^
+
+error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+  --> $DIR/needs-has-incoherent-impls.rs:22:1
+   |
+LL | / impl extern_crate::EnumWithAttr {
+LL | |
+LL | |     fn foo() {}
+LL | | }
+   | |_^
+   |
+   = help: consider moving this inherent impl into the crate defining the type if possible
+help: alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items
+  --> $DIR/needs-has-incoherent-impls.rs:24:5
+   |
+LL |     fn foo() {}
+   |     ^^^^^^^^^^^
+
+error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+  --> $DIR/needs-has-incoherent-impls.rs:30:1
+   |
+LL | / impl extern_crate::EnumNoAttr {
+LL | |
+LL | |     fn foo() {}
+LL | | }
+   | |_^
+   |
+   = help: consider moving this inherent impl into the crate defining the type if possible
+help: alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items
+  --> $DIR/needs-has-incoherent-impls.rs:30:1
+   |
+LL | / impl extern_crate::EnumNoAttr {
+LL | |
+LL | |     fn foo() {}
+LL | | }
+   | |_^
+
+error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+  --> $DIR/needs-has-incoherent-impls.rs:34:1
+   |
+LL | / impl extern_crate::EnumNoAttr {
+LL | |
+LL | |     #[rustc_allow_incoherent_impl]
+LL | |     fn bar() {}
+LL | | }
+   | |_^
+   |
+   = help: consider moving this inherent impl into the crate defining the type if possible
+help: alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items
+  --> $DIR/needs-has-incoherent-impls.rs:34:1
+   |
+LL | / impl extern_crate::EnumNoAttr {
+LL | |
+LL | |     #[rustc_allow_incoherent_impl]
+LL | |     fn bar() {}
+LL | | }
+   | |_^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0390`.
diff --git a/src/test/ui/incoherent-inherent-impls/no-attr-empty-impl.rs b/src/test/ui/incoherent-inherent-impls/no-attr-empty-impl.rs
new file mode 100644 (file)
index 0000000..62c249e
--- /dev/null
@@ -0,0 +1,18 @@
+// aux-build:extern-crate.rs
+extern crate extern_crate;
+
+impl extern_crate::StructWithAttr {}
+//~^ ERROR cannot define inherent `impl` for a type outside of the crate
+
+impl extern_crate::StructNoAttr {}
+//~^ ERROR cannot define inherent `impl` for a type outside of the crate
+
+impl extern_crate::EnumWithAttr {}
+//~^ ERROR cannot define inherent `impl` for a type outside of the crate
+
+impl extern_crate::EnumNoAttr {}
+//~^ ERROR cannot define inherent `impl` for a type outside of the crate
+
+impl f32 {} //~ ERROR cannot define inherent `impl` for primitive types
+
+fn main() {}
diff --git a/src/test/ui/incoherent-inherent-impls/no-attr-empty-impl.stderr b/src/test/ui/incoherent-inherent-impls/no-attr-empty-impl.stderr
new file mode 100644 (file)
index 0000000..b3f8b51
--- /dev/null
@@ -0,0 +1,44 @@
+error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+  --> $DIR/no-attr-empty-impl.rs:4:1
+   |
+LL | impl extern_crate::StructWithAttr {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl for type defined outside of crate.
+   |
+   = note: define and implement a trait or new type instead
+
+error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+  --> $DIR/no-attr-empty-impl.rs:7:1
+   |
+LL | impl extern_crate::StructNoAttr {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl for type defined outside of crate.
+   |
+   = note: define and implement a trait or new type instead
+
+error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+  --> $DIR/no-attr-empty-impl.rs:10:1
+   |
+LL | impl extern_crate::EnumWithAttr {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl for type defined outside of crate.
+   |
+   = note: define and implement a trait or new type instead
+
+error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+  --> $DIR/no-attr-empty-impl.rs:13:1
+   |
+LL | impl extern_crate::EnumNoAttr {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl for type defined outside of crate.
+   |
+   = note: define and implement a trait or new type instead
+
+error[E0390]: cannot define inherent `impl` for primitive types
+  --> $DIR/no-attr-empty-impl.rs:16:6
+   |
+LL | impl f32 {}
+   |      ^^^
+   |
+   = help: consider using an extension trait instead
+
+error: aborting due to 5 previous errors
+
+Some errors have detailed explanations: E0116, E0390.
+For more information about an error, try `rustc --explain E0116`.
diff --git a/src/test/ui/inline-const/const-expr-generic-err.rs b/src/test/ui/inline-const/const-expr-generic-err.rs
new file mode 100644 (file)
index 0000000..4e8879a
--- /dev/null
@@ -0,0 +1,15 @@
+// build-fail
+#![feature(inline_const)]
+
+fn foo<T>() {
+    const { assert!(std::mem::size_of::<T>() == 0); } //~ ERROR E0080
+}
+
+fn bar<const N: usize>() -> usize {
+    const { N - 1 } //~ ERROR E0080
+}
+
+fn main() {
+    foo::<i32>();
+    bar::<0>();
+}
diff --git a/src/test/ui/inline-const/const-expr-generic-err.stderr b/src/test/ui/inline-const/const-expr-generic-err.stderr
new file mode 100644 (file)
index 0000000..db0d85a
--- /dev/null
@@ -0,0 +1,29 @@
+error[E0080]: evaluation of `foo::<i32>::{constant#0}` failed
+  --> $DIR/const-expr-generic-err.rs:5:13
+   |
+LL |     const { assert!(std::mem::size_of::<T>() == 0); }
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: std::mem::size_of::<T>() == 0', $DIR/const-expr-generic-err.rs:5:13
+   |
+   = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: the above error was encountered while instantiating `fn foo::<i32>`
+  --> $DIR/const-expr-generic-err.rs:13:5
+   |
+LL |     foo::<i32>();
+   |     ^^^^^^^^^^^^
+
+error[E0080]: evaluation of `bar::<0_usize>::{constant#0}` failed
+  --> $DIR/const-expr-generic-err.rs:9:13
+   |
+LL |     const { N - 1 }
+   |             ^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow
+
+note: the above error was encountered while instantiating `fn bar::<0_usize>`
+  --> $DIR/const-expr-generic-err.rs:14:5
+   |
+LL |     bar::<0>();
+   |     ^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/inline-const/const-expr-generic-err2.rs b/src/test/ui/inline-const/const-expr-generic-err2.rs
new file mode 100644 (file)
index 0000000..e097cbe
--- /dev/null
@@ -0,0 +1,10 @@
+#![feature(inline_const)]
+
+fn foo<T>() {
+    let _ = [0u8; const { std::mem::size_of::<T>() }];
+    //~^ ERROR: constant expression depends on a generic parameter
+}
+
+fn main() {
+    foo::<i32>();
+}
diff --git a/src/test/ui/inline-const/const-expr-generic-err2.stderr b/src/test/ui/inline-const/const-expr-generic-err2.stderr
new file mode 100644 (file)
index 0000000..00b716c
--- /dev/null
@@ -0,0 +1,10 @@
+error: constant expression depends on a generic parameter
+  --> $DIR/const-expr-generic-err2.rs:4:19
+   |
+LL |     let _ = [0u8; const { std::mem::size_of::<T>() }];
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this may fail depending on what value the parameter takes
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/inline-const/const-expr-generic.rs b/src/test/ui/inline-const/const-expr-generic.rs
new file mode 100644 (file)
index 0000000..3207bfa
--- /dev/null
@@ -0,0 +1,15 @@
+// check-pass
+#![feature(inline_const)]
+
+fn foo<T>() -> usize {
+    const { std::mem::size_of::<T>() }
+}
+
+fn bar<const N: usize>() -> usize {
+    const { N + 1 }
+}
+
+fn main() {
+    foo::<i32>();
+    bar::<1>();
+}
index be7e1d8d44984c7676aaa1c80a07569d9feb8cdb..e1946467583e908c1ea23d85bdaacd88a9a909ac 100644 (file)
@@ -1,6 +1,5 @@
 #![allow(incomplete_features)]
 #![feature(inline_const_pat)]
-#![feature(generic_const_exprs)]
 
 // rust-lang/rust#82518: ICE with inline-const in match referencing const-generic parameter
 
@@ -16,7 +15,7 @@ const fn f(x: usize) -> usize {
     x + 1
 }
 
-fn bar<const V: usize>() where [(); f(V)]: {
+fn bar<const V: usize>() {
     match 0 {
         const { f(V) } => {},
         //~^ ERROR constant pattern depends on a generic parameter
index 5fe5a7a6dad412b4eb97c6efc8f33939dd7e3b24..ade200d99ba39c46390e7b9009866ec396c2f683 100644 (file)
@@ -1,17 +1,17 @@
 error[E0158]: const parameters cannot be referenced in patterns
-  --> $DIR/const-match-pat-generic.rs:9:9
+  --> $DIR/const-match-pat-generic.rs:8:9
    |
 LL |         const { V } => {},
    |         ^^^^^^^^^^^
 
 error: constant pattern depends on a generic parameter
-  --> $DIR/const-match-pat-generic.rs:21:9
+  --> $DIR/const-match-pat-generic.rs:20:9
    |
 LL |         const { f(V) } => {},
    |         ^^^^^^^^^^^^^^
 
 error: constant pattern depends on a generic parameter
-  --> $DIR/const-match-pat-generic.rs:21:9
+  --> $DIR/const-match-pat-generic.rs:20:9
    |
 LL |         const { f(V) } => {},
    |         ^^^^^^^^^^^^^^
diff --git a/src/test/ui/invalid/invalid-debugger-visualizer-option.rs b/src/test/ui/invalid/invalid-debugger-visualizer-option.rs
new file mode 100644 (file)
index 0000000..5332298
--- /dev/null
@@ -0,0 +1,4 @@
+#![feature(debugger_visualizer)]
+#![debugger_visualizer(random_file = "../foo.random")] //~ ERROR invalid argument
+
+fn main() {}
diff --git a/src/test/ui/invalid/invalid-debugger-visualizer-option.stderr b/src/test/ui/invalid/invalid-debugger-visualizer-option.stderr
new file mode 100644 (file)
index 0000000..24ad936
--- /dev/null
@@ -0,0 +1,10 @@
+error: invalid argument
+  --> $DIR/invalid-debugger-visualizer-option.rs:2:1
+   |
+LL | #![debugger_visualizer(random_file = "../foo.random")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: expected: `natvis_file = "..."`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/invalid/invalid-debugger-visualizer-target.rs b/src/test/ui/invalid/invalid-debugger-visualizer-target.rs
new file mode 100644 (file)
index 0000000..7668d09
--- /dev/null
@@ -0,0 +1,5 @@
+#![feature(debugger_visualizer)]
+
+#[debugger_visualizer(natvis_file = "../foo.natvis")] //~ ERROR attribute should be applied to a module
+
+fn main() {}
diff --git a/src/test/ui/invalid/invalid-debugger-visualizer-target.stderr b/src/test/ui/invalid/invalid-debugger-visualizer-target.stderr
new file mode 100644 (file)
index 0000000..3555bbb
--- /dev/null
@@ -0,0 +1,8 @@
+error: attribute should be applied to a module
+  --> $DIR/invalid-debugger-visualizer-target.rs:3:1
+   |
+LL | #[debugger_visualizer(natvis_file = "../foo.natvis")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
index e5b8747bd80476d9bb395c6ea2e52c676fc3908f..82df50d9dc34f0c4e30438441b2b017ed3aa4fa7 100644 (file)
@@ -5,7 +5,6 @@ fn f2<P>(_: P, _: ()) {}
 fn f3<'a>(x: &'a ((), &'a mut ())) {
     f2(|| x.0, f1(x.1))
 //~^ ERROR cannot borrow `*x.1` as mutable, as it is behind a `&` reference
-//~| ERROR cannot borrow `*x.1` as mutable because it is also borrowed as immutable
 }
 
 fn main() {}
index 901c75981768e63481afacd505435e4f9890c2fd..f654605423379849b5788867d7fa14b2bf3e6aca 100644 (file)
@@ -6,17 +6,6 @@ LL | fn f3<'a>(x: &'a ((), &'a mut ())) {
 LL |     f2(|| x.0, f1(x.1))
    |                   ^^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0502]: cannot borrow `*x.1` as mutable because it is also borrowed as immutable
-  --> $DIR/issue-61623.rs:6:19
-   |
-LL |     f2(|| x.0, f1(x.1))
-   |     -- -- ---     ^^^ mutable borrow occurs here
-   |     |  |  |
-   |     |  |  first borrow occurs due to use of `x` in closure
-   |     |  immutable borrow occurs here
-   |     immutable borrow later used by call
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-Some errors have detailed explanations: E0502, E0596.
-For more information about an error, try `rustc --explain E0502`.
+For more information about this error, try `rustc --explain E0596`.
diff --git a/src/test/ui/lifetimes/issue-64173-unused-lifetimes.rs b/src/test/ui/lifetimes/issue-64173-unused-lifetimes.rs
new file mode 100644 (file)
index 0000000..8080dd7
--- /dev/null
@@ -0,0 +1,19 @@
+use std::mem::size_of;
+
+struct Foo<'s> { //~ ERROR: parameter `'s` is never used
+    array: [(); size_of::<&Self>()],
+    //~^ ERROR: generic `Self` types are currently not permitted in anonymous constants
+}
+
+// The below is taken from https://github.com/rust-lang/rust/issues/66152#issuecomment-550275017
+// as the root cause seems the same.
+
+const fn foo<T>() -> usize {
+    0
+}
+
+struct Bar<'a> { //~ ERROR: parameter `'a` is never used
+    beta: [(); foo::<&'a ()>()], //~ ERROR: a non-static lifetime is not allowed in a `const`
+}
+
+fn main() {}
diff --git a/src/test/ui/lifetimes/issue-64173-unused-lifetimes.stderr b/src/test/ui/lifetimes/issue-64173-unused-lifetimes.stderr
new file mode 100644 (file)
index 0000000..a487cbe
--- /dev/null
@@ -0,0 +1,35 @@
+error[E0658]: a non-static lifetime is not allowed in a `const`
+  --> $DIR/issue-64173-unused-lifetimes.rs:16:23
+   |
+LL |     beta: [(); foo::<&'a ()>()],
+   |                       ^^
+   |
+   = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
+   = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
+
+error: generic `Self` types are currently not permitted in anonymous constants
+  --> $DIR/issue-64173-unused-lifetimes.rs:4:28
+   |
+LL |     array: [(); size_of::<&Self>()],
+   |                            ^^^^
+
+error[E0392]: parameter `'s` is never used
+  --> $DIR/issue-64173-unused-lifetimes.rs:3:12
+   |
+LL | struct Foo<'s> {
+   |            ^^ unused parameter
+   |
+   = help: consider removing `'s`, referring to it in a field, or using a marker such as `PhantomData`
+
+error[E0392]: parameter `'a` is never used
+  --> $DIR/issue-64173-unused-lifetimes.rs:15:12
+   |
+LL | struct Bar<'a> {
+   |            ^^ unused parameter
+   |
+   = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0392, E0658.
+For more information about an error, try `rustc --explain E0392`.
index 4b4169faadf55d5d309c5440f22932b0c6a75217..f5f7bf0a758e151bec262f57d981619cfa48f3c6 100644 (file)
@@ -23,6 +23,4 @@ fn main() {
     let mut conflict = Repro;
     let prev = conflict.get();
     conflict.insert(*prev + *x);
-    //~^ WARN cannot borrow `conflict` as mutable because it is also borrowed as immutable
-    //~| WARN this borrowing pattern was not meant to be accepted
 }
diff --git a/src/test/ui/nll/lint-no-err.stderr b/src/test/ui/nll/lint-no-err.stderr
deleted file mode 100644 (file)
index 1e7aecf..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-warning: cannot borrow `conflict` as mutable because it is also borrowed as immutable
-  --> $DIR/lint-no-err.rs:25:5
-   |
-LL |     let prev = conflict.get();
-   |                -------------- immutable borrow occurs here
-LL |     conflict.insert(*prev + *x);
-   |     ^^^^^^^^^^^^^^^^-----^^^^^^
-   |     |               |
-   |     |               immutable borrow later used here
-   |     mutable borrow occurs here
-   |
-   = note: `#[warn(mutable_borrow_reservation_conflict)]` on by default
-   = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future
-   = note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159>
-
-warning: 1 warning emitted
-
index 2bc34f3c6bfc072a427d6041e3249ce4faa41117..a2e30e2e93b5a63f009dccf6280be1cc887cdd34 100644 (file)
 
 #[proc_macro]
 pub fn expand_expr_is(input: TokenStream) -> TokenStream {
+    expand_expr_is_inner(input, false)
+}
+
+#[proc_macro]
+pub fn expand_expr_is_trim(input: TokenStream) -> TokenStream {
+    expand_expr_is_inner(input, true)
+}
+
+fn expand_expr_is_inner(input: TokenStream, trim_invisible: bool) -> TokenStream {
     let mut iter = input.into_iter();
     let mut expected_tts = Vec::new();
     loop {
@@ -22,14 +31,18 @@ pub fn expand_expr_is(input: TokenStream) -> TokenStream {
         }
     }
 
-    let expected = expected_tts.into_iter().collect::<TokenStream>();
-    let expanded = iter.collect::<TokenStream>().expand_expr().expect("expand_expr failed");
-    assert!(
-        expected.to_string() == expanded.to_string(),
-        "assert failed\nexpected: `{}`\nexpanded: `{}`",
-        expected.to_string(),
-        expanded.to_string()
-    );
+    // If requested, trim the "invisible" delimiters at the start and end.
+    let expected = expected_tts.into_iter().collect::<TokenStream>().to_string();
+    let expected = if trim_invisible {
+        let len1 = "/*«*/ ".len();
+        let len2 = " /*»*/".len();
+        &expected[len1..expected.len() - len2]
+    } else {
+        &expected[..]
+    };
+    let expanded = iter.collect::<TokenStream>().expand_expr().unwrap().to_string();
+
+    assert_eq!(expected, expanded);
 
     TokenStream::new()
 }
index 4de8746a1b4602b64cbcbb9c101563dae7f64b31..3d0e7eaff00d853f5356582cf0bbb7152163f839 100644 (file)
@@ -1,4 +1,5 @@
 PRINT-BANG INPUT (DISPLAY): self
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ self /*»*/
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Group {
         delimiter: None,
@@ -13,8 +14,10 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
 ]
 PRINT-BANG INPUT (DISPLAY): 1 + 1, { "a" }, let a = 1;, String, my_name, 'a, my_val = 30,
 std::option::Option, pub(in some::path) , [a b c], -30
-PRINT-BANG RE-COLLECTED (DISPLAY): 1 + 1, { "a" }, let a = 1, String, my_name, 'a, my_val = 30,
-std :: option :: Option, pub(in some :: path), [a b c], - 30
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ 1 + 1 /*»*/, /*«*/ { "a" } /*»*/, /*«*/ let a = 1 /*»*/, /*«*/
+String /*»*/, my_name, /*«*/ 'a /*»*/, /*«*/ my_val = 30 /*»*/, /*«*/
+std :: option :: Option /*»*/, /*«*/ pub(in some :: path) /*»*/, [a b c],
+/*«*/ - 30 /*»*/
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Group {
         delimiter: None,
@@ -295,6 +298,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
     },
 ]
 PRINT-BANG INPUT (DISPLAY): (a, b)
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ (a, b) /*»*/
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Group {
         delimiter: None,
index 7e6b540332c7963c95654412c6378c84e7d01400..5fe6ff72b4544a93276baf6f8054cd9f6892b822 100644 (file)
@@ -1,5 +1,5 @@
 PRINT-BANG INPUT (DISPLAY): Vec<u8>
-PRINT-BANG RE-COLLECTED (DISPLAY): Vec < u8 >
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ Vec < u8 > /*»*/
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Group {
         delimiter: None,
index d1146d970306264b1485cfc6f243bd51134a62f4..edcb30f892cdf0b359018eccd74bee28a87643f5 100644 (file)
@@ -2,9 +2,9 @@
 
 extern crate expand_expr;
 
-use expand_expr::{
-    check_expand_expr_file, echo_pm, expand_expr_fail, expand_expr_is, recursive_expand,
-};
+use expand_expr::{check_expand_expr_file, echo_pm, expand_expr_fail, expand_expr_is};
+use expand_expr::{expand_expr_is_trim, recursive_expand};
+
 
 // Check builtin macros can be expanded.
 
@@ -47,21 +47,21 @@ macro_rules! echo_expr {
 
 macro_rules! simple_lit {
     ($l:literal) => {
-        expand_expr_is!($l, $l);
-        expand_expr_is!($l, echo_lit!($l));
-        expand_expr_is!($l, echo_expr!($l));
-        expand_expr_is!($l, echo_tts!($l));
-        expand_expr_is!($l, echo_pm!($l));
+        expand_expr_is_trim!($l, $l);
+        expand_expr_is_trim!($l, echo_lit!($l));
+        expand_expr_is_trim!($l, echo_expr!($l));
+        expand_expr_is_trim!($l, echo_tts!($l));
+        expand_expr_is_trim!($l, echo_pm!($l));
         const _: () = {
             macro_rules! mac {
                 () => {
                     $l
                 };
             }
-            expand_expr_is!($l, mac!());
-            expand_expr_is!($l, echo_expr!(mac!()));
-            expand_expr_is!($l, echo_tts!(mac!()));
-            expand_expr_is!($l, echo_pm!(mac!()));
+            expand_expr_is_trim!($l, mac!());
+            expand_expr_is_trim!($l, echo_expr!(mac!()));
+            expand_expr_is_trim!($l, echo_tts!(mac!()));
+            expand_expr_is_trim!($l, echo_pm!(mac!()));
         };
     };
 }
index 686d53e8876608a322b87badfbd6f4cd5b0b580f..04b516fd254243550031cc841af0047137c05c87 100644 (file)
@@ -1,5 +1,6 @@
 PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = #[allow(warnings)] 0 ; 0 }, }
-PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = #[allow(warnings)] #[allow(warnings)] 0 ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E
+{ V = { let _ = /*«*/ #[allow(warnings)] #[allow(warnings)] 0 /*»*/ ; 0 }, }
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "enum",
@@ -123,7 +124,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
     },
 ]
 PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { 0; } ; 0 }, }
-PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { 0 } ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ 0 /*»*/ } ; 0 }, }
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "enum",
@@ -203,6 +204,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
     },
 ]
 PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { {} } ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ {} /*»*/ } ; 0 }, }
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "enum",
@@ -281,7 +283,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
     },
 ]
 PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { PATH; } ; 0 }, }
-PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { PATH } ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ PATH /*»*/ } ; 0 }, }
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "enum",
@@ -359,7 +361,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
     },
 ]
 PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { 0 + 1; } ; 0 }, }
-PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { 0 + 1 } ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ 0 + 1 /*»*/ } ; 0 }, }
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "enum",
@@ -450,7 +452,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [
     },
 ]
 PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { PATH + 1; } ; 0 }, }
-PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { PATH + 1 } ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ PATH + 1 /*»*/ } ; 0 }, }
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "enum",
index 0fda6654ff37051f9a4c3c18d72256ee430fb547..55818969c71781c5829b4f67fac0e49d858bed54 100644 (file)
@@ -96,6 +96,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [
     },
 ]
 PRINT-BANG INPUT (DISPLAY): 1 + 1 * 2
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ 1 + 1 /*»*/ * 2
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Group {
         delimiter: None,
index 60a400a5deabfd0d57f6b3d4a1dc338aad28f172..6cf8043c34f81fd95307d67482e54782877994af 100644 (file)
@@ -1,7 +1,7 @@
 PRINT-BANG INPUT (DISPLAY): foo! { #[fake_attr] mod bar {
     #![doc = r" Foo"]
 } }
-PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): foo! { #[fake_attr] mod bar { #! [doc = r" Foo"] } }
+PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): foo! { #[fake_attr] /*«*/ mod bar { #! [doc = r" Foo"] } /*»*/ }
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Ident {
         ident: "foo",
index 4b7ed874307d8f6c65dc8ad6cfb139945c6e0876..adbd653ead4b7e5ddc1b35c455a4bd0caa8ebf66 100644 (file)
@@ -1,4 +1,5 @@
 PRINT-BANG INPUT (DISPLAY): ;
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ ; /*»*/
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Group {
         delimiter: None,
index a3d24dd26fe978f19f3dac03d4f96773b613c215..b912e426d5d993869e647655e92a68158af3a0da 100644 (file)
@@ -1,4 +1,6 @@
 PRINT-BANG INPUT (DISPLAY): 0 + 1 + 2 + 3
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ 0 + 1 + 2 /*»*/ + 3
+PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): /*«*/ /*«*/ /*«*/ 0 /*»*/ + 1 /*»*/ + 2 /*»*/ + 3
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Group {
         delimiter: None,
index 6b410f0bfb7e3b856c06f1db4deb0e5248aac630..0d2f33b41750d1401c43e0d76dbd7c98d1e58ca8 100644 (file)
@@ -1,4 +1,5 @@
 PRINT-BANG INPUT (DISPLAY): "hi" 1 + (25) + 1 (1 + 1)
+PRINT-BANG RE-COLLECTED (DISPLAY): "hi" /*«*/ 1 + (25) + 1 /*»*/ (1 + 1)
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Literal {
         kind: Str,
@@ -71,6 +72,9 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
     },
 ]
 PRINT-BANG INPUT (DISPLAY): "hi" "hello".len() + "world".len() (1 + 1)
+PRINT-BANG RE-COLLECTED (DISPLAY): "hi" /*«*/ "hello".len() + "world".len() /*»*/ (1 + 1)
+PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): "hi" /*«*/ /*«*/ "hello".len() /*»*/ + /*«*/ "world".len() /*»*/ /*»*/
+(1 + 1)
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Literal {
         kind: Str,
index 4d884348f2ca423df47d1f8aa3b77f28670566a2..32981e7011d970a7182718fe542bb120067c1247 100644 (file)
@@ -1,5 +1,5 @@
 PRINT-ATTR_ARGS INPUT (DISPLAY): a, line!(), b
-PRINT-ATTR_ARGS RE-COLLECTED (DISPLAY): a, line! (), b
+PRINT-ATTR_ARGS RE-COLLECTED (DISPLAY): a, /*«*/ line! () /*»*/, b
 PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
     Ident {
         ident: "a",
index c08e5308138c966deb834cceb151ad98e058099f..ba18ca75d7fe4dc2743da1d8cfda7d9390f7b377 100644 (file)
@@ -1,5 +1,5 @@
 PRINT-BANG INPUT (DISPLAY): struct S;
-PRINT-BANG RE-COLLECTED (DISPLAY): struct S ;
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ struct S ; /*»*/
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Group {
         delimiter: None,
index 354657db4db380f5b068a3f04c1e94793b86a5ef..71e5065a87a8888c63c19e433bc597cb38c55cc9 100644 (file)
@@ -8,16 +8,16 @@
 
 macro one($a:expr, $b:expr) {
     two!($a, $b);
-    //~^ ERROR first parent: "hello"
-    //~| ERROR second parent: "world"
+    //~^ ERROR first parent: /*«*/ "hello" /*»*/
+    //~| ERROR second parent: /*«*/ "world" /*»*/
 }
 
 macro two($a:expr, $b:expr) {
     three!($a, $b);
-    //~^ ERROR first final: "hello"
-    //~| ERROR second final: "world"
-    //~| ERROR first final: "yay"
-    //~| ERROR second final: "rust"
+    //~^ ERROR first final: /*«*/ "hello" /*»*/
+    //~| ERROR second final: /*«*/ "world" /*»*/
+    //~| ERROR first final: /*«*/ "yay" /*»*/
+    //~| ERROR second final: /*«*/ "rust" /*»*/
 }
 
 // forwarding tokens directly doesn't create a new source chain
 
 fn main() {
     one!("hello", "world");
-    //~^ ERROR first grandparent: "hello"
-    //~| ERROR second grandparent: "world"
-    //~| ERROR first source: "hello"
-    //~| ERROR second source: "world"
+    //~^ ERROR first grandparent: /*«*/ "hello" /*»*/
+    //~| ERROR second grandparent: /*«*/ "world" /*»*/
+    //~| ERROR first source: /*«*/ "hello" /*»*/
+    //~| ERROR second source: /*«*/ "world" /*»*/
 
     two!("yay", "rust");
-    //~^ ERROR first parent: "yay"
-    //~| ERROR second parent: "rust"
-    //~| ERROR first source: "yay"
-    //~| ERROR second source: "rust"
+    //~^ ERROR first parent: /*«*/ "yay" /*»*/
+    //~| ERROR second parent: /*«*/ "rust" /*»*/
+    //~| ERROR first source: /*«*/ "yay" /*»*/
+    //~| ERROR second source: /*«*/ "rust" /*»*/
 
     three!("hip", "hop");
     //~^ ERROR first final: "hip"
index 4548269b507930fb1bd407fbca12afc4f5e9f9a8..e42218ea70117b3ed05144b789e5bcf044618870 100644 (file)
@@ -1,4 +1,4 @@
-error: first final: "hello"
+error: first final: /*«*/ "hello" /*»*/
   --> $DIR/parent-source-spans.rs:16:12
    |
 LL |     three!($a, $b);
@@ -9,7 +9,7 @@ LL |     one!("hello", "world");
    |
    = note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: second final: "world"
+error: second final: /*«*/ "world" /*»*/
   --> $DIR/parent-source-spans.rs:16:16
    |
 LL |     three!($a, $b);
@@ -20,7 +20,7 @@ LL |     one!("hello", "world");
    |
    = note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: first parent: "hello"
+error: first parent: /*«*/ "hello" /*»*/
   --> $DIR/parent-source-spans.rs:10:5
    |
 LL |     two!($a, $b);
@@ -31,7 +31,7 @@ LL |     one!("hello", "world");
    |
    = note: this error originates in the macro `one` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: second parent: "world"
+error: second parent: /*«*/ "world" /*»*/
   --> $DIR/parent-source-spans.rs:10:5
    |
 LL |     two!($a, $b);
@@ -42,31 +42,31 @@ LL |     one!("hello", "world");
    |
    = note: this error originates in the macro `one` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: first grandparent: "hello"
+error: first grandparent: /*«*/ "hello" /*»*/
   --> $DIR/parent-source-spans.rs:36:5
    |
 LL |     one!("hello", "world");
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
-error: second grandparent: "world"
+error: second grandparent: /*«*/ "world" /*»*/
   --> $DIR/parent-source-spans.rs:36:5
    |
 LL |     one!("hello", "world");
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
-error: first source: "hello"
+error: first source: /*«*/ "hello" /*»*/
   --> $DIR/parent-source-spans.rs:36:5
    |
 LL |     one!("hello", "world");
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
-error: second source: "world"
+error: second source: /*«*/ "world" /*»*/
   --> $DIR/parent-source-spans.rs:36:5
    |
 LL |     one!("hello", "world");
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
-error: first final: "yay"
+error: first final: /*«*/ "yay" /*»*/
   --> $DIR/parent-source-spans.rs:16:12
    |
 LL |     three!($a, $b);
@@ -77,7 +77,7 @@ LL |     two!("yay", "rust");
    |
    = note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: second final: "rust"
+error: second final: /*«*/ "rust" /*»*/
   --> $DIR/parent-source-spans.rs:16:16
    |
 LL |     three!($a, $b);
@@ -88,25 +88,25 @@ LL |     two!("yay", "rust");
    |
    = note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: first parent: "yay"
+error: first parent: /*«*/ "yay" /*»*/
   --> $DIR/parent-source-spans.rs:42:5
    |
 LL |     two!("yay", "rust");
    |     ^^^^^^^^^^^^^^^^^^^
 
-error: second parent: "rust"
+error: second parent: /*«*/ "rust" /*»*/
   --> $DIR/parent-source-spans.rs:42:5
    |
 LL |     two!("yay", "rust");
    |     ^^^^^^^^^^^^^^^^^^^
 
-error: first source: "yay"
+error: first source: /*«*/ "yay" /*»*/
   --> $DIR/parent-source-spans.rs:42:5
    |
 LL |     two!("yay", "rust");
    |     ^^^^^^^^^^^^^^^^^^^
 
-error: second source: "rust"
+error: second source: /*«*/ "rust" /*»*/
   --> $DIR/parent-source-spans.rs:42:5
    |
 LL |     two!("yay", "rust");
index d187267388577afba36d530d8e59e1888cff7e63..b8d528efc15909eaf42223a3153e7c4cf8205898 100644 (file)
@@ -48,6 +48,11 @@ error[E0425]: cannot find function `static_method` in this scope
    |
 LL |         static_method();
    |         ^^^^^^^^^^^^^ not found in this scope
+   |
+help: consider using the associated function
+   |
+LL |         Self::static_method();
+   |         ~~~~~~~~~~~~~~~~~~~
 
 error[E0425]: cannot find function `purr` in this scope
   --> $DIR/issue-2356.rs:54:9
@@ -85,6 +90,11 @@ error[E0425]: cannot find function `grow_older` in this scope
    |
 LL |     grow_older();
    |     ^^^^^^^^^^ not found in this scope
+   |
+help: consider using the associated function
+   |
+LL |     Self::grow_older();
+   |     ~~~~~~~~~~~~~~~~
 
 error[E0425]: cannot find function `shave` in this scope
   --> $DIR/issue-2356.rs:74:5
diff --git a/src/test/ui/suggestions/assoc_fn_without_self.rs b/src/test/ui/suggestions/assoc_fn_without_self.rs
new file mode 100644 (file)
index 0000000..778d984
--- /dev/null
@@ -0,0 +1,20 @@
+fn main() {}
+
+struct S;
+
+impl S {
+    fn foo() {}
+
+    fn bar(&self) {}
+
+    fn baz(a: u8, b: u8) {}
+
+    fn b() {
+        fn c() {
+            foo(); //~ ERROR cannot find function `foo` in this scope
+        }
+        foo(); //~ ERROR cannot find function `foo` in this scope
+        bar(); //~ ERROR cannot find function `bar` in this scope
+        baz(2, 3); //~ ERROR cannot find function `baz` in this scope
+    }
+}
diff --git a/src/test/ui/suggestions/assoc_fn_without_self.stderr b/src/test/ui/suggestions/assoc_fn_without_self.stderr
new file mode 100644 (file)
index 0000000..4a0e62e
--- /dev/null
@@ -0,0 +1,37 @@
+error[E0425]: cannot find function `foo` in this scope
+  --> $DIR/assoc_fn_without_self.rs:14:13
+   |
+LL |             foo();
+   |             ^^^ not found in this scope
+
+error[E0425]: cannot find function `foo` in this scope
+  --> $DIR/assoc_fn_without_self.rs:16:9
+   |
+LL |         foo();
+   |         ^^^ not found in this scope
+   |
+help: consider using the associated function
+   |
+LL |         Self::foo();
+   |         ~~~~~~~~~
+
+error[E0425]: cannot find function `bar` in this scope
+  --> $DIR/assoc_fn_without_self.rs:17:9
+   |
+LL |         bar();
+   |         ^^^ not found in this scope
+
+error[E0425]: cannot find function `baz` in this scope
+  --> $DIR/assoc_fn_without_self.rs:18:9
+   |
+LL |         baz(2, 3);
+   |         ^^^ not found in this scope
+   |
+help: consider using the associated function
+   |
+LL |         Self::baz(2, 3);
+   |         ~~~~~~~~~
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0425`.
index 1c0f83d6c12da643ed6803a60db80bd303345c3a..98ac215ad6cc53f6b7a280eb71df97f57e35cffd 100644 (file)
@@ -12,6 +12,6 @@ fn use_alias<T>(val: T) -> AliasOfForeignType<T> {
 }
 
 impl<T> foreign_crate::ForeignTrait for AliasOfForeignType<T> {}
-//~^ ERROR the type parameter `T` is not constrained by the impl trait, self type, or predicates
+//~^ ERROR cannot implement trait on type alias impl trait
 
 fn main() {}
index 6ede0fa14ba707abf6d2b180935d86e69d52ac19..3ce25d94f6e124b832d7d0e97a697f083b351831 100644 (file)
@@ -1,9 +1,14 @@
-error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates
-  --> $DIR/coherence.rs:14:6
+error: cannot implement trait on type alias impl trait
+  --> $DIR/coherence.rs:14:41
    |
 LL | impl<T> foreign_crate::ForeignTrait for AliasOfForeignType<T> {}
-   |      ^ unconstrained type parameter
+   |                                         ^^^^^^^^^^^^^^^^^^^^^
+   |
+note: type alias impl trait defined here
+  --> $DIR/coherence.rs:9:30
+   |
+LL | type AliasOfForeignType<T> = impl LocalTrait;
+   |                              ^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0207`.
index d610e8c7bc478ee1fc9b98ed6e0bef9debe1355a..89884bfc85902ee4cf825d2d5130a6d5b9935d69 100644 (file)
@@ -9,7 +9,7 @@ body:
     attributes:
       label: Description
       description: >
-        Please provide a discription of the issue, along with any information
+        Please provide a description of the issue, along with any information
         you feel relevant to replicate it.
     validations:
       required: true
index 9357ccc4f4e76f26d013ac115fae3fba71a175c6..25e436d30b97dc42dacee379ad90f9d069c462d5 100644 (file)
@@ -23,7 +23,7 @@ body:
     id: reproducer
     attributes:
       label: Reproducer
-      description: Please provide the code and steps to repoduce the bug
+      description: Please provide the code and steps to reproduce the bug
       value: |
         I tried this code:
 
index b7dd400ee7333efa6ea35295abb6e2624519128d..561b65c93a7f9329799d2be796c2590b1ae215dd 100644 (file)
@@ -24,7 +24,7 @@ body:
     attributes:
       label: Reproducer
       description: >
-        Please provide the code and steps to repoduce the bug together with the
+        Please provide the code and steps to reproduce the bug together with the
         output from Clippy.
       value: |
         I tried this code:
index cd83bc9642b60c73ee1529c492512b993f251fab..0e27cc927acea4c010f810cd350f047eebece429 100644 (file)
@@ -6,14 +6,14 @@ on:
     branches-ignore:
       - auto
       - try
-    # Don't run Clippy tests, when only textfiles were modified
+    # Don't run Clippy tests, when only text files were modified
     paths-ignore:
     - 'COPYRIGHT'
     - 'LICENSE-*'
     - '**.md'
     - '**.txt'
   pull_request:
-    # Don't run Clippy tests, when only textfiles were modified
+    # Don't run Clippy tests, when only text files were modified
     paths-ignore:
     - 'COPYRIGHT'
     - 'LICENSE-*'
@@ -37,7 +37,7 @@ jobs:
         github_token: "${{ secrets.github_token }}"
 
     - name: Checkout
-      uses: actions/checkout@v2.3.3
+      uses: actions/checkout@v3.0.2
 
     - name: Install toolchain
       run: rustup show active-toolchain
index f571485e6d389befdf909d2f9ab5fa7bf25db69a..9b3fd3ddfeb38cc357c20e0c818b2fcad5cd9977 100644 (file)
@@ -25,7 +25,7 @@ jobs:
         github_token: "${{ secrets.github_token }}"
 
     - name: Checkout
-      uses: actions/checkout@v2.3.3
+      uses: actions/checkout@v3.0.2
       with:
         ref: ${{ github.ref }}
 
@@ -88,7 +88,7 @@ jobs:
       if: matrix.host == 'i686-unknown-linux-gnu'
 
     - name: Checkout
-      uses: actions/checkout@v2.3.3
+      uses: actions/checkout@v3.0.2
 
     - name: Install toolchain
       run: rustup show active-toolchain
@@ -154,7 +154,7 @@ jobs:
         github_token: "${{ secrets.github_token }}"
 
     - name: Checkout
-      uses: actions/checkout@v2.3.3
+      uses: actions/checkout@v3.0.2
 
     - name: Install toolchain
       run: rustup show active-toolchain
@@ -212,7 +212,7 @@ jobs:
         github_token: "${{ secrets.github_token }}"
 
     - name: Checkout
-      uses: actions/checkout@v2.3.3
+      uses: actions/checkout@v3.0.2
 
     - name: Install toolchain
       run: rustup show active-toolchain
index 5dfab1d2ebc402495477e11fd70daa13372d38ce..22051093c9cf960992c3a80923e71f72ef4414f1 100644 (file)
@@ -23,7 +23,7 @@ jobs:
     steps:
     # Setup
     - name: Checkout
-      uses: actions/checkout@v2.3.3
+      uses: actions/checkout@v3.0.2
 
     # Run
     - name: Build
index b8be730be32b00644ec1ed70658a832bf2284397..71d71d10359e7e76b4f2cbc1b8843247d266659c 100644 (file)
@@ -21,10 +21,10 @@ jobs:
     steps:
     # Setup
     - name: Checkout
-      uses: actions/checkout@v2.3.3
+      uses: actions/checkout@v3.0.2
 
     - name: Checkout
-      uses: actions/checkout@v2.3.3
+      uses: actions/checkout@v3.0.2
       with:
         ref: ${{ env.TARGET_BRANCH }}
         path: 'out'
index 56c00544c93a7892d7aa80a0ec05e6c70ad0734d..a179bfa72617213735d50473ae66a73dc2355ecc 100644 (file)
@@ -16,7 +16,7 @@ jobs:
     steps:
     # Setup
     - name: Checkout
-      uses: actions/checkout@v2.3.3
+      uses: actions/checkout@v3.0.2
 
     - name: Setup Node.js
       uses: actions/setup-node@v1.4.4
index b4097ea86a5185ce492067bad0ffb086733ad4c1..751f9fccd88d4389866d835eb08ebd0a3d3547a7 100644 (file)
@@ -6,7 +6,108 @@ document.
 
 ## Unreleased / In Rust Nightly
 
-[57b3c4b...master](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...master)
+[d0cf3481...master](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...master)
+
+## Rust 1.61 (beta)
+
+Current beta, released 2022-05-19
+
+[57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481)
+
+### New Lints
+
+* [`only_used_in_recursion`]
+  [#8422](https://github.com/rust-lang/rust-clippy/pull/8422)
+* [`cast_enum_truncation`]
+  [#8381](https://github.com/rust-lang/rust-clippy/pull/8381)
+* [`missing_spin_loop`]
+  [#8174](https://github.com/rust-lang/rust-clippy/pull/8174)
+* [`deref_by_slicing`]
+  [#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
+* [`needless_match`]
+  [#8471](https://github.com/rust-lang/rust-clippy/pull/8471)
+* [`allow_attributes_without_reason`] (Requires `#![feature(lint_reasons)]`)
+  [#8504](https://github.com/rust-lang/rust-clippy/pull/8504)
+* [`print_in_format_impl`]
+  [#8253](https://github.com/rust-lang/rust-clippy/pull/8253)
+* [`unnecessary_find_map`]
+  [#8489](https://github.com/rust-lang/rust-clippy/pull/8489)
+* [`or_then_unwrap`]
+  [#8561](https://github.com/rust-lang/rust-clippy/pull/8561)
+* [`unnecessary_join`]
+  [#8579](https://github.com/rust-lang/rust-clippy/pull/8579)
+* [`iter_with_drain`]
+  [#8483](https://github.com/rust-lang/rust-clippy/pull/8483)
+* [`cast_enum_constructor`]
+  [#8562](https://github.com/rust-lang/rust-clippy/pull/8562)
+* [`cast_slice_different_sizes`]
+  [#8445](https://github.com/rust-lang/rust-clippy/pull/8445)
+
+### Moves and Deprecations
+
+* Moved [`transmute_undefined_repr`] to `nursery` (now allow-by-default)
+  [#8432](https://github.com/rust-lang/rust-clippy/pull/8432)
+* Moved [`try_err`] to `restriction`
+  [#8544](https://github.com/rust-lang/rust-clippy/pull/8544)
+* Move [`iter_with_drain`] to `nursery`
+  [#8541](https://github.com/rust-lang/rust-clippy/pull/8541)
+* Renamed `to_string_in_display` to [`recursive_format_impl`]
+  [#8188](https://github.com/rust-lang/rust-clippy/pull/8188)
+
+### Enhancements
+
+* [`dbg_macro`]: The lint level can now be set with crate attributes and works inside macros
+  [#8411](https://github.com/rust-lang/rust-clippy/pull/8411)
+* [`ptr_as_ptr`]: Now works inside macros
+  [#8442](https://github.com/rust-lang/rust-clippy/pull/8442)
+* [`use_self`]: Now works for variants in match expressions
+  [#8456](https://github.com/rust-lang/rust-clippy/pull/8456)
+* [`await_holding_lock`]: Now lints for `parking_lot::{Mutex, RwLock}`
+  [#8419](https://github.com/rust-lang/rust-clippy/pull/8419)
+* [`recursive_format_impl`]: Now checks for format calls on `self`
+  [#8188](https://github.com/rust-lang/rust-clippy/pull/8188)
+
+### False Positive Fixes
+
+* [`new_without_default`]: No longer lints for `new()` methods with `#[doc(hidden)]`
+  [#8472](https://github.com/rust-lang/rust-clippy/pull/8472)
+* [`transmute_undefined_repr`]: No longer lints for single field structs with `#[repr(C)]`,
+  generic parameters, wide pointers, unions, tuples and allow several forms of type erasure
+  [#8425](https://github.com/rust-lang/rust-clippy/pull/8425)
+  [#8553](https://github.com/rust-lang/rust-clippy/pull/8553)
+  [#8440](https://github.com/rust-lang/rust-clippy/pull/8440)
+  [#8547](https://github.com/rust-lang/rust-clippy/pull/8547)
+* [`match_single_binding`], [`match_same_arms`], [`match_as_ref`], [`match_bool`]: No longer
+  lint `match` expressions with `cfg`ed arms
+  [#8443](https://github.com/rust-lang/rust-clippy/pull/8443)
+* [`single_component_path_imports`]: No longer lint on macros
+  [#8537](https://github.com/rust-lang/rust-clippy/pull/8537)
+* [`ptr_arg`]: Allow `&mut` arguments for `Cow<_>`
+  [#8552](https://github.com/rust-lang/rust-clippy/pull/8552)
+* [`needless_borrow`]: No longer lints for method calls
+  [#8441](https://github.com/rust-lang/rust-clippy/pull/8441)
+* [`match_same_arms`]: Now ensures that interposing arm patterns don't overlap
+  [#8232](https://github.com/rust-lang/rust-clippy/pull/8232)
+* [`default_trait_access`]: Now allows `Default::default` in update expressions
+  [#8433](https://github.com/rust-lang/rust-clippy/pull/8433)
+
+### Suggestion Fixes/Improvements
+
+* [`redundant_slicing`]: Fixed suggestion for a method calls
+  [#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
+* [`map_flatten`]: Long suggestions will now be split up into two help messages
+  [#8520](https://github.com/rust-lang/rust-clippy/pull/8520)
+* [`unnecessary_lazy_evaluations`]: Now shows suggestions for longer code snippets
+  [#8543](https://github.com/rust-lang/rust-clippy/pull/8543)
+* [`unnecessary_sort_by`]: Now suggests `Reverse` including the path
+  [#8462](https://github.com/rust-lang/rust-clippy/pull/8462)
+* [`search_is_some`]: More suggestions are now `MachineApplicable`
+  [#8536](https://github.com/rust-lang/rust-clippy/pull/8536)
+
+### Documentation Improvements
+
+* [`new_without_default`]: Document `pub` requirement for the struct and fields
+  [#8429](https://github.com/rust-lang/rust-clippy/pull/8429)
 
 ## Rust 1.60
 
@@ -3182,6 +3283,7 @@ Released 2018-09-13
 [`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
 [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
 [`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
+[`await_holding_invalid_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type
 [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
 [`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
 [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
@@ -3198,6 +3300,7 @@ Released 2018-09-13
 [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
 [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
 [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
+[`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len
 [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
 [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
 [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
@@ -3262,6 +3365,7 @@ Released 2018-09-13
 [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
 [`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
 [`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
+[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop
 [`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
 [`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
 [`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
@@ -3314,6 +3418,7 @@ Released 2018-09-13
 [`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop
 [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
 [`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args
+[`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string
 [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
 [`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
 [`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10
@@ -3356,6 +3461,7 @@ Released 2018-09-13
 [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
 [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
 [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
+[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
 [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
 [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
 [`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
@@ -3372,6 +3478,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_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
 [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
@@ -3472,6 +3579,7 @@ Released 2018-09-13
 [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
 [`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
 [`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
+[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take
 [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
 [`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
 [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
@@ -3527,6 +3635,7 @@ Released 2018-09-13
 [`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
 [`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
 [`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
+[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
 [`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
 [`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
 [`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
@@ -3627,6 +3736,7 @@ Released 2018-09-13
 [`transmute_undefined_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_undefined_repr
 [`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
 [`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
+[`trim_split_whitespace`]: https://rust-lang.github.io/rust-clippy/master/index.html#trim_split_whitespace
 [`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
 [`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
 [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
@@ -3650,6 +3760,7 @@ Released 2018-09-13
 [`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
 [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
+[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
 [`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_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned
index 81faa5fe5e14823474fa0a90661da56ea75afa1e..2cfbcea5034e653beb818b1be5dc89b94b856d2c 100644 (file)
@@ -4,6 +4,7 @@ version = "0.0.1"
 edition = "2021"
 
 [dependencies]
+aho-corasick = "0.7"
 clap = "2.33"
 indoc = "1.0"
 itertools = "0.10.1"
index 9c6d754b400fc0f41595cfd8a4a745a1de926dd9..81e807cf10c7c3a489e3a601b569714b160fbf66 100644 (file)
@@ -1,3 +1,4 @@
+#![feature(let_chains)]
 #![feature(let_else)]
 #![feature(once_cell)]
 #![feature(rustc_private)]
index b1fe35a0243f04e4887cdb020edd0de32040c7eb..ebf8f38d4906eae4d15eaf1c3ef6179295216553 100644 (file)
@@ -19,9 +19,9 @@ fn main() {
             if matches.is_present("print-only") {
                 update_lints::print_lints();
             } else if matches.is_present("check") {
-                update_lints::run(update_lints::UpdateMode::Check);
+                update_lints::update(update_lints::UpdateMode::Check);
             } else {
-                update_lints::run(update_lints::UpdateMode::Change);
+                update_lints::update(update_lints::UpdateMode::Change);
             }
         },
         ("new_lint", Some(matches)) => {
@@ -31,18 +31,36 @@ fn main() {
                 matches.value_of("category"),
                 matches.is_present("msrv"),
             ) {
-                Ok(_) => update_lints::run(update_lints::UpdateMode::Change),
+                Ok(_) => update_lints::update(update_lints::UpdateMode::Change),
                 Err(e) => eprintln!("Unable to create lint: {}", e),
             }
         },
         ("setup", Some(sub_command)) => match sub_command.subcommand() {
-            ("intellij", Some(matches)) => setup::intellij::setup_rustc_src(
-                matches
-                    .value_of("rustc-repo-path")
-                    .expect("this field is mandatory and therefore always valid"),
-            ),
-            ("git-hook", Some(matches)) => setup::git_hook::install_hook(matches.is_present("force-override")),
-            ("vscode-tasks", Some(matches)) => setup::vscode::install_tasks(matches.is_present("force-override")),
+            ("intellij", Some(matches)) => {
+                if matches.is_present("remove") {
+                    setup::intellij::remove_rustc_src();
+                } else {
+                    setup::intellij::setup_rustc_src(
+                        matches
+                            .value_of("rustc-repo-path")
+                            .expect("this field is mandatory and therefore always valid"),
+                    );
+                }
+            },
+            ("git-hook", Some(matches)) => {
+                if matches.is_present("remove") {
+                    setup::git_hook::remove_hook();
+                } else {
+                    setup::git_hook::install_hook(matches.is_present("force-override"));
+                }
+            },
+            ("vscode-tasks", Some(matches)) => {
+                if matches.is_present("remove") {
+                    setup::vscode::remove_tasks();
+                } else {
+                    setup::vscode::install_tasks(matches.is_present("force-override"));
+                }
+            },
             _ => {},
         },
         ("remove", Some(sub_command)) => match sub_command.subcommand() {
@@ -60,6 +78,12 @@ fn main() {
             let path = matches.value_of("path").unwrap();
             lint::run(path);
         },
+        ("rename_lint", Some(matches)) => {
+            let old_name = matches.value_of("old_name").unwrap();
+            let new_name = matches.value_of("new_name").unwrap_or(old_name);
+            let uplift = matches.is_present("uplift");
+            update_lints::rename(old_name, new_name, uplift);
+        },
         _ => {},
     }
 }
@@ -167,6 +191,12 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
                 .subcommand(
                     SubCommand::with_name("intellij")
                         .about("Alter dependencies so Intellij Rust can find rustc internals")
+                        .arg(
+                            Arg::with_name("remove")
+                                .long("remove")
+                                .help("Remove the dependencies added with 'cargo dev setup intellij'")
+                                .required(false),
+                        )
                         .arg(
                             Arg::with_name("rustc-repo-path")
                                 .long("repo-path")
@@ -174,12 +204,19 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
                                 .help("The path to a rustc repo that will be used for setting the dependencies")
                                 .takes_value(true)
                                 .value_name("path")
+                                .conflicts_with("remove")
                                 .required(true),
                         ),
                 )
                 .subcommand(
                     SubCommand::with_name("git-hook")
                         .about("Add a pre-commit git hook that formats your code to make it look pretty")
+                        .arg(
+                            Arg::with_name("remove")
+                                .long("remove")
+                                .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'")
+                                .required(false),
+                        )
                         .arg(
                             Arg::with_name("force-override")
                                 .long("force-override")
@@ -191,6 +228,12 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
                 .subcommand(
                     SubCommand::with_name("vscode-tasks")
                         .about("Add several tasks to vscode for formatting, validation and testing")
+                        .arg(
+                            Arg::with_name("remove")
+                                .long("remove")
+                                .help("Remove the tasks added with 'cargo dev setup vscode-tasks'")
+                                .required(false),
+                        )
                         .arg(
                             Arg::with_name("force-override")
                                 .long("force-override")
@@ -242,5 +285,26 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
                         .help("The path to a file or package directory to lint"),
                 ),
         )
+        .subcommand(
+            SubCommand::with_name("rename_lint")
+                .about("Renames the given lint")
+                .arg(
+                    Arg::with_name("old_name")
+                        .index(1)
+                        .required(true)
+                        .help("The name of the lint to rename"),
+                )
+                .arg(
+                    Arg::with_name("new_name")
+                        .index(2)
+                        .required_unless("uplift")
+                        .help("The new name of the lint"),
+                )
+                .arg(
+                    Arg::with_name("uplift")
+                        .long("uplift")
+                        .help("This lint will be uplifted into rustc"),
+                ),
+        )
         .get_matches()
 }
index 7a3fd1317619e39d3021f8dafe8e0281dc4a99a1..10f67d301f887d5893974cdf2e8292da2f230007 100644 (file)
@@ -1,5 +1,6 @@
 use crate::clippy_project_root;
 use indoc::indoc;
+use std::fmt::Write as _;
 use std::fs::{self, OpenOptions};
 use std::io::prelude::*;
 use std::io::{self, ErrorKind};
@@ -232,7 +233,8 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
         )
     });
 
-    result.push_str(&format!(
+    let _ = write!(
+        result,
         indoc! {r#"
             declare_clippy_lint! {{
                 /// ### What it does
@@ -256,7 +258,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
         version = version,
         name_upper = name_upper,
         category = category,
-    ));
+    );
 
     result.push_str(&if enable_msrv {
         format!(
index a1e4dd103b88bfa708948c44ba7cd8a48ec1a346..f691ae4fa45da22e67d8700894b2365db3d4fad2 100644 (file)
@@ -7,7 +7,7 @@
 const CLIPPY_DEV_DIR: &str = "clippy_dev";
 
 /// This function verifies that the tool is being executed in the clippy directory.
-/// This is useful to ensure that setups only modify Clippys resources. The verification
+/// This is useful to ensure that setups only modify Clippy's resources. The verification
 /// is done by checking that `clippy_dev` is a sub directory of the current directory.
 ///
 /// It will print an error message and return `false` if the directory could not be
@@ -17,7 +17,7 @@ fn verify_inside_clippy_dir() -> bool {
     if path.exists() && path.is_dir() {
         true
     } else {
-        eprintln!("error: unable to verify that the working directory is clippys directory");
+        eprintln!("error: unable to verify that the working directory is clippy's directory");
         false
     }
 }
index 59db51fbfac5149fec5158a137c91e94fa86bf4b..1a6a4336da27e2c2955fcf48a828e7138cd9a95b 100644 (file)
@@ -1,11 +1,13 @@
-use core::fmt::Write;
+use aho_corasick::AhoCorasickBuilder;
+use core::fmt::Write as _;
 use itertools::Itertools;
 use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
 use std::ffi::OsStr;
 use std::fs;
-use std::path::Path;
-use walkdir::WalkDir;
+use std::io::{self, Read as _, Seek as _, Write as _};
+use std::path::{Path, PathBuf};
+use walkdir::{DirEntry, WalkDir};
 
 use crate::clippy_project_root;
 
@@ -30,12 +32,19 @@ pub enum UpdateMode {
 /// # Panics
 ///
 /// Panics if a file path could not read from or then written to
-#[allow(clippy::too_many_lines)]
-pub fn run(update_mode: UpdateMode) {
-    let (lints, deprecated_lints) = gather_all();
+pub fn update(update_mode: UpdateMode) {
+    let (lints, deprecated_lints, renamed_lints) = gather_all();
+    generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints);
+}
 
-    let internal_lints = Lint::internal_lints(&lints);
-    let usable_lints = Lint::usable_lints(&lints);
+fn generate_lint_files(
+    update_mode: UpdateMode,
+    lints: &[Lint],
+    deprecated_lints: &[DeprecatedLint],
+    renamed_lints: &[RenamedLint],
+) {
+    let internal_lints = Lint::internal_lints(lints);
+    let usable_lints = Lint::usable_lints(lints);
     let mut sorted_usable_lints = usable_lints.clone();
     sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
 
@@ -87,7 +96,7 @@ pub fn run(update_mode: UpdateMode) {
     process_file(
         "clippy_lints/src/lib.deprecated.rs",
         update_mode,
-        &gen_deprecated(&deprecated_lints),
+        &gen_deprecated(deprecated_lints),
     );
 
     let all_group_lints = usable_lints.iter().filter(|l| {
@@ -107,10 +116,16 @@ pub fn run(update_mode: UpdateMode) {
             &content,
         );
     }
+
+    let content = gen_deprecated_lints_test(deprecated_lints);
+    process_file("tests/ui/deprecated.rs", update_mode, &content);
+
+    let content = gen_renamed_lints_test(renamed_lints);
+    process_file("tests/ui/rename.rs", update_mode, &content);
 }
 
 pub fn print_lints() {
-    let (lint_list, _) = gather_all();
+    let (lint_list, _, _) = gather_all();
     let usable_lints = Lint::usable_lints(&lint_list);
     let usable_lint_count = usable_lints.len();
     let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
@@ -128,6 +143,209 @@ pub fn print_lints() {
     println!("there are {} lints", usable_lint_count);
 }
 
+/// Runs the `rename_lint` command.
+///
+/// This does the following:
+/// * Adds an entry to `renamed_lints.rs`.
+/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`).
+/// * Renames the lint struct to the new name.
+/// * Renames the module containing the lint struct to the new name if it shares a name with the
+///   lint.
+///
+/// # Panics
+/// Panics for the following conditions:
+/// * If a file path could not read from or then written to
+/// * If either lint name has a prefix
+/// * If `old_name` doesn't name an existing lint.
+/// * If `old_name` names a deprecated or renamed lint.
+#[allow(clippy::too_many_lines)]
+pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
+    if let Some((prefix, _)) = old_name.split_once("::") {
+        panic!("`{}` should not contain the `{}` prefix", old_name, prefix);
+    }
+    if let Some((prefix, _)) = new_name.split_once("::") {
+        panic!("`{}` should not contain the `{}` prefix", new_name, prefix);
+    }
+
+    let (mut lints, deprecated_lints, mut renamed_lints) = gather_all();
+    let mut old_lint_index = None;
+    let mut found_new_name = false;
+    for (i, lint) in lints.iter().enumerate() {
+        if lint.name == old_name {
+            old_lint_index = Some(i);
+        } else if lint.name == new_name {
+            found_new_name = true;
+        }
+    }
+    let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{}`", old_name));
+
+    let lint = RenamedLint {
+        old_name: format!("clippy::{}", old_name),
+        new_name: if uplift {
+            new_name.into()
+        } else {
+            format!("clippy::{}", new_name)
+        },
+    };
+
+    // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in
+    // case.
+    assert!(
+        !renamed_lints.iter().any(|l| lint.old_name == l.old_name),
+        "`{}` has already been renamed",
+        old_name
+    );
+    assert!(
+        !deprecated_lints.iter().any(|l| lint.old_name == l.name),
+        "`{}` has already been deprecated",
+        old_name
+    );
+
+    // Update all lint level attributes. (`clippy::lint_name`)
+    for file in WalkDir::new(clippy_project_root())
+        .into_iter()
+        .map(Result::unwrap)
+        .filter(|f| {
+            let name = f.path().file_name();
+            let ext = f.path().extension();
+            (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed")))
+                && name != Some(OsStr::new("rename.rs"))
+                && name != Some(OsStr::new("renamed_lints.rs"))
+        })
+    {
+        rewrite_file(file.path(), |s| {
+            replace_ident_like(s, &[(&lint.old_name, &lint.new_name)])
+        });
+    }
+
+    renamed_lints.push(lint);
+    renamed_lints.sort_by(|lhs, rhs| {
+        lhs.new_name
+            .starts_with("clippy::")
+            .cmp(&rhs.new_name.starts_with("clippy::"))
+            .reverse()
+            .then_with(|| lhs.old_name.cmp(&rhs.old_name))
+    });
+
+    write_file(
+        Path::new("clippy_lints/src/renamed_lints.rs"),
+        &gen_renamed_lints_list(&renamed_lints),
+    );
+
+    if uplift {
+        write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
+        println!(
+            "`{}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually.",
+            old_name
+        );
+    } else if found_new_name {
+        write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
+        println!(
+            "`{}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually.",
+            new_name
+        );
+    } else {
+        // Rename the lint struct and source files sharing a name with the lint.
+        let lint = &mut lints[old_lint_index];
+        let old_name_upper = old_name.to_uppercase();
+        let new_name_upper = new_name.to_uppercase();
+        lint.name = new_name.into();
+
+        // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist.
+        if try_rename_file(
+            Path::new(&format!("tests/ui/{}.rs", old_name)),
+            Path::new(&format!("tests/ui/{}.rs", new_name)),
+        ) {
+            try_rename_file(
+                Path::new(&format!("tests/ui/{}.stderr", old_name)),
+                Path::new(&format!("tests/ui/{}.stderr", new_name)),
+            );
+            try_rename_file(
+                Path::new(&format!("tests/ui/{}.fixed", old_name)),
+                Path::new(&format!("tests/ui/{}.fixed", new_name)),
+            );
+        }
+
+        // Try to rename the file containing the lint if the file name matches the lint's name.
+        let replacements;
+        let replacements = if lint.module == old_name
+            && try_rename_file(
+                Path::new(&format!("clippy_lints/src/{}.rs", old_name)),
+                Path::new(&format!("clippy_lints/src/{}.rs", new_name)),
+            ) {
+            // Edit the module name in the lint list. Note there could be multiple lints.
+            for lint in lints.iter_mut().filter(|l| l.module == old_name) {
+                lint.module = new_name.into();
+            }
+            replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)];
+            replacements.as_slice()
+        } else if !lint.module.contains("::")
+            // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs`
+            && try_rename_file(
+                Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, old_name)),
+                Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, new_name)),
+            )
+        {
+            // Edit the module name in the lint list. Note there could be multiple lints, or none.
+            let renamed_mod = format!("{}::{}", lint.module, old_name);
+            for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) {
+                lint.module = format!("{}::{}", lint.module, new_name);
+            }
+            replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)];
+            replacements.as_slice()
+        } else {
+            replacements = [(&*old_name_upper, &*new_name_upper), ("", "")];
+            &replacements[0..1]
+        };
+
+        // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being
+        // renamed.
+        for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("renamed_lints.rs")) {
+            rewrite_file(file.path(), |s| replace_ident_like(s, replacements));
+        }
+
+        generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
+        println!("{} has been successfully renamed", old_name);
+    }
+
+    println!("note: `cargo uitest` still needs to be run to update the test results");
+}
+
+/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there
+/// were no replacements.
+fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option<String> {
+    fn is_ident_char(c: u8) -> bool {
+        matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_')
+    }
+
+    let searcher = AhoCorasickBuilder::new()
+        .dfa(true)
+        .match_kind(aho_corasick::MatchKind::LeftmostLongest)
+        .build_with_size::<u16, _, _>(replacements.iter().map(|&(x, _)| x.as_bytes()))
+        .unwrap();
+
+    let mut result = String::with_capacity(contents.len() + 1024);
+    let mut pos = 0;
+    let mut edited = false;
+    for m in searcher.find_iter(contents) {
+        let (old, new) = replacements[m.pattern()];
+        result.push_str(&contents[pos..m.start()]);
+        result.push_str(
+            if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0))
+                && !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0))
+            {
+                edited = true;
+                new
+            } else {
+                old
+            },
+        );
+        pos = m.end();
+    }
+    result.push_str(&contents[pos..]);
+    edited.then(|| result)
+}
+
 fn round_to_fifty(count: usize) -> usize {
     count / 50 * 50
 }
@@ -210,6 +428,19 @@ fn new(name: &str, reason: &str) -> Self {
     }
 }
 
+struct RenamedLint {
+    old_name: String,
+    new_name: String,
+}
+impl RenamedLint {
+    fn new(old_name: &str, new_name: &str) -> Self {
+        Self {
+            old_name: remove_line_splices(old_name),
+            new_name: remove_line_splices(new_name),
+        }
+    }
+}
+
 /// Generates the code for registering a group
 fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lint>) -> String {
     let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect();
@@ -217,12 +448,13 @@ fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lin
 
     let mut output = GENERATED_FILE_COMMENT.to_string();
 
-    output.push_str(&format!(
-        "store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![\n",
+    let _ = writeln!(
+        output,
+        "store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![",
         group_name
-    ));
+    );
     for (module, name) in details {
-        output.push_str(&format!("    LintId::of({}::{}),\n", module, name));
+        let _ = writeln!(output, "    LintId::of({}::{}),", module, name);
     }
     output.push_str("])\n");
 
@@ -235,7 +467,8 @@ fn gen_deprecated(lints: &[DeprecatedLint]) -> String {
     let mut output = GENERATED_FILE_COMMENT.to_string();
     output.push_str("{\n");
     for lint in lints {
-        output.push_str(&format!(
+        let _ = write!(
+            output,
             concat!(
                 "    store.register_removed(\n",
                 "        \"clippy::{}\",\n",
@@ -243,7 +476,7 @@ fn gen_deprecated(lints: &[DeprecatedLint]) -> String {
                 "    );\n"
             ),
             lint.name, lint.reason,
-        ));
+        );
     }
     output.push_str("}\n");
 
@@ -269,25 +502,62 @@ fn gen_register_lint_list<'a>(
         if !is_public {
             output.push_str("    #[cfg(feature = \"internal\")]\n");
         }
-        output.push_str(&format!("    {}::{},\n", module_name, lint_name));
+        let _ = writeln!(output, "    {}::{},", module_name, lint_name);
     }
     output.push_str("])\n");
 
     output
 }
 
+fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String {
+    let mut res: String = GENERATED_FILE_COMMENT.into();
+    for lint in lints {
+        writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap();
+    }
+    res.push_str("\nfn main() {}\n");
+    res
+}
+
+fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String {
+    let mut seen_lints = HashSet::new();
+    let mut res: String = GENERATED_FILE_COMMENT.into();
+    res.push_str("// run-rustfix\n\n");
+    for lint in lints {
+        if seen_lints.insert(&lint.new_name) {
+            writeln!(res, "#![allow({})]", lint.new_name).unwrap();
+        }
+    }
+    seen_lints.clear();
+    for lint in lints {
+        if seen_lints.insert(&lint.old_name) {
+            writeln!(res, "#![warn({})]", lint.old_name).unwrap();
+        }
+    }
+    res.push_str("\nfn main() {}\n");
+    res
+}
+
+fn gen_renamed_lints_list(lints: &[RenamedLint]) -> String {
+    const HEADER: &str = "\
+        // This file is managed by `cargo dev rename_lint`. Prefer using that when possible.\n\n\
+        #[rustfmt::skip]\n\
+        pub static RENAMED_LINTS: &[(&str, &str)] = &[\n";
+
+    let mut res = String::from(HEADER);
+    for lint in lints {
+        writeln!(res, "    (\"{}\", \"{}\"),", lint.old_name, lint.new_name).unwrap();
+    }
+    res.push_str("];\n");
+    res
+}
+
 /// Gathers all lints defined in `clippy_lints/src`
-fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>) {
+fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) {
     let mut lints = Vec::with_capacity(1000);
     let mut deprecated_lints = Vec::with_capacity(50);
-    let root_path = clippy_project_root().join("clippy_lints/src");
+    let mut renamed_lints = Vec::with_capacity(50);
 
-    for (rel_path, file) in WalkDir::new(&root_path)
-        .into_iter()
-        .map(Result::unwrap)
-        .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
-        .map(|f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f))
-    {
+    for (rel_path, file) in clippy_lints_src_files() {
         let path = file.path();
         let contents =
             fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e));
@@ -305,13 +575,21 @@ fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>) {
             module.strip_suffix(".rs").unwrap_or(&module)
         };
 
-        if module == "deprecated_lints" {
-            parse_deprecated_contents(&contents, &mut deprecated_lints);
-        } else {
-            parse_contents(&contents, module, &mut lints);
+        match module {
+            "deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints),
+            "renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints),
+            _ => parse_contents(&contents, module, &mut lints),
         }
     }
-    (lints, deprecated_lints)
+    (lints, deprecated_lints, renamed_lints)
+}
+
+fn clippy_lints_src_files() -> impl Iterator<Item = (PathBuf, DirEntry)> {
+    let root_path = clippy_project_root().join("clippy_lints/src");
+    let iter = WalkDir::new(&root_path).into_iter();
+    iter.map(Result::unwrap)
+        .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
+        .map(move |f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f))
 }
 
 macro_rules! match_tokens {
@@ -394,6 +672,25 @@ fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) {
     }
 }
 
+fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) {
+    for line in contents.lines() {
+        let mut offset = 0usize;
+        let mut iter = tokenize(line).map(|t| {
+            let range = offset..offset + t.len;
+            offset = range.end;
+            (t.kind, &line[range])
+        });
+        let (old_name, new_name) = match_tokens!(
+            iter,
+            // ("old_name",
+            Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(old_name) Comma
+            // "new_name"),
+            Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma
+        );
+        lints.push(RenamedLint::new(old_name, new_name));
+    }
+}
+
 /// Removes the line splices and surrounding quotes from a string literal
 fn remove_line_splices(s: &str) -> String {
     let s = s
@@ -462,6 +759,52 @@ fn replace_region_in_text<'a>(
     Ok(res)
 }
 
+fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
+    match fs::OpenOptions::new().create_new(true).write(true).open(new_name) {
+        Ok(file) => drop(file),
+        Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false,
+        Err(e) => panic_file(e, new_name, "create"),
+    };
+    match fs::rename(old_name, new_name) {
+        Ok(()) => true,
+        Err(e) => {
+            drop(fs::remove_file(new_name));
+            if e.kind() == io::ErrorKind::NotFound {
+                false
+            } else {
+                panic_file(e, old_name, "rename");
+            }
+        },
+    }
+}
+
+#[allow(clippy::needless_pass_by_value)]
+fn panic_file(error: io::Error, name: &Path, action: &str) -> ! {
+    panic!("failed to {} file `{}`: {}", action, name.display(), error)
+}
+
+fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option<String>) {
+    let mut file = fs::OpenOptions::new()
+        .write(true)
+        .read(true)
+        .open(path)
+        .unwrap_or_else(|e| panic_file(e, path, "open"));
+    let mut buf = String::new();
+    file.read_to_string(&mut buf)
+        .unwrap_or_else(|e| panic_file(e, path, "read"));
+    if let Some(new_contents) = f(&buf) {
+        file.rewind().unwrap_or_else(|e| panic_file(e, path, "write"));
+        file.write_all(new_contents.as_bytes())
+            .unwrap_or_else(|e| panic_file(e, path, "write"));
+        file.set_len(new_contents.len() as u64)
+            .unwrap_or_else(|e| panic_file(e, path, "write"));
+    }
+}
+
+fn write_file(path: &Path, contents: &str) {
+    fs::write(path, contents).unwrap_or_else(|e| panic_file(e, path, "write"));
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
index d0b03c0a9c276e644e89c6bfa22499cfdce7e052..8b0e11cb802eeae0996551e3c588f45688d096bc 100644 (file)
@@ -6,7 +6,7 @@
 use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
 use clippy_utils::{extract_msrv_attr, meets_msrv};
 use if_chain::if_chain;
-use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
+use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MacArgs, MacArgsEq, MetaItemKind, NestedMetaItem};
 use rustc_errors::Applicability;
 use rustc_hir::{
     Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
@@ -335,9 +335,6 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
                     }
                     if let Some(lint_list) = &attr.meta_item_list() {
                         if attr.ident().map_or(false, |ident| is_lint_level(ident.name)) {
-                            // permit `unused_imports`, `deprecated`, `unreachable_pub`,
-                            // `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items
-                            // and `unused_imports` for `extern crate` items with `macro_use`
                             for lint in lint_list {
                                 match item.kind {
                                     ItemKind::Use(..) => {
@@ -345,10 +342,12 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
                                             || is_word(lint, sym::deprecated)
                                             || is_word(lint, sym!(unreachable_pub))
                                             || is_word(lint, sym!(unused))
-                                            || extract_clippy_lint(lint)
-                                                .map_or(false, |s| s.as_str() == "wildcard_imports")
-                                            || extract_clippy_lint(lint)
-                                                .map_or(false, |s| s.as_str() == "enum_glob_use")
+                                            || extract_clippy_lint(lint).map_or(false, |s| {
+                                                matches!(
+                                                    s.as_str(),
+                                                    "wildcard_imports" | "enum_glob_use" | "redundant_pub_crate",
+                                                )
+                                            })
                                         {
                                             return;
                                         }
@@ -594,6 +593,10 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It
         };
 
         if attr.style == AttrStyle::Outer {
+            if let MacArgs::Eq(_, MacArgsEq::Ast(expr)) = &attr_item.args
+                && !matches!(expr.kind, rustc_ast::ExprKind::Lit(..)) {
+                return;
+            }
             if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) {
                 return;
             }
index 4592ca7274888dd97b13552ea01ac9c75dd8bf50..5b7c4591504e1cbaa3710a1cad5b684b4f13efbe 100644 (file)
@@ -1,12 +1,15 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::{match_def_path, paths};
+use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def_id::DefId;
-use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind};
+use rustc_hir::{def::Res, AsyncGeneratorKind, Body, BodyId, GeneratorKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::GeneratorInteriorTypeCause;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::Span;
 
+use crate::utils::conf::DisallowedType;
+
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for calls to await while holding a non-async-aware MutexGuard.
     "inside an async function, holding a `RefCell` ref while calling `await`"
 }
 
-declare_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF]);
+declare_clippy_lint! {
+    /// ### What it does
+    /// Allows users to configure types which should not be held across `await`
+    /// suspension points.
+    ///
+    /// ### Why is this bad?
+    /// There are some types which are perfectly "safe" to be used concurrently
+    /// from a memory access perspective but will cause bugs at runtime if they
+    /// are held in such a way.
+    ///
+    /// ### Known problems
+    ///
+    /// ### Example
+    ///
+    /// ```toml
+    /// await-holding-invalid-types = [
+    ///   # You can specify a type name
+    ///   "CustomLockType",
+    ///   # You can (optionally) specify a reason
+    ///   { path = "OtherCustomLockType", reason = "Relies on a thread local" }
+    /// ]
+    /// ```
+    ///
+    /// ```rust
+    /// # async fn baz() {}
+    /// struct CustomLockType;
+    /// struct OtherCustomLockType;
+    /// async fn foo() {
+    ///   let _x = CustomLockType;
+    ///   let _y = OtherCustomLockType;
+    ///   baz().await; // Lint violation
+    /// }
+    /// ```
+    #[clippy::version = "1.49.0"]
+    pub AWAIT_HOLDING_INVALID_TYPE,
+    suspicious,
+    "holding a type across an await point which is not allowed to be held as per the configuration"
+}
+
+impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]);
+
+#[derive(Debug)]
+pub struct AwaitHolding {
+    conf_invalid_types: Vec<DisallowedType>,
+    def_ids: FxHashMap<DefId, DisallowedType>,
+}
+
+impl AwaitHolding {
+    pub(crate) fn new(conf_invalid_types: Vec<DisallowedType>) -> Self {
+        Self {
+            conf_invalid_types,
+            def_ids: FxHashMap::default(),
+        }
+    }
+}
 
 impl LateLintPass<'_> for AwaitHolding {
+    fn check_crate(&mut self, cx: &LateContext<'_>) {
+        for conf in &self.conf_invalid_types {
+            let path = match conf {
+                DisallowedType::Simple(path) | DisallowedType::WithReason { path, .. } => path,
+            };
+            let segs: Vec<_> = path.split("::").collect();
+            if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) {
+                self.def_ids.insert(id, conf.clone());
+            }
+        }
+    }
+
     fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
         use AsyncGeneratorKind::{Block, Closure, Fn};
         if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind {
@@ -137,7 +206,7 @@ fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
                 hir_id: body.value.hir_id,
             };
             let typeck_results = cx.tcx.typeck_body(body_id);
-            check_interior_types(
+            self.check_interior_types(
                 cx,
                 typeck_results.generator_interior_types.as_ref().skip_binder(),
                 body.value.span,
@@ -146,46 +215,68 @@ fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
     }
 }
 
-fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
-    for ty_cause in ty_causes {
-        if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
-            if is_mutex_guard(cx, adt.did()) {
-                span_lint_and_then(
-                    cx,
-                    AWAIT_HOLDING_LOCK,
-                    ty_cause.span,
-                    "this `MutexGuard` is held across an `await` point",
-                    |diag| {
-                        diag.help(
-                            "consider using an async-aware `Mutex` type or ensuring the \
+impl AwaitHolding {
+    fn check_interior_types(&self, cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
+        for ty_cause in ty_causes {
+            if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
+                if is_mutex_guard(cx, adt.did()) {
+                    span_lint_and_then(
+                        cx,
+                        AWAIT_HOLDING_LOCK,
+                        ty_cause.span,
+                        "this `MutexGuard` is held across an `await` point",
+                        |diag| {
+                            diag.help(
+                                "consider using an async-aware `Mutex` type or ensuring the \
                                 `MutexGuard` is dropped before calling await",
-                        );
-                        diag.span_note(
-                            ty_cause.scope_span.unwrap_or(span),
-                            "these are all the `await` points this lock is held through",
-                        );
-                    },
-                );
-            }
-            if is_refcell_ref(cx, adt.did()) {
-                span_lint_and_then(
-                    cx,
-                    AWAIT_HOLDING_REFCELL_REF,
-                    ty_cause.span,
-                    "this `RefCell` reference is held across an `await` point",
-                    |diag| {
-                        diag.help("ensure the reference is dropped before calling `await`");
-                        diag.span_note(
-                            ty_cause.scope_span.unwrap_or(span),
-                            "these are all the `await` points this reference is held through",
-                        );
-                    },
-                );
+                            );
+                            diag.span_note(
+                                ty_cause.scope_span.unwrap_or(span),
+                                "these are all the `await` points this lock is held through",
+                            );
+                        },
+                    );
+                } else if is_refcell_ref(cx, adt.did()) {
+                    span_lint_and_then(
+                        cx,
+                        AWAIT_HOLDING_REFCELL_REF,
+                        ty_cause.span,
+                        "this `RefCell` reference is held across an `await` point",
+                        |diag| {
+                            diag.help("ensure the reference is dropped before calling `await`");
+                            diag.span_note(
+                                ty_cause.scope_span.unwrap_or(span),
+                                "these are all the `await` points this reference is held through",
+                            );
+                        },
+                    );
+                } else if let Some(disallowed) = self.def_ids.get(&adt.did()) {
+                    emit_invalid_type(cx, ty_cause.span, disallowed);
+                }
             }
         }
     }
 }
 
+fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedType) {
+    let (type_name, reason) = match disallowed {
+        DisallowedType::Simple(path) => (path, &None),
+        DisallowedType::WithReason { path, reason } => (path, reason),
+    };
+
+    span_lint_and_then(
+        cx,
+        AWAIT_HOLDING_INVALID_TYPE,
+        span,
+        &format!("`{type_name}` may not be held across an `await` point per `clippy.toml`",),
+        |diag| {
+            if let Some(reason) = reason {
+                diag.note(reason.clone());
+            }
+        },
+    );
+}
+
 fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
     match_def_path(cx, def_id, &paths::MUTEX_GUARD)
         || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD)
diff --git a/src/tools/clippy/clippy_lints/src/bytes_count_to_len.rs b/src/tools/clippy/clippy_lints/src/bytes_count_to_len.rs
new file mode 100644 (file)
index 0000000..d70dbf5
--- /dev/null
@@ -0,0 +1,70 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{match_def_path, paths};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// It checks for `str::bytes().count()` and suggests replacing it with
+    /// `str::len()`.
+    ///
+    /// ### Why is this bad?
+    /// `str::bytes().count()` is longer and may not be as performant as using
+    /// `str::len()`.
+    ///
+    /// ### Example
+    /// ```rust
+    /// "hello".bytes().count();
+    /// String::from("hello").bytes().count();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// "hello".len();
+    /// String::from("hello").len();
+    /// ```
+    #[clippy::version = "1.62.0"]
+    pub BYTES_COUNT_TO_LEN,
+    complexity,
+    "Using `bytes().count()` when `len()` performs the same functionality"
+}
+
+declare_lint_pass!(BytesCountToLen => [BYTES_COUNT_TO_LEN]);
+
+impl<'tcx> LateLintPass<'tcx> for BytesCountToLen {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+        if_chain! {
+            if let hir::ExprKind::MethodCall(_, expr_args, _) = &expr.kind;
+            if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+            if match_def_path(cx, expr_def_id, &paths::ITER_COUNT);
+
+            if let [bytes_expr] = &**expr_args;
+            if let hir::ExprKind::MethodCall(_, bytes_args, _) = &bytes_expr.kind;
+            if let Some(bytes_def_id) = cx.typeck_results().type_dependent_def_id(bytes_expr.hir_id);
+            if match_def_path(cx, bytes_def_id, &paths::STR_BYTES);
+
+            if let [str_expr] = &**bytes_args;
+            let ty = cx.typeck_results().expr_ty(str_expr).peel_refs();
+
+            if is_type_diagnostic_item(cx, ty, sym::String) || ty.kind() == &ty::Str;
+            then {
+                let mut applicability = Applicability::MachineApplicable;
+                span_lint_and_sugg(
+                    cx,
+                    BYTES_COUNT_TO_LEN,
+                    expr.span,
+                    "using long and hard to read `.bytes().count()`",
+                    "consider calling `.len()` instead",
+                    format!("{}.len()", snippet_with_applicability(cx, str_expr.span, "..", &mut applicability)),
+                    applicability
+                );
+            }
+        };
+    }
+}
index a8f9c189adec88672e722c00c265425ea4acec71..7af200708ff0326533b354b5d9801515261e6d0c 100644 (file)
@@ -42,8 +42,8 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &
         if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind;
         if (2..=6).contains(&ext_literal.as_str().len());
         if ext_literal.as_str().starts_with('.');
-        if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_digit(10))
-            || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_digit(10));
+        if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
+            || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
         then {
             let mut ty = ctx.typeck_results().expr_ty(obj);
             ty = match ty.kind() {
index 421bd6f53f71b4a78f37788f2a16b61301c76a44..64f87c80f8d147a5e3e21aed776582434d465fee 100644 (file)
@@ -29,21 +29,19 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
         ExprKind::Block(block, _) => block.expr.map_or(nbits, |e| apply_reductions(cx, nbits, e, signed)),
         ExprKind::Binary(op, left, right) => match op.node {
             BinOpKind::Div => {
-                apply_reductions(cx, nbits, left, signed)
-                    - (if signed {
-                        0 // let's be conservative here
-                    } else {
-                        // by dividing by 1, we remove 0 bits, etc.
-                        get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
-                    })
+                apply_reductions(cx, nbits, left, signed).saturating_sub(if signed {
+                    // let's be conservative here
+                    0
+                } else {
+                    // by dividing by 1, we remove 0 bits, etc.
+                    get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
+                })
             },
             BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right)
                 .unwrap_or(u64::max_value())
                 .min(apply_reductions(cx, nbits, left, signed)),
-            BinOpKind::Shr => {
-                apply_reductions(cx, nbits, left, signed)
-                    - constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))
-            },
+            BinOpKind::Shr => apply_reductions(cx, nbits, left, signed)
+                .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))),
             _ => nbits,
         },
         ExprKind::MethodCall(method, [left, right], _) => {
index 3608c1654d5c71196b209cc0c1c47617a16556b8..2238668abca71d464e9f8f0be73600d1b95140a2 100644 (file)
@@ -1,4 +1,4 @@
-use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source::snippet_opt};
+use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source};
 use if_chain::if_chain;
 use rustc_ast::Mutability;
 use rustc_hir::{Expr, ExprKind, Node};
@@ -8,32 +8,7 @@
 
 use super::CAST_SLICE_DIFFERENT_SIZES;
 
-fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    let map = cx.tcx.hir();
-    if_chain! {
-        if let Some(parent_id) = map.find_parent_node(expr.hir_id);
-        if let Some(parent) = map.find(parent_id);
-        then {
-            let expr = match parent {
-                Node::Block(block) => {
-                    if let Some(parent_expr) = block.expr {
-                        parent_expr
-                    } else {
-                        return false;
-                    }
-                },
-                Node::Expr(expr) => expr,
-                _ => return false,
-            };
-
-            matches!(expr.kind, ExprKind::Cast(..))
-        } else {
-            false
-        }
-    }
-}
-
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVersion>) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Option<RustcVersion>) {
     // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist
     if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) {
         return;
@@ -45,8 +20,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVe
         return;
     }
 
-    if let Some((from_slice_ty, to_slice_ty)) = expr_cast_chain_tys(cx, expr) {
-        if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(from_slice_ty.ty), cx.layout_of(to_slice_ty.ty)) {
+    if let Some(CastChainInfo {
+        left_cast,
+        start_ty,
+        end_ty,
+    }) = expr_cast_chain_tys(cx, expr)
+    {
+        if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) {
             let from_size = from_layout.size.bytes();
             let to_size = to_layout.size.bytes();
             if from_size != to_size && from_size != 0 && to_size != 0 {
@@ -56,21 +36,20 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVe
                     expr.span,
                     &format!(
                         "casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count",
-                        from_slice_ty, from_size, to_slice_ty, to_size,
+                        start_ty.ty, from_size, end_ty.ty, to_size,
                     ),
                     |diag| {
-                        let cast_expr = match expr.kind {
-                            ExprKind::Cast(cast_expr, ..) => cast_expr,
-                            _ => unreachable!("expr should be a cast as checked by expr_cast_chain_tys"),
-                        };
-                        let ptr_snippet = snippet_opt(cx, cast_expr.span).unwrap();
+                        let ptr_snippet = source::snippet(cx, left_cast.span, "..");
 
-                        let (mutbl_fn_str, mutbl_ptr_str) = match to_slice_ty.mutbl {
+                        let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl {
                             Mutability::Mut => ("_mut", "mut"),
                             Mutability::Not => ("", "const"),
                         };
                         let sugg = format!(
-                            "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {to_slice_ty}, ..)"
+                            "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)",
+                            // get just the ty from the TypeAndMut so that the printed type isn't something like `mut
+                            // T`, extract just the `T`
+                            end_ty.ty
                         );
 
                         diag.span_suggestion(
@@ -86,6 +65,31 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVe
     }
 }
 
+fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    let map = cx.tcx.hir();
+    if_chain! {
+        if let Some(parent_id) = map.find_parent_node(expr.hir_id);
+        if let Some(parent) = map.find(parent_id);
+        then {
+            let expr = match parent {
+                Node::Block(block) => {
+                    if let Some(parent_expr) = block.expr {
+                        parent_expr
+                    } else {
+                        return false;
+                    }
+                },
+                Node::Expr(expr) => expr,
+                _ => return false,
+            };
+
+            matches!(expr.kind, ExprKind::Cast(..))
+        } else {
+            false
+        }
+    }
+}
+
 /// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if
 /// the type is one of those slices
 fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option<TypeAndMut<'_>> {
@@ -98,18 +102,40 @@ fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option<TypeAndMut<'_>> {
     }
 }
 
-/// Returns the pair (original ptr T, final ptr U) if the expression is composed of casts
+struct CastChainInfo<'tcx> {
+    /// The left most part of the cast chain, or in other words, the first cast in the chain
+    /// Used for diagnostics
+    left_cast: &'tcx Expr<'tcx>,
+    /// The starting type of the cast chain
+    start_ty: TypeAndMut<'tcx>,
+    /// The final type of the cast chain
+    end_ty: TypeAndMut<'tcx>,
+}
+
+/// Returns a `CastChainInfo` with the left-most cast in the chain and the original ptr T and final
+/// ptr U if the expression is composed of casts.
 /// Returns None if the expr is not a Cast
-fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<(TypeAndMut<'tcx>, TypeAndMut<'tcx>)> {
+fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<CastChainInfo<'tcx>> {
     if let ExprKind::Cast(cast_expr, _cast_to_hir_ty) = expr.peel_blocks().kind {
         let cast_to = cx.typeck_results().expr_ty(expr);
         let to_slice_ty = get_raw_slice_ty_mut(cast_to)?;
-        if let Some((inner_from_ty, _inner_to_ty)) = expr_cast_chain_tys(cx, cast_expr) {
-            Some((inner_from_ty, to_slice_ty))
+
+        // If the expression that makes up the source of this cast is itself a cast, recursively
+        // call `expr_cast_chain_tys` and update the end type with the final tartet type.
+        // Otherwise, this cast is not immediately nested, just construct the info for this cast
+        if let Some(prev_info) = expr_cast_chain_tys(cx, cast_expr) {
+            Some(CastChainInfo {
+                end_ty: to_slice_ty,
+                ..prev_info
+            })
         } else {
             let cast_from = cx.typeck_results().expr_ty(cast_expr);
             let from_slice_ty = get_raw_slice_ty_mut(cast_from)?;
-            Some((from_slice_ty, to_slice_ty))
+            Some(CastChainInfo {
+                left_cast: cast_expr,
+                start_ty: from_slice_ty,
+                end_ty: to_slice_ty,
+            })
         }
     } else {
         None
index 55c1f085657bb1842eafd7c52ff5e75a5b68321c..b58252dcb9424b90339fc2cb86f5159f8991a181 100644 (file)
     ///
     /// ### Why is this bad?
     /// Casting a function pointer to an integer can have surprising results and can occur
-    /// accidentally if parantheses are omitted from a function call. If you aren't doing anything
+    /// accidentally if parentheses are omitted from a function call. If you aren't doing anything
     /// low-level with function pointers then you can opt-out of casting functions to integers in
     /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
     /// pointer casts in your code.
     /// let y: u32 = x.abs() as u32;
     /// ```
     /// Use instead:
+    /// ```rust
     /// let x: i32 = -42;
     /// let y: u32 = x.unsigned_abs();
     /// ```
index eae2723a7dac1f60ea6e1282a8385e8ae07166ec..3227e6e86af4e3021c3b97c28655437536f12f2c 100644 (file)
 //! This lint is **warn** by default
 
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::{snippet_block, snippet_block_with_applicability};
+use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability};
 use clippy_utils::sugg::Sugg;
 use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -102,7 +103,7 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
 fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
     if let ast::ExprKind::If(check, then, else_) = &expr.kind {
         if let Some(else_) = else_ {
-            check_collapsible_maybe_if_let(cx, else_);
+            check_collapsible_maybe_if_let(cx, then.span, else_);
         } else if let ast::ExprKind::Let(..) = check.kind {
             // Prevent triggering on `if let a = b { if c { .. } }`.
         } else {
@@ -119,7 +120,7 @@ fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool {
     trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*")
 }
 
-fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
+fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) {
     if_chain! {
         if let ast::ExprKind::Block(ref block, _) = else_.kind;
         if !block_starts_with_comment(cx, block);
@@ -128,6 +129,11 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
         if !else_.span.from_expansion();
         if let ast::ExprKind::If(..) = else_.kind;
         then {
+            // Prevent "elseif"
+            // Check that the "else" is followed by whitespace
+            let up_to_else = then_span.between(block.span);
+            let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { !c.is_whitespace() } else { false };
+
             let mut applicability = Applicability::MachineApplicable;
             span_lint_and_sugg(
                 cx,
@@ -135,7 +141,11 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
                 block.span,
                 "this `else { if .. }` block can be collapsed",
                 "collapse nested if block",
-                snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(),
+                format!(
+                    "{}{}",
+                    if requires_space { " " } else { "" },
+                    snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability)
+                ),
                 applicability,
             );
         }
index 503cef76775e4c8a56fa5479f8efe0066c2e09d3..b3fd8af4730dc16d6ee1e6102e040d2e9da38217 100644 (file)
@@ -240,7 +240,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
                     lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span);
                 }
             },
-            hir::ItemKind::Impl(ref impl_) => {
+            hir::ItemKind::Impl(impl_) => {
                 self.in_trait_impl = impl_.of_trait.is_some();
             },
             hir::ItemKind::Trait(_, unsafety, ..) => {
@@ -621,10 +621,8 @@ fn has_needless_main(code: String, edition: Edition) -> bool {
                 let filename = FileName::anon_source_code(&code);
 
                 let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-                let fallback_bundle = rustc_errors::fallback_fluent_bundle(
-                    rustc_errors::DEFAULT_LOCALE_RESOURCES,
-                    false
-                );
+                let fallback_bundle =
+                    rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
                 let emitter = EmitterWriter::new(
                     Box::new(io::sink()),
                     None,
@@ -741,7 +739,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
     /// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok).
     /// Plurals are also excluded (`IDs` is ok).
     fn is_camel_case(s: &str) -> bool {
-        if s.starts_with(|c: char| c.is_digit(10)) {
+        if s.starts_with(|c: char| c.is_ascii_digit()) {
             return false;
         }
 
index 88c54828da834da3e94ae9d513a76102e1d1f0c9..25014bfa1a5b12595b41415a2010317ac7952143 100644 (file)
 const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \
                                    Forgetting a copy leaves the original intact";
 const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \
-                                 Dropping such a type only extends it's contained lifetimes";
+                                 Dropping such a type only extends its contained lifetimes";
 const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \
                                    Forgetting such a type is the same as dropping it";
 
diff --git a/src/tools/clippy/clippy_lints/src/empty_drop.rs b/src/tools/clippy/clippy_lints/src/empty_drop.rs
new file mode 100644 (file)
index 0000000..325ae23
--- /dev/null
@@ -0,0 +1,65 @@
+use clippy_utils::{diagnostics::span_lint_and_sugg, peel_blocks};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for empty `Drop` implementations.
+    ///
+    /// ### Why is this bad?
+    /// Empty `Drop` implementations have no effect when dropping an instance of the type. They are
+    /// most likely useless. However, an empty `Drop` implementation prevents a type from being
+    /// destructured, which might be the intention behind adding the implementation as a marker.
+    ///
+    /// ### Example
+    /// ```rust
+    /// struct S;
+    ///
+    /// impl Drop for S {
+    ///     fn drop(&mut self) {}
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// struct S;
+    /// ```
+    #[clippy::version = "1.61.0"]
+    pub EMPTY_DROP,
+    restriction,
+    "empty `Drop` implementations"
+}
+declare_lint_pass!(EmptyDrop => [EMPTY_DROP]);
+
+impl LateLintPass<'_> for EmptyDrop {
+    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
+        if_chain! {
+            if let ItemKind::Impl(Impl {
+                of_trait: Some(ref trait_ref),
+                items: [child],
+                ..
+            }) = item.kind;
+            if trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait();
+            if let impl_item_hir = child.id.hir_id();
+            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);
+            let func_expr = peel_blocks(func_expr);
+            if let ExprKind::Block(block, _) = func_expr.kind;
+            if block.stmts.is_empty() && block.expr.is_none();
+            then {
+                span_lint_and_sugg(
+                    cx,
+                    EMPTY_DROP,
+                    item.span,
+                    "empty drop implementation",
+                    "try removing this impl",
+                    String::new(),
+                    Applicability::MaybeIncorrect
+                );
+            }
+        }
+    }
+}
index fdeac8d82557fd518cb54143fe152246070bd2bb..8430e7b4c82713554b0107be84ef57d269c1a483 100644 (file)
@@ -66,7 +66,7 @@ fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Spa
     }
 
     // there might still be field declarations hidden from the AST
-    // (conditionaly compiled code using #[cfg(..)])
+    // (conditionally compiled code using #[cfg(..)])
 
     let Some(braces_span_str) = snippet_opt(cx, braces_span) else {
         return false;
index 845863bd209c6ef4cc9696b68341bf9fee537437..1b19868e4c70faf2d33f6a5911528f971b25672b 100644 (file)
@@ -3,7 +3,7 @@
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::local_used_after_expr;
-use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id};
+use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
@@ -103,6 +103,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         let closure_ty = cx.typeck_results().expr_ty(expr);
 
         if_chain!(
+            if !is_adjusted(cx, &body.value);
             if let ExprKind::Call(callee, args) = body.value.kind;
             if let ExprKind::Path(_) = callee.kind;
             if check_inputs(cx, body.params, args);
@@ -125,8 +126,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                         if_chain! {
                             if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
                             if substs.as_closure().kind() == ClosureKind::FnMut;
-                            if get_enclosing_loop_or_closure(cx.tcx, expr).is_some()
-                                || path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, callee));
+                            if path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr));
 
                             then {
                                 // Mutable closure is used after current expr; we cannot consume it.
@@ -145,6 +145,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         );
 
         if_chain!(
+            if !is_adjusted(cx, &body.value);
             if let ExprKind::MethodCall(path, args, _) = body.value.kind;
             if check_inputs(cx, body.params, args);
             let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
diff --git a/src/tools/clippy/clippy_lints/src/format_push_string.rs b/src/tools/clippy/clippy_lints/src/format_push_string.rs
new file mode 100644 (file)
index 0000000..ee15ae9
--- /dev/null
@@ -0,0 +1,77 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{match_def_path, paths, peel_hir_expr_refs};
+use rustc_hir::{BinOpKind, 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
+    /// Detects cases where the result of a `format!` call is
+    /// appended to an existing `String`.
+    ///
+    /// ### Why is this bad?
+    /// Introduces an extra, avoidable heap allocation.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let mut s = String::new();
+    /// s += &format!("0x{:X}", 1024);
+    /// s.push_str(&format!("0x{:X}", 1024));
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use std::fmt::Write as _; // import without risk of name clashing
+    ///
+    /// let mut s = String::new();
+    /// let _ = write!(s, "0x{:X}", 1024);
+    /// ```
+    #[clippy::version = "1.61.0"]
+    pub FORMAT_PUSH_STRING,
+    perf,
+    "`format!(..)` appended to existing `String`"
+}
+declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]);
+
+fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+    is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String)
+}
+fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+    if let Some(macro_def_id) = e.span.ctxt().outer_expn_data().macro_def_id {
+        cx.tcx.get_diagnostic_name(macro_def_id) == Some(sym::format_macro)
+    } else {
+        false
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for FormatPushString {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        let arg = match expr.kind {
+            ExprKind::MethodCall(_, [_, arg], _) => {
+                if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) &&
+                match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
+                    arg
+                } else {
+                    return;
+                }
+            }
+            ExprKind::AssignOp(op, left, arg)
+            if op.node == BinOpKind::Add && is_string(cx, left) => {
+                arg
+            },
+            _ => return,
+        };
+        let (arg, _) = peel_hir_expr_refs(arg);
+        if is_format(cx, arg) {
+            span_lint_and_help(
+                cx,
+                FORMAT_PUSH_STRING,
+                expr.span,
+                "`format!(..)` appended to existing `String`",
+                None,
+                "consider using `write!` to avoid the extra allocation",
+            );
+        }
+    }
+}
index 5462d913fb4414bf554e3c73d1bd5f0697493413..38e943d2eb8720cd98a0039e5dc3ac188c40d6b8 100644 (file)
@@ -20,7 +20,7 @@
 pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
     let attrs = cx.tcx.hir().attrs(item.hir_id());
     let attr = must_use_attr(attrs);
-    if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind {
+    if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind {
         let is_public = cx.access_levels.is_exported(item.def_id);
         let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
         if let Some(attr) = attr {
@@ -105,12 +105,7 @@ fn check_needless_must_use(
             fn_header_span,
             "this unit-returning function has a `#[must_use]` attribute",
             |diag| {
-                diag.span_suggestion(
-                    attr.span,
-                    "remove the attribute",
-                    "",
-                    Applicability::MachineApplicable,
-                );
+                diag.span_suggestion(attr.span, "remove the attribute", "", Applicability::MachineApplicable);
             },
         );
     } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
index 120fcb2619c7c66dd59dd58bdb3b7c1ea8b30170..2e63a1f920d64b39a69ad214ecd4cb6e03ada3a2 100644 (file)
@@ -14,7 +14,7 @@
 use super::RESULT_UNIT_ERR;
 
 pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
-    if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind {
+    if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind {
         let is_public = cx.access_levels.is_exported(item.def_id);
         let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
         if is_public {
index 4d6bef89bea7f09cbbfa8f618b4d26b871e371b6..40cc5cd4bcf9df4111d202591fde32e8fb4339e6 100644 (file)
@@ -1,3 +1,4 @@
+use clippy_utils::get_parent_expr;
 use clippy_utils::source::snippet;
 use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
     /// # let x = 1;
     /// x / 1 + 0 * 1 - 0 | 0;
     /// ```
+    ///
+    /// ### Known problems
+    /// False negatives: `f(0 + if b { 1 } else { 2 } + 3);` is reducible to
+    /// `f(if b { 1 } else { 2 } + 3);`. But the lint doesn't trigger for the code.
+    /// See [#8724](https://github.com/rust-lang/rust-clippy/issues/8724)
     #[clippy::version = "pre 1.29.0"]
     pub IDENTITY_OP,
     complexity,
 declare_lint_pass!(IdentityOp => [IDENTITY_OP]);
 
 impl<'tcx> LateLintPass<'tcx> for IdentityOp {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if e.span.from_expansion() {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if expr.span.from_expansion() {
             return;
         }
-        if let ExprKind::Binary(cmp, left, right) = e.kind {
-            if is_allowed(cx, cmp, left, right) {
-                return;
-            }
-            match cmp.node {
-                BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
-                    check(cx, left, 0, e.span, right.span);
-                    check(cx, right, 0, e.span, left.span);
-                },
-                BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => check(cx, right, 0, e.span, left.span),
-                BinOpKind::Mul => {
-                    check(cx, left, 1, e.span, right.span);
-                    check(cx, right, 1, e.span, left.span);
-                },
-                BinOpKind::Div => check(cx, right, 1, e.span, left.span),
-                BinOpKind::BitAnd => {
-                    check(cx, left, -1, e.span, right.span);
-                    check(cx, right, -1, e.span, left.span);
-                },
-                BinOpKind::Rem => check_remainder(cx, left, right, e.span, left.span),
-                _ => (),
+        if let ExprKind::Binary(cmp, left, right) = &expr.kind {
+            if !is_allowed(cx, *cmp, left, right) {
+                match cmp.node {
+                    BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
+                        if reducible_to_right(cx, expr, right) {
+                            check(cx, left, 0, expr.span, right.span);
+                        }
+                        check(cx, right, 0, expr.span, left.span);
+                    },
+                    BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
+                        check(cx, right, 0, expr.span, left.span);
+                    },
+                    BinOpKind::Mul => {
+                        if reducible_to_right(cx, expr, right) {
+                            check(cx, left, 1, expr.span, right.span);
+                        }
+                        check(cx, right, 1, expr.span, left.span);
+                    },
+                    BinOpKind::Div => check(cx, right, 1, expr.span, left.span),
+                    BinOpKind::BitAnd => {
+                        if reducible_to_right(cx, expr, right) {
+                            check(cx, left, -1, expr.span, right.span);
+                        }
+                        check(cx, right, -1, expr.span, left.span);
+                    },
+                    BinOpKind::Rem => {
+                        // Don't call reducible_to_right because N % N is always reducible to 1
+                        check_remainder(cx, left, right, expr.span, left.span);
+                    },
+                    _ => (),
+                }
             }
         }
     }
 }
 
+/// Checks if `left op ..right` can be actually reduced to `right`
+/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }`
+/// cannot be reduced to `if b { 1 } else { 2 } +  if b { 3 } else { 4 }`
+/// See #8724
+fn reducible_to_right(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> bool {
+    if let ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) = right.kind {
+        is_toplevel_binary(cx, binary)
+    } else {
+        true
+    }
+}
+
+fn is_toplevel_binary(cx: &LateContext<'_>, must_be_binary: &Expr<'_>) -> bool {
+    if let Some(parent) = get_parent_expr(cx, must_be_binary) && let ExprKind::Binary(..) = &parent.kind {
+        false
+    } else {
+        true
+    }
+}
+
 fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool {
     // This lint applies to integers
     !cx.typeck_results().expr_ty(left).peel_refs().is_integral()
index d5430a8c91750784b0033ccb226c9d352c632b60..feb1b1014b180838b74b771c0a852f6cf671497c 100644 (file)
@@ -117,7 +117,7 @@ fn suggestion<'tcx>(
         }
 
         match item.kind {
-            ItemKind::Impl(ref impl_) => {
+            ItemKind::Impl(impl_) => {
                 let mut vis = ImplicitHasherTypeVisitor::new(cx);
                 vis.visit_ty(impl_.self_ty);
 
@@ -155,7 +155,7 @@ fn suggestion<'tcx>(
                     );
                 }
             },
-            ItemKind::Fn(ref sig, ref generics, body_id) => {
+            ItemKind::Fn(ref sig, generics, body_id) => {
                 let body = cx.tcx.hir().body(body_id);
 
                 for ty in sig.decl.inputs {
index c8ec2f45137072539afbba571cb87541eb34bff7..14b22d2b50d054fdec8e0061666bf02d381bd518 100644 (file)
@@ -7,6 +7,7 @@
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::symbol::Symbol;
+use std::fmt::Write as _;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -89,7 +90,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
                 let mut fields_snippet = String::new();
                 let (last_ident, idents) = ordered_fields.split_last().unwrap();
                 for ident in idents {
-                    fields_snippet.push_str(&format!("{}, ", ident));
+                    let _ = write!(fields_snippet, "{}, ", ident);
                 }
                 fields_snippet.push_str(&last_ident.to_string());
 
index 6b62748ffef2e2812195961768e5d416197d6a26..8a84513b7792fdbf0f97db326feac7cc8f2c274b 100644 (file)
@@ -116,7 +116,7 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap<hir
             let bound_ty = cx.typeck_results().node_type(pat.hir_id);
             if let ty::Slice(inner_ty) | ty::Array(inner_ty, _) = bound_ty.peel_refs().kind() {
                 // The values need to use the `ref` keyword if they can't be copied.
-                // This will need to be adjusted if the lint want to support multable access in the future
+                // This will need to be adjusted if the lint want to support mutable access in the future
                 let src_is_ref = bound_ty.is_ref() && binding != hir::BindingAnnotation::Ref;
                 let needs_ref = !(src_is_ref || is_copy(cx, *inner_ty));
 
index 254d3f8a4d0f901a672585236c66e19819d963a5..6a031a627df946669e206ae77fb2255d21bf71e4 100644 (file)
@@ -119,7 +119,7 @@ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
 fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option<Span> {
     let id = cx.tcx.hir().local_def_id_to_hir_id(id);
     if let Node::Item(&Item {
-        kind: ItemKind::Impl(ref impl_item),
+        kind: ItemKind::Impl(impl_item),
         span,
         ..
     }) = cx.tcx.hir().get(id)
index 9284e002409920673763967bb375b5bbf1b1b90a..7e1548531f10cc9301c558d013dcd133c30dc330 100644 (file)
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
+use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -49,6 +50,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                 && fields
                     .iter()
                     .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit))
+                && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..))
             {
                 let expr_spans = fields
                     .iter()
diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs
new file mode 100644 (file)
index 0000000..8bef13c
--- /dev/null
@@ -0,0 +1,86 @@
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::is_lint_allowed;
+use clippy_utils::macros::root_macro_call_first_node;
+use rustc_ast::LitKind;
+use rustc_hir::Expr;
+use rustc_hir::ExprKind;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for the inclusion of large files via `include_bytes!()`
+    /// and `include_str!()`
+    ///
+    /// ### Why is this bad?
+    /// Including large files can increase the size of the binary
+    ///
+    /// ### Example
+    /// ```rust,ignore
+    /// let included_str = include_str!("very_large_file.txt");
+    /// let included_bytes = include_bytes!("very_large_file.txt");
+    /// ```
+    ///
+    /// Instead, you can load the file at runtime:
+    /// ```rust,ignore
+    /// use std::fs;
+    ///
+    /// let string = fs::read_to_string("very_large_file.txt")?;
+    /// let bytes = fs::read("very_large_file.txt")?;
+    /// ```
+    #[clippy::version = "1.62.0"]
+    pub LARGE_INCLUDE_FILE,
+    restriction,
+    "including a large file"
+}
+
+pub struct LargeIncludeFile {
+    max_file_size: u64,
+}
+
+impl LargeIncludeFile {
+    #[must_use]
+    pub fn new(max_file_size: u64) -> Self {
+        Self { max_file_size }
+    }
+}
+
+impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]);
+
+impl LateLintPass<'_> for LargeIncludeFile {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
+        if_chain! {
+            if let Some(macro_call) = root_macro_call_first_node(cx, expr);
+            if !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id);
+            if cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
+            || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id);
+            if let ExprKind::Lit(lit) = &expr.kind;
+            then {
+                let len = match &lit.node {
+                    // include_bytes
+                    LitKind::ByteStr(bstr) => bstr.len(),
+                    // include_str
+                    LitKind::Str(sym, _) => sym.as_str().len(),
+                    _ => return,
+                };
+
+                if len as u64 <= self.max_file_size {
+                    return;
+                }
+
+                span_lint_and_note(
+                    cx,
+                    LARGE_INCLUDE_FILE,
+                    expr.span,
+                    "attempted to include a large file",
+                    None,
+                    &format!(
+                        "the configuration allows a maximum size of {} bytes",
+                        self.max_file_size
+                    ),
+                );
+            }
+        }
+    }
+}
index 14ca93b5f3c14b3629a0168b421a3527797ffc8d..e68718f9fdf99165efdb6682b8e0ea4d5a2efe18 100644 (file)
@@ -14,6 +14,7 @@
     LintId::of(attrs::DEPRECATED_SEMVER),
     LintId::of(attrs::MISMATCHED_TARGET_OS),
     LintId::of(attrs::USELESS_ATTRIBUTE),
+    LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
     LintId::of(bit_mask::BAD_BIT_MASK),
@@ -23,6 +24,7 @@
     LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
     LintId::of(booleans::LOGIC_BUG),
     LintId::of(booleans::NONMINIMAL_BOOL),
+    LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
     LintId::of(casts::CAST_ABS_TO_UNSIGNED),
     LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
     LintId::of(casts::CAST_ENUM_TRUNCATION),
@@ -77,6 +79,7 @@
     LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
     LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
     LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
+    LintId::of(format_push_string::FORMAT_PUSH_STRING),
     LintId::of(formatting::POSSIBLE_MISSING_COMMA),
     LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
     LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
     LintId::of(methods::FLAT_MAP_IDENTITY),
     LintId::of(methods::INSPECT_FOR_EACH),
     LintId::of(methods::INTO_ITER_ON_REF),
+    LintId::of(methods::IS_DIGIT_ASCII_RADIX),
     LintId::of(methods::ITERATOR_STEP_BY_ZERO),
     LintId::of(methods::ITER_CLONED_COLLECT),
     LintId::of(methods::ITER_COUNT),
     LintId::of(methods::MAP_FLATTEN),
     LintId::of(methods::MAP_IDENTITY),
     LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
+    LintId::of(methods::NEEDLESS_OPTION_TAKE),
     LintId::of(methods::NEEDLESS_SPLITN),
     LintId::of(methods::NEW_RET_NO_SELF),
     LintId::of(methods::OK_EXPECT),
     LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
     LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
     LintId::of(octal_escapes::OCTAL_ESCAPES),
-    LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
     LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
     LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
     LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
     LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
     LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
     LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
-    LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
     LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
+    LintId::of(strings::TRIM_SPLIT_WHITESPACE),
     LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
     LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
     LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
     LintId::of(uninit_vec::UNINIT_VEC),
     LintId::of(unit_hash::UNIT_HASH),
     LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
+    LintId::of(unit_types::LET_UNIT_VALUE),
     LintId::of(unit_types::UNIT_ARG),
     LintId::of(unit_types::UNIT_CMP),
     LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
     LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
+    LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
     LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
     LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
     LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
index 10369a855ae6e007f43469e7c58e4f38e4368376..6f3c433af31a6287b9fce5dee51ad0daaf5f8fcf 100644 (file)
@@ -5,6 +5,7 @@
 store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![
     LintId::of(attrs::DEPRECATED_CFG_ATTR),
     LintId::of(booleans::NONMINIMAL_BOOL),
+    LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
     LintId::of(casts::CHAR_LIT_AS_U8),
     LintId::of(casts::UNNECESSARY_CAST),
     LintId::of(derivable_impls::DERIVABLE_IMPLS),
@@ -45,6 +46,7 @@
     LintId::of(methods::MAP_FLATTEN),
     LintId::of(methods::MAP_IDENTITY),
     LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
+    LintId::of(methods::NEEDLESS_OPTION_TAKE),
     LintId::of(methods::NEEDLESS_SPLITN),
     LintId::of(methods::OPTION_AS_REF_DEREF),
     LintId::of(methods::OPTION_FILTER_MAP),
@@ -66,7 +68,6 @@
     LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
     LintId::of(no_effect::NO_EFFECT),
     LintId::of(no_effect::UNNECESSARY_OPERATION),
-    LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
     LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
     LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
     LintId::of(precedence::PRECEDENCE),
index 532590aaa5a3d294d96dc5cf831318ee8a97c276..5768edc501884feee3e529445ff01f79188cf533 100644 (file)
@@ -52,6 +52,7 @@
     attrs::INLINE_ALWAYS,
     attrs::MISMATCHED_TARGET_OS,
     attrs::USELESS_ATTRIBUTE,
+    await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE,
     await_holding_invalid::AWAIT_HOLDING_LOCK,
     await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
     bit_mask::BAD_BIT_MASK,
@@ -64,6 +65,7 @@
     booleans::NONMINIMAL_BOOL,
     borrow_as_ptr::BORROW_AS_PTR,
     bytecount::NAIVE_BYTECOUNT,
+    bytes_count_to_len::BYTES_COUNT_TO_LEN,
     cargo::CARGO_COMMON_METADATA,
     cargo::MULTIPLE_CRATE_VERSIONS,
     cargo::NEGATIVE_FEATURE_NAMES,
     drop_forget_ref::UNDROPPED_MANUALLY_DROPS,
     duration_subsec::DURATION_SUBSEC,
     else_if_without_else::ELSE_IF_WITHOUT_ELSE,
+    empty_drop::EMPTY_DROP,
     empty_enum::EMPTY_ENUM,
     empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS,
     entry::MAP_ENTRY,
     format_args::TO_STRING_IN_FORMAT_ARGS,
     format_impl::PRINT_IN_FORMAT_IMPL,
     format_impl::RECURSIVE_FORMAT_IMPL,
+    format_push_string::FORMAT_PUSH_STRING,
     formatting::POSSIBLE_MISSING_COMMA,
     formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
     formatting::SUSPICIOUS_ELSE_FORMATTING,
     iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
     large_const_arrays::LARGE_CONST_ARRAYS,
     large_enum_variant::LARGE_ENUM_VARIANT,
+    large_include_file::LARGE_INCLUDE_FILE,
     large_stack_arrays::LARGE_STACK_ARRAYS,
     len_zero::COMPARISON_TO_EMPTY,
     len_zero::LEN_WITHOUT_IS_EMPTY,
     methods::INEFFICIENT_TO_STRING,
     methods::INSPECT_FOR_EACH,
     methods::INTO_ITER_ON_REF,
+    methods::IS_DIGIT_ASCII_RADIX,
     methods::ITERATOR_STEP_BY_ZERO,
     methods::ITER_CLONED_COLLECT,
     methods::ITER_COUNT,
     methods::MAP_IDENTITY,
     methods::MAP_UNWRAP_OR,
     methods::NEEDLESS_OPTION_AS_DEREF,
+    methods::NEEDLESS_OPTION_TAKE,
     methods::NEEDLESS_SPLITN,
     methods::NEW_RET_NO_SELF,
     methods::OK_EXPECT,
     ptr::PTR_ARG,
     ptr_eq::PTR_EQ,
     ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
+    pub_use::PUB_USE,
     question_mark::QUESTION_MARK,
     ranges::MANUAL_RANGE_CONTAINS,
     ranges::RANGE_MINUS_ONE,
     strings::STRING_SLICE,
     strings::STRING_TO_STRING,
     strings::STR_TO_STRING,
+    strings::TRIM_SPLIT_WHITESPACE,
     strlen_on_c_strings::STRLEN_ON_C_STRINGS,
     suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS,
     suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
     unit_types::UNIT_CMP,
     unnamed_address::FN_ADDRESS_COMPARISONS,
     unnamed_address::VTABLE_ADDRESS_COMPARISONS,
+    unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
     unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
     unnecessary_sort_by::UNNECESSARY_SORT_BY,
     unnecessary_wraps::UNNECESSARY_WRAPS,
index c2fc67afba517f4447d33df8bfc5d504015f6c77..ec187563b3f645a1c3c50d774cc8f2e26bf0d2ab 100644 (file)
@@ -20,6 +20,7 @@
     LintId::of(mutex_atomic::MUTEX_INTEGER),
     LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
     LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
+    LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
     LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
     LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
     LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
@@ -27,6 +28,8 @@
     LintId::of(strings::STRING_LIT_AS_BYTES),
     LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
     LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY),
+    LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
+    LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
     LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR),
     LintId::of(transmute::USELESS_TRANSMUTE),
     LintId::of(use_self::USE_SELF),
index eb6534cb8cae761433a0a979ff932f78d063f5a7..2ee2c6e3358cd030ffd32a1b8287fc379932e348 100644 (file)
     LintId::of(ref_option_ref::REF_OPTION_REF),
     LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
     LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
+    LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
     LintId::of(strings::STRING_ADD_ASSIGN),
-    LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
-    LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
     LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
     LintId::of(types::LINKEDLIST),
     LintId::of(types::OPTION_OPTION),
     LintId::of(unicode::UNICODE_NOT_NFC),
-    LintId::of(unit_types::LET_UNIT_VALUE),
     LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS),
     LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS),
     LintId::of(unused_async::UNUSED_ASYNC),
index f2f5c988d8f9056b557e05ac61e43287b01ef2d6..82431863e6cfd56cf3482b2f9f03c6d3ebbd003c 100644 (file)
@@ -7,6 +7,7 @@
     LintId::of(escape::BOXED_LOCAL),
     LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
     LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
+    LintId::of(format_push_string::FORMAT_PUSH_STRING),
     LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
     LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
     LintId::of(loops::MANUAL_MEMCPY),
@@ -23,7 +24,6 @@
     LintId::of(misc::CMP_OWNED),
     LintId::of(redundant_clone::REDUNDANT_CLONE),
     LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
-    LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
     LintId::of(types::BOX_COLLECTION),
     LintId::of(types::REDUNDANT_ALLOCATION),
     LintId::of(vec::USELESS_VEC),
index 4802dd877e99d12d90d85d124d164d034bad14ef..77ec6c83ba4b4d8b5d6691fb0391382582e14686 100644 (file)
@@ -16,6 +16,7 @@
     LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION),
     LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
     LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
+    LintId::of(empty_drop::EMPTY_DROP),
     LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS),
     LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
     LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
@@ -26,6 +27,7 @@
     LintId::of(indexing_slicing::INDEXING_SLICING),
     LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL),
     LintId::of(integer_division::INTEGER_DIVISION),
+    LintId::of(large_include_file::LARGE_INCLUDE_FILE),
     LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
     LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
     LintId::of(map_err_ignore::MAP_ERR_IGNORE),
@@ -53,6 +55,7 @@
     LintId::of(panic_unimplemented::UNIMPLEMENTED),
     LintId::of(panic_unimplemented::UNREACHABLE),
     LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
+    LintId::of(pub_use::PUB_USE),
     LintId::of(redundant_slicing::DEREF_BY_SLICING),
     LintId::of(same_name_method::SAME_NAME_METHOD),
     LintId::of(shadow::SHADOW_REUSE),
index 3114afac8863e69f574e172383fcc8f50414f50b..d183c0449cd5fd6fb7059de9c8076c5bdcb52f70 100644 (file)
@@ -61,6 +61,7 @@
     LintId::of(methods::CHARS_NEXT_CMP),
     LintId::of(methods::ERR_EXPECT),
     LintId::of(methods::INTO_ITER_ON_REF),
+    LintId::of(methods::IS_DIGIT_ASCII_RADIX),
     LintId::of(methods::ITER_CLONED_COLLECT),
     LintId::of(methods::ITER_NEXT_SLICE),
     LintId::of(methods::ITER_NTH_ZERO),
     LintId::of(returns::NEEDLESS_RETURN),
     LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
     LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
+    LintId::of(strings::TRIM_SPLIT_WHITESPACE),
     LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
     LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
+    LintId::of(unit_types::LET_UNIT_VALUE),
+    LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
     LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
     LintId::of(unused_unit::UNUSED_UNIT),
     LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
index 82f45b5fd58b9691e7c13360c9fe5821de283f4d..7a881bfe3991265ea34c021512e42a783e1783a3 100644 (file)
@@ -5,6 +5,7 @@
 store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![
     LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
     LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
+    LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
     LintId::of(casts::CAST_ABS_TO_UNSIGNED),
index c9b836f95808a623d02b567e5b5ca2f502aa8e33..3bb821a14829535e924d08c81fadab601d68ce53 100644 (file)
@@ -163,6 +163,8 @@ macro_rules! declare_clippy_lint {
 #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
 mod utils;
 
+mod renamed_lints;
+
 // begin lints modules, do not remove this comment, it’s used in `update_lints`
 mod absurd_extreme_comparisons;
 mod approx_const;
@@ -181,6 +183,7 @@ macro_rules! declare_clippy_lint {
 mod booleans;
 mod borrow_as_ptr;
 mod bytecount;
+mod bytes_count_to_len;
 mod cargo;
 mod case_sensitive_file_extension_comparisons;
 mod casts;
@@ -209,6 +212,7 @@ macro_rules! declare_clippy_lint {
 mod drop_forget_ref;
 mod duration_subsec;
 mod else_if_without_else;
+mod empty_drop;
 mod empty_enum;
 mod empty_structs_with_brackets;
 mod entry;
@@ -231,6 +235,7 @@ macro_rules! declare_clippy_lint {
 mod format;
 mod format_args;
 mod format_impl;
+mod format_push_string;
 mod formatting;
 mod from_over_into;
 mod from_str_radix_10;
@@ -259,6 +264,7 @@ macro_rules! declare_clippy_lint {
 mod iter_not_returning_iterator;
 mod large_const_arrays;
 mod large_enum_variant;
+mod large_include_file;
 mod large_stack_arrays;
 mod len_zero;
 mod let_if_seq;
@@ -336,6 +342,7 @@ macro_rules! declare_clippy_lint {
 mod ptr;
 mod ptr_eq;
 mod ptr_offset_with_cast;
+mod pub_use;
 mod question_mark;
 mod ranges;
 mod redundant_clone;
@@ -383,6 +390,7 @@ macro_rules! declare_clippy_lint {
 mod unit_return_expecting_ord;
 mod unit_types;
 mod unnamed_address;
+mod unnecessary_owned_empty_strings;
 mod unnecessary_self_imports;
 mod unnecessary_sort_by;
 mod unnecessary_wraps;
@@ -499,7 +507,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     {
         store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal));
         store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce));
-        store.register_late_pass(|| Box::new(utils::inspector::DeepCodeInspector));
         store.register_late_pass(|| Box::new(utils::internal_lints::CollapsibleCalls));
         store.register_late_pass(|| Box::new(utils::internal_lints::CompilerLintFunctions::new()));
         store.register_late_pass(|| Box::new(utils::internal_lints::IfChainStyle));
@@ -511,8 +518,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl));
     }
 
+    store.register_late_pass(|| Box::new(utils::dump_hir::DumpHir));
     store.register_late_pass(|| Box::new(utils::author::Author));
-    store.register_late_pass(|| Box::new(await_holding_invalid::AwaitHolding));
+    let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
+    store.register_late_pass(move || {
+        Box::new(await_holding_invalid::AwaitHolding::new(
+            await_holding_invalid_types.clone(),
+        ))
+    });
     store.register_late_pass(|| Box::new(serde_api::SerdeApi));
     let vec_box_size_threshold = conf.vec_box_size_threshold;
     let type_complexity_threshold = conf.type_complexity_threshold;
@@ -572,7 +585,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv)));
     store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv)));
     store.register_late_pass(move || Box::new(matches::Matches::new(msrv)));
-    store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustive::new(msrv)));
+    store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv)));
+    store.register_late_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv)));
     store.register_late_pass(move || Box::new(manual_strip::ManualStrip::new(msrv)));
     store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)));
     store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv)));
@@ -812,6 +826,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone())));
     store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
     store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
+    store.register_late_pass(|| Box::new(empty_drop::EmptyDrop));
     store.register_late_pass(|| Box::new(strings::StrToString));
     store.register_late_pass(|| Box::new(strings::StringToString));
     store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
@@ -868,6 +883,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     });
     store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
     store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
+    store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
+    store.register_early_pass(|| Box::new(pub_use::PubUse));
+    store.register_late_pass(|| Box::new(format_push_string::FormatPushString));
+    store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen));
+    let max_include_file_size = conf.max_include_file_size;
+    store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
+    store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
@@ -919,43 +941,9 @@ fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) {
 ///
 /// Used in `./src/driver.rs`.
 pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
-    // NOTE: when renaming a lint, add a corresponding test to tests/ui/rename.rs
-    ls.register_renamed("clippy::stutter", "clippy::module_name_repetitions");
-    ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default");
-    ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity");
-    ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes");
-    ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map");
-    ls.register_renamed("clippy::box_vec", "clippy::box_collection");
-    ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions");
-    ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions");
-    ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or");
-    ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or");
-    ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or");
-    ls.register_renamed("clippy::option_unwrap_used", "clippy::unwrap_used");
-    ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used");
-    ls.register_renamed("clippy::option_expect_used", "clippy::expect_used");
-    ls.register_renamed("clippy::result_expect_used", "clippy::expect_used");
-    ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles");
-    ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles");
-    ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
-    ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
-    ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
-    ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok");
-    ls.register_renamed("clippy::disallowed_type", "clippy::disallowed_types");
-    ls.register_renamed("clippy::disallowed_method", "clippy::disallowed_methods");
-    ls.register_renamed("clippy::ref_in_deref", "clippy::needless_borrow");
-    ls.register_renamed("clippy::to_string_in_display", "clippy::recursive_format_impl");
-
-    // uplifted lints
-    ls.register_renamed("clippy::invalid_ref", "invalid_value");
-    ls.register_renamed("clippy::into_iter_on_array", "array_into_iter");
-    ls.register_renamed("clippy::unused_label", "unused_labels");
-    ls.register_renamed("clippy::drop_bounds", "drop_bounds");
-    ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr");
-    ls.register_renamed("clippy::panic_params", "non_fmt_panics");
-    ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints");
-    ls.register_renamed("clippy::invalid_atomic_ordering", "invalid_atomic_ordering");
-    ls.register_renamed("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums");
+    for (old_name, new_name) in renamed_lints::RENAMED_LINTS {
+        ls.register_renamed(old_name, new_name);
+    }
 }
 
 // only exists to let the dogfood integration test works.
index 662a561f171e91c49450f05d4818ff68ce973531..ab5d3fa7b6d9c98b55a5c82c0a192e75f68dde13 100644 (file)
@@ -1,16 +1,19 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::trait_ref_of_method;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
 use rustc_hir::intravisit::{
-    walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty, Visitor,
+    walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
+    walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor,
 };
 use rustc_hir::FnRetTy::Return;
 use rustc_hir::{
-    BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem,
+    BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem,
     ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier,
     TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
 };
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::nested_filter as middle_nested_filter;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
 use rustc_span::symbol::{kw, Ident, Symbol};
 
 impl<'tcx> LateLintPass<'tcx> for Lifetimes {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        if let ItemKind::Fn(ref sig, ref generics, id) = item.kind {
+        if let ItemKind::Fn(ref sig, generics, id) = item.kind {
             check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true);
+        } else if let ItemKind::Impl(impl_) = item.kind {
+            report_extra_impl_lifetimes(cx, impl_);
         }
     }
 
@@ -95,7 +100,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>)
                 sig.decl,
                 Some(id),
                 None,
-                &item.generics,
+                item.generics,
                 item.span,
                 report_extra_lifetimes,
             );
@@ -108,7 +113,7 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>
                 TraitFn::Required(sig) => (None, Some(sig)),
                 TraitFn::Provided(id) => (Some(id), None),
             };
-            check_fn_inner(cx, sig.decl, body, trait_sig, &item.generics, item.span, true);
+            check_fn_inner(cx, sig.decl, body, trait_sig, item.generics, item.span, true);
         }
     }
 }
@@ -201,8 +206,7 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident:
             visitor.visit_ty(self_ty);
 
             !visitor.all_lts().is_empty()
-        }
-        else {
+        } else {
             false
         }
     }
@@ -486,11 +490,29 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_
     false
 }
 
-struct LifetimeChecker {
+struct LifetimeChecker<'cx, 'tcx, F> {
+    cx: &'cx LateContext<'tcx>,
     map: FxHashMap<Symbol, Span>,
+    phantom: std::marker::PhantomData<F>,
 }
 
-impl<'tcx> Visitor<'tcx> for LifetimeChecker {
+impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> {
+    fn new(cx: &'cx LateContext<'tcx>, map: FxHashMap<Symbol, Span>) -> LifetimeChecker<'cx, 'tcx, F> {
+        Self {
+            cx,
+            map,
+            phantom: std::marker::PhantomData,
+        }
+    }
+}
+
+impl<'cx, 'tcx, F> Visitor<'tcx> for LifetimeChecker<'cx, 'tcx, F>
+where
+    F: NestedFilter<'tcx>,
+{
+    type Map = rustc_middle::hir::map::Map<'tcx>;
+    type NestedFilter = F;
+
     // for lifetimes as parameters of generics
     fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
         self.map.remove(&lifetime.name.ident().name);
@@ -506,6 +528,10 @@ fn visit_generic_param(&mut self, param: &'tcx GenericParam<'_>) {
             walk_generic_param(self, param);
         }
     }
+
+    fn nested_visit_map(&mut self) -> Self::Map {
+        self.cx.tcx.hir()
+    }
 }
 
 fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) {
@@ -517,7 +543,7 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>,
             _ => None,
         })
         .collect();
-    let mut checker = LifetimeChecker { map: hs };
+    let mut checker = LifetimeChecker::<hir_nested_filter::None>::new(cx, hs);
 
     walk_generics(&mut checker, generics);
     walk_fn_decl(&mut checker, func);
@@ -532,6 +558,32 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>,
     }
 }
 
+fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>) {
+    let hs = impl_
+        .generics
+        .params
+        .iter()
+        .filter_map(|par| match par.kind {
+            GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)),
+            _ => None,
+        })
+        .collect();
+    let mut checker = LifetimeChecker::<middle_nested_filter::All>::new(cx, hs);
+
+    walk_generics(&mut checker, impl_.generics);
+    if let Some(ref trait_ref) = impl_.of_trait {
+        walk_trait_ref(&mut checker, trait_ref);
+    }
+    walk_ty(&mut checker, impl_.self_ty);
+    for item in impl_.items {
+        walk_impl_item_ref(&mut checker, item);
+    }
+
+    for &v in checker.map.values() {
+        span_lint(cx, EXTRA_UNUSED_LIFETIMES, v, "this lifetime isn't used in the impl");
+    }
+}
+
 struct BodyLifetimeChecker {
     lifetimes_used_in_body: bool,
 }
index b7430f49229ae6e673a98fd596f717b3324418c0..269d3c62eafcd5d0e9692f80bfb27a250cbbc0d3 100644 (file)
     /// This is most probably a typo
     ///
     /// ### Known problems
-    /// - Recommends a signed suffix, even though the number might be too big and an unsigned
-    ///   suffix is required
+    /// - Does not match on integers too large to fit in the corresponding unsigned type
     /// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers
     ///
     /// ### Example
-    /// ```rust
-    /// // Probably mistyped
-    /// 2_32;
-    ///
-    /// // Good
-    /// 2_i32;
+    /// `2_32` => `2_i32`
+    /// `250_8 => `250_u8`
     /// ```
     #[clippy::version = "1.30.0"]
     pub MISTYPED_LITERAL_SUFFIXES,
@@ -310,18 +305,47 @@ fn check_for_mistyped_suffix(
             return true;
         }
 
-        let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent {
-            (exponent, &["32", "64"][..], 'f')
+        let (part, mistyped_suffixes, is_float) = if let Some((_, exponent)) = &mut num_lit.exponent {
+            (exponent, &["32", "64"][..], true)
         } else if num_lit.fraction.is_some() {
-            (&mut num_lit.integer, &["32", "64"][..], 'f')
+            return true;
         } else {
-            (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i')
+            (&mut num_lit.integer, &["8", "16", "32", "64"][..], false)
         };
 
         let mut split = part.rsplit('_');
         let last_group = split.next().expect("At least one group");
         if split.next().is_some() && mistyped_suffixes.contains(&last_group) {
-            *part = &part[..part.len() - last_group.len()];
+            let main_part = &part[..part.len() - last_group.len()];
+            let missing_char;
+            if is_float {
+                missing_char = 'f';
+            } else {
+                let radix = match num_lit.radix {
+                    Radix::Binary => 2,
+                    Radix::Octal => 8,
+                    Radix::Decimal => 10,
+                    Radix::Hexadecimal => 16,
+                };
+                if let Ok(int) = u64::from_str_radix(&main_part.replace('_', ""), radix) {
+                    missing_char = match (last_group, int) {
+                        ("8", i) if i8::try_from(i).is_ok() => 'i',
+                        ("16", i) if i16::try_from(i).is_ok() => 'i',
+                        ("32", i) if i32::try_from(i).is_ok() => 'i',
+                        ("64", i) if i64::try_from(i).is_ok() => 'i',
+                        ("8", u) if u8::try_from(u).is_ok() => 'u',
+                        ("16", u) if u16::try_from(u).is_ok() => 'u',
+                        ("32", u) if u32::try_from(u).is_ok() => 'u',
+                        ("64", _) => 'u',
+                        _ => {
+                            return true;
+                        },
+                    }
+                } else {
+                    return true;
+                }
+            }
+            *part = main_part;
             let mut sugg = num_lit.format();
             sugg.push('_');
             sugg.push(missing_char);
index 9ba9642fcc833691fabcd4489b206e6ddb16a37a..70a118d6b353930ea8c751c0eef2fe0a96b47e0b 100644 (file)
@@ -168,8 +168,9 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
             .operands
             .iter()
             .map(|(o, _)| match o {
-                InlineAsmOperand::In { expr, .. }
-                | InlineAsmOperand::InOut { expr, .. } => never_loop_expr(expr, main_loop_id),
+                InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
+                    never_loop_expr(expr, main_loop_id)
+                },
                 InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter(), main_loop_id),
                 InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
                     never_loop_expr_all(&mut once(in_expr).chain(out_expr.iter()), main_loop_id)
index dcf44303cf449f2a0b83322414643fdd7243e6db..ac3d9447b6bd3d35fc2472e35ca7dd00ad84eb9e 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
-use clippy_utils::{meets_msrv, msrvs};
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{get_parent_expr, meets_msrv, msrvs};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
@@ -24,7 +24,7 @@
     /// ```
     /// Use instead:
     /// ```rust
-    /// usize::BITS;
+    /// usize::BITS as usize;
     /// ```
     #[clippy::version = "1.60.0"]
     pub MANUAL_BITS,
@@ -59,16 +59,19 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx 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 sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS"));
+
                 span_lint_and_sugg(
                     cx,
                     MANUAL_BITS,
                     expr.span,
                     "usage of `mem::size_of::<T>()` to obtain the size of `T` in bits",
                     "consider using",
-                    format!("{}::BITS", snippet_opt(cx, real_ty.span).unwrap()),
-                    Applicability::MachineApplicable,
+                    sugg,
+                    app,
                 );
             }
         }
@@ -108,3 +111,36 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<
         }
     }
 }
+
+fn create_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, base_sugg: String) -> String {
+    if let Some(parent_expr) = get_parent_expr(cx, expr) {
+        if is_ty_conversion(parent_expr) {
+            return base_sugg;
+        }
+
+        // These expressions have precedence over casts, the suggestion therefore
+        // needs to be wrapped into parentheses
+        match parent_expr.kind {
+            ExprKind::Unary(..) | ExprKind::AddrOf(..) | ExprKind::MethodCall(..) => {
+                return format!("({base_sugg} as usize)");
+            },
+            _ => {},
+        }
+    }
+
+    format!("{base_sugg} as usize")
+}
+
+fn is_ty_conversion(expr: &Expr<'_>) -> bool {
+    if let ExprKind::Cast(..) = expr.kind {
+        true
+    } else if let ExprKind::MethodCall(path, [_], _) = expr.kind
+        && path.ident.name == rustc_span::sym::try_into
+    {
+        // This is only called for `usize` which implements `TryInto`. Therefore,
+        // we don't have to check here if `self` implements the `TryInto` trait.
+        true
+    } else {
+        false
+    }
+}
index 33d1bb2985f43fdcd4dcd525abf8d0fb16082c78..b8d620d81713046ac5892d9c6239cb72157d240f 100644 (file)
@@ -1,13 +1,17 @@
 use clippy_utils::attrs::is_doc_hidden;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{meets_msrv, msrvs};
-use if_chain::if_chain;
-use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind};
+use clippy_utils::{is_lint_allowed, meets_msrv, msrvs};
+use rustc_ast::ast::{self, VisibilityKind};
+use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
-use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+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_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::{sym, Span};
 
 declare_clippy_lint! {
     "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
 }
 
-#[derive(Clone)]
-pub struct ManualNonExhaustive {
+#[allow(clippy::module_name_repetitions)]
+pub struct ManualNonExhaustiveStruct {
     msrv: Option<RustcVersion>,
 }
 
-impl ManualNonExhaustive {
+impl ManualNonExhaustiveStruct {
     #[must_use]
     pub fn new(msrv: Option<RustcVersion>) -> Self {
         Self { msrv }
     }
 }
 
-impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
+impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]);
 
-impl EarlyLintPass for ManualNonExhaustive {
-    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
-        if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
-            return;
-        }
+#[allow(clippy::module_name_repetitions)]
+pub struct ManualNonExhaustiveEnum {
+    msrv: Option<RustcVersion>,
+    constructed_enum_variants: FxHashSet<(DefId, DefId)>,
+    potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>,
+}
 
-        match &item.kind {
-            ItemKind::Enum(def, _) => {
-                check_manual_non_exhaustive_enum(cx, item, &def.variants);
-            },
-            ItemKind::Struct(variant_data, _) => {
-                if let VariantData::Unit(..) = variant_data {
-                    return;
-                }
-
-                check_manual_non_exhaustive_struct(cx, item, variant_data);
-            },
-            _ => {},
+impl ManualNonExhaustiveEnum {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self {
+            msrv,
+            constructed_enum_variants: FxHashSet::default(),
+            potential_enums: Vec::new(),
         }
     }
-
-    extract_msrv_attr!(EarlyContext);
 }
 
-fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) {
-    fn is_non_exhaustive_marker(variant: &Variant) -> bool {
-        matches!(variant.data, VariantData::Unit(_))
-            && variant.ident.as_str().starts_with('_')
-            && is_doc_hidden(&variant.attrs)
-    }
+impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]);
 
-    let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v));
-    if_chain! {
-        if let Some(marker) = markers.next();
-        if markers.count() == 0 && variants.len() > 1;
-        then {
-            span_lint_and_then(
-                cx,
-                MANUAL_NON_EXHAUSTIVE,
-                item.span,
-                "this seems like a manual implementation of the non-exhaustive pattern",
-                |diag| {
-                    if_chain! {
-                        if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive));
-                        let header_span = cx.sess().source_map().span_until_char(item.span, '{');
-                        if let Some(snippet) = snippet_opt(cx, header_span);
-                        then {
+impl EarlyLintPass for ManualNonExhaustiveStruct {
+    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
+            return;
+        }
+
+        if let ast::ItemKind::Struct(variant_data, _) = &item.kind {
+            let (fields, delimiter) = match variant_data {
+                ast::VariantData::Struct(fields, _) => (&**fields, '{'),
+                ast::VariantData::Tuple(fields, _) => (&**fields, '('),
+                ast::VariantData::Unit(_) => return,
+            };
+            if fields.len() <= 1 {
+                return;
+            }
+            let mut iter = fields.iter().filter_map(|f| match f.vis.kind {
+                VisibilityKind::Public => None,
+                VisibilityKind::Inherited => Some(Ok(f)),
+                _ => Some(Err(())),
+            });
+            if let Some(Ok(field)) = iter.next()
+                && iter.next().is_none()
+                && field.ty.kind.is_unit()
+                && field.ident.map_or(true, |name| name.as_str().starts_with('_'))
+            {
+                span_lint_and_then(
+                    cx,
+                    MANUAL_NON_EXHAUSTIVE,
+                    item.span,
+                    "this seems like a manual implementation of the non-exhaustive pattern",
+                    |diag| {
+                        if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive))
+                            && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter)
+                            && let Some(snippet) = snippet_opt(cx, header_span)
+                        {
                             diag.span_suggestion(
                                 header_span,
                                 "add the attribute",
@@ -126,61 +138,84 @@ fn is_non_exhaustive_marker(variant: &Variant) -> bool {
                                 Applicability::Unspecified,
                             );
                         }
+                        diag.span_help(field.span, "remove this field");
                     }
-                    diag.span_help(marker.span, "remove this variant");
-                });
+                );
+            }
         }
     }
+
+    extract_msrv_attr!(EarlyContext);
 }
 
-fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) {
-    fn is_private(field: &FieldDef) -> bool {
-        matches!(field.vis.kind, VisibilityKind::Inherited)
-    }
+impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
+            return;
+        }
 
-    fn is_non_exhaustive_marker(field: &FieldDef) -> bool {
-        is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_'))
+        if let hir::ItemKind::Enum(def, _) = &item.kind
+            && def.variants.len() > 1
+        {
+            let mut iter = def.variants.iter().filter_map(|v| {
+                let id = cx.tcx.hir().local_def_id(v.id);
+                (matches!(v.data, hir::VariantData::Unit(_))
+                    && v.ident.as_str().starts_with('_')
+                    && is_doc_hidden(cx.tcx.get_attrs(id.to_def_id())))
+                .then(|| (id, v.span))
+            });
+            if let Some((id, span)) = iter.next()
+                && iter.next().is_none()
+            {
+                self.potential_enums.push((item.def_id, id, item.span, span));
+            }
+        }
     }
 
-    fn find_header_span(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) -> Span {
-        let delimiter = match data {
-            VariantData::Struct(..) => '{',
-            VariantData::Tuple(..) => '(',
-            VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"),
-        };
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+        if let ExprKind::Path(QPath::Resolved(None, p)) = &e.kind
+            && let [.., name] = p.segments
+            && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res
+            && name.ident.as_str().starts_with('_')
+        {
+            let variant_id = cx.tcx.parent(id);
+            let enum_id = cx.tcx.parent(variant_id);
 
-        cx.sess().source_map().span_until_char(item.span, delimiter)
+            self.constructed_enum_variants.insert((enum_id, variant_id));
+        }
     }
 
-    let fields = data.fields();
-    let private_fields = fields.iter().filter(|f| is_private(f)).count();
-    let public_fields = fields.iter().filter(|f| f.vis.kind.is_pub()).count();
-
-    if_chain! {
-        if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1;
-        if let Some(marker) = fields.iter().find(|f| is_non_exhaustive_marker(f));
-        then {
+    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
+        for &(enum_id, _, enum_span, variant_span) in
+            self.potential_enums.iter().filter(|&&(enum_id, variant_id, _, _)| {
+                !self
+                    .constructed_enum_variants
+                    .contains(&(enum_id.to_def_id(), variant_id.to_def_id()))
+                    && !is_lint_allowed(cx, MANUAL_NON_EXHAUSTIVE, cx.tcx.hir().local_def_id_to_hir_id(enum_id))
+            })
+        {
             span_lint_and_then(
                 cx,
                 MANUAL_NON_EXHAUSTIVE,
-                item.span,
+                enum_span,
                 "this seems like a manual implementation of the non-exhaustive pattern",
                 |diag| {
-                    if_chain! {
-                        if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive));
-                        let header_span = find_header_span(cx, item, data);
-                        if let Some(snippet) = snippet_opt(cx, header_span);
-                        then {
+                    if !cx.tcx.adt_def(enum_id).is_variant_list_non_exhaustive()
+                        && let header_span = cx.sess().source_map().span_until_char(enum_span, '{')
+                        && let Some(snippet) = snippet_opt(cx, header_span)
+                    {
                             diag.span_suggestion(
                                 header_span,
                                 "add the attribute",
                                 format!("#[non_exhaustive] {}", snippet),
                                 Applicability::Unspecified,
                             );
-                        }
                     }
-                    diag.span_help(marker.span, "remove this field");
-                });
+                    diag.span_help(variant_span, "remove this variant");
+                },
+            );
         }
     }
+
+    extract_msrv_attr!(LateContext);
 }
index e233300e26ab898e94efcdbd13b5bee37b6dbcf7..ceb66947d02c6717a0ba802f61268997c00e48f0 100644 (file)
@@ -143,15 +143,11 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
 impl MapClone {
     fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) {
         let mut applicability = Applicability::MachineApplicable;
-        let message = if is_copy {
-            "you are using an explicit closure for copying elements"
-        } else {
-            "you are using an explicit closure for cloning elements"
-        };
-        let sugg_method = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) {
-            "copied"
+
+        let (message, sugg_method) = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) {
+            ("you are using an explicit closure for copying elements", "copied")
         } else {
-            "cloned"
+            ("you are using an explicit closure for cloning elements", "cloned")
         };
 
         span_lint_and_sugg(
index 77a4917ec58f0a80afd66d1d8e4e8841a94ad96b..3349b85f1347a396519aeb5cba96c59813c93cec 100644 (file)
@@ -24,7 +24,7 @@
     ///     vec.push(value)
     /// }
     ///
-    /// if let Some(valie) = iter.next().ok() {
+    /// if let Some(value) = iter.next().ok() {
     ///     vec.push(value)
     /// }
     /// ```
@@ -60,7 +60,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! {
             if let ExprKind::MethodCall(ok_path, [ref 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() methoduse std::marker::Sized;
+            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";
 
diff --git a/src/tools/clippy/clippy_lints/src/matches/infalliable_detructuring_match.rs b/src/tools/clippy/clippy_lints/src/matches/infalliable_detructuring_match.rs
deleted file mode 100644 (file)
index 2472acb..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs};
-use rustc_errors::Applicability;
-use rustc_hir::{ExprKind, Local, MatchSource, PatKind, QPath};
-use rustc_lint::LateContext;
-
-use super::INFALLIBLE_DESTRUCTURING_MATCH;
-
-pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool {
-    if_chain! {
-        if !local.span.from_expansion();
-        if let Some(expr) = local.init;
-        if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
-        if arms.len() == 1 && arms[0].guard.is_none();
-        if let PatKind::TupleStruct(
-            QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
-        if args.len() == 1;
-        if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
-        let body = peel_blocks(arms[0].body);
-        if path_to_local_id(body, arg);
-
-        then {
-            let mut applicability = Applicability::MachineApplicable;
-            span_lint_and_sugg(
-                cx,
-                INFALLIBLE_DESTRUCTURING_MATCH,
-                local.span,
-                "you seem to be trying to use `match` to destructure a single infallible pattern. \
-                Consider using `let`",
-                "try this",
-                format!(
-                    "let {}({}) = {};",
-                    snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
-                    snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
-                    snippet_with_applicability(cx, target.span, "..", &mut applicability),
-                ),
-                applicability,
-            );
-            return true;
-        }
-    }
-    false
-}
diff --git a/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs b/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs
new file mode 100644 (file)
index 0000000..2472acb
--- /dev/null
@@ -0,0 +1,44 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs};
+use rustc_errors::Applicability;
+use rustc_hir::{ExprKind, Local, MatchSource, PatKind, QPath};
+use rustc_lint::LateContext;
+
+use super::INFALLIBLE_DESTRUCTURING_MATCH;
+
+pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool {
+    if_chain! {
+        if !local.span.from_expansion();
+        if let Some(expr) = local.init;
+        if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
+        if arms.len() == 1 && arms[0].guard.is_none();
+        if let PatKind::TupleStruct(
+            QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
+        if args.len() == 1;
+        if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
+        let body = peel_blocks(arms[0].body);
+        if path_to_local_id(body, arg);
+
+        then {
+            let mut applicability = Applicability::MachineApplicable;
+            span_lint_and_sugg(
+                cx,
+                INFALLIBLE_DESTRUCTURING_MATCH,
+                local.span,
+                "you seem to be trying to use `match` to destructure a single infallible pattern. \
+                Consider using `let`",
+                "try this",
+                format!(
+                    "let {}({}) = {};",
+                    snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
+                    snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
+                    snippet_with_applicability(cx, target.span, "..", &mut applicability),
+                ),
+                applicability,
+            );
+            return true;
+        }
+    }
+    false
+}
index b8591fe0db0ad31a3e94fc030f69310a3f137f7f..9b7344fb8b0b21a93d9f5a69a9d698921e6c0771 100644 (file)
@@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
         .map(|a| NormalizedPat::from_pat(cx, &arena, a.pat))
         .collect();
 
-    // The furthast forwards a pattern can move without semantic changes
+    // The furthest forwards a pattern can move without semantic changes
     let forwards_blocking_idxs: Vec<_> = normalized_pats
         .iter()
         .enumerate()
@@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
         })
         .collect();
 
-    // The furthast backwards a pattern can move without semantic changes
+    // The furthest backwards a pattern can move without semantic changes
     let backwards_blocking_idxs: Vec<_> = normalized_pats
         .iter()
         .enumerate()
index e93b494653fc05923feecace84790bb7606d6122..401ecef460c35cefd52f225576b5ddbd73b021ce 100644 (file)
@@ -1,4 +1,4 @@
-use clippy_utils::source::{snippet_opt, walk_span_to_context};
+use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context};
 use clippy_utils::{meets_msrv, msrvs};
 use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
 use rustc_lexer::{tokenize, TokenKind};
@@ -7,7 +7,7 @@
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{Span, SpanData, SyntaxContext};
 
-mod infalliable_detructuring_match;
+mod infallible_destructuring_match;
 mod match_as_ref;
 mod match_bool;
 mod match_like_matches;
@@ -653,6 +653,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         }
 
         if let ExprKind::Match(ex, arms, source) = expr.kind {
+            if !span_starts_with(cx, expr.span, "match") {
+                return;
+            }
             if !contains_cfg_arm(cx, expr, ex, arms) {
                 if source == MatchSource::Normal {
                     if !(meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO)
@@ -691,7 +694,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
     }
 
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
-        self.infallible_destructuring_match_linted |= infalliable_detructuring_match::check(cx, local);
+        self.infallible_destructuring_match_linted |= infallible_destructuring_match::check(cx, local);
     }
 
     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
index 2105a03e03a301cb8cca7aba53623766dd1fdc6e..f920ad4651f9d4b8c8d304cfb55bd1e99eb1d73c 100644 (file)
@@ -83,7 +83,7 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
             return false;
         }
 
-        // Recurrsively check for each `else if let` phrase,
+        // Recursively check for each `else if let` phrase,
         if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else) {
             return check_if_let(cx, nested_if_let);
         }
@@ -99,7 +99,7 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
                 if let ExprKind::Path(ref qpath) = ret.kind {
                     return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret);
                 }
-                return true;
+                return false;
             }
             return eq_expr_value(cx, if_let.let_expr, ret);
         }
@@ -118,7 +118,7 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
 }
 
 /// Manually check for coercion casting by checking if the type of the match operand or let expr
-/// differs with the assigned local variable or the funtion return type.
+/// differs with the assigned local variable or the function return type.
 fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool {
     if let Some(p_node) = get_parent_node(cx.tcx, p_expr.hir_id) {
         match p_node {
index aa3552001f469e4aa6ba2720117baa72c3e2bd32..37b67647efe9e0d6b54abddce1cbec6d5390f65d 100644 (file)
@@ -2,17 +2,16 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
+use clippy_utils::ty::needs_ordered_drop;
 use clippy_utils::{higher, match_def_path};
 use clippy_utils::{is_lang_ctor, is_trait_method, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
-use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{OptionNone, PollPending};
 use rustc_hir::{
     intravisit::{walk_expr, Visitor},
-    Arm, Block, Expr, ExprKind, LangItem, Node, Pat, PatKind, QPath, UnOp,
+    Arm, Block, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp,
 };
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty};
@@ -32,59 +31,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
     }
 }
 
-/// Checks if the drop order for a type matters. Some std types implement drop solely to
-/// deallocate memory. For these types, and composites containing them, changing the drop order
-/// won't result in any observable side effects.
-fn type_needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
-    type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
-}
-
-fn type_needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
-    if !seen.insert(ty) {
-        return false;
-    }
-    if !ty.needs_drop(cx.tcx, cx.param_env) {
-        false
-    } else if !cx
-        .tcx
-        .lang_items()
-        .drop_trait()
-        .map_or(false, |id| implements_trait(cx, ty, id, &[]))
-    {
-        // This type doesn't implement drop, so no side effects here.
-        // Check if any component type has any.
-        match ty.kind() {
-            ty::Tuple(fields) => fields.iter().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
-            ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, *ty, seen),
-            ty::Adt(adt, subs) => adt
-                .all_fields()
-                .map(|f| f.ty(cx.tcx, subs))
-                .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
-            _ => true,
-        }
-    }
-    // Check for std types which implement drop, but only for memory allocation.
-    else if is_type_diagnostic_item(cx, ty, sym::Vec)
-        || is_type_lang_item(cx, ty, LangItem::OwnedBox)
-        || is_type_diagnostic_item(cx, ty, sym::Rc)
-        || is_type_diagnostic_item(cx, ty, sym::Arc)
-        || is_type_diagnostic_item(cx, ty, sym::cstring_type)
-        || is_type_diagnostic_item(cx, ty, sym::BTreeMap)
-        || is_type_diagnostic_item(cx, ty, sym::LinkedList)
-        || match_type(cx, ty, &paths::WEAK_RC)
-        || match_type(cx, ty, &paths::WEAK_ARC)
-    {
-        // Check all of the generic arguments.
-        if let ty::Adt(_, subs) = ty.kind() {
-            subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen))
-        } else {
-            true
-        }
-    } else {
-        true
-    }
-}
-
 // Extract the generic arguments out of a type
 fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
     if_chain! {
@@ -115,7 +61,7 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
                 // e.g. In `(String::new(), 0).1` the string is a temporary value.
                 ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
                     if !matches!(expr.kind, ExprKind::Path(_)) {
-                        if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
+                        if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
                             self.res = true;
                         } else {
                             self.visit_expr(expr);
@@ -126,7 +72,7 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
                 // e.g. In `(vec![0])[0]` the vector is a temporary value.
                 ExprKind::Index(base, index) => {
                     if !matches!(base.kind, ExprKind::Path(_)) {
-                        if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
+                        if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
                             self.res = true;
                         } else {
                             self.visit_expr(base);
@@ -143,7 +89,7 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
                             .typeck_results()
                             .type_dependent_def_id(expr.hir_id)
                             .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
-                        if self_by_ref && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) {
+                        if self_by_ref && needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) {
                             self.res = true;
                         } else {
                             self.visit_expr(self_arg);
@@ -243,7 +189,7 @@ fn find_sugg_for_if_let<'tcx>(
     // scrutinee would be, so they have to be considered as well.
     // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
     // for the duration if body.
-    let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
+    let needs_drop = needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
 
     // check that `while_let_on_iterator` lint does not trigger
     if_chain! {
index 5076239a57c4d79f707b8aa0655d698ba126faf7..0aadb482acddad443b4161732e31cc7871937146 100644 (file)
@@ -14,6 +14,7 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) {
         if let ty::Adt(def, _) = ty.kind();
         if def.is_struct() || def.is_union();
         if fields.len() == def.non_enum_variant().fields.len();
+        if !def.non_enum_variant().is_field_list_non_exhaustive();
 
         then {
             span_lint_and_help(
diff --git a/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs b/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
new file mode 100644 (file)
index 0000000..ad333df
--- /dev/null
@@ -0,0 +1,50 @@
+//! Lint for `c.is_digit(10)`
+
+use super::IS_DIGIT_ASCII_RADIX;
+use clippy_utils::{
+    consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs,
+    source::snippet_with_applicability,
+};
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_semver::RustcVersion;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    self_arg: &'tcx Expr<'_>,
+    radix: &'tcx Expr<'_>,
+    msrv: Option<&RustcVersion>,
+) {
+    if !meets_msrv(msrv, &msrvs::IS_ASCII_DIGIT) {
+        return;
+    }
+
+    if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_char() {
+        return;
+    }
+
+    if let Some(radix_val) = constant_full_int(cx, cx.typeck_results(), radix) {
+        let (num, replacement) = match radix_val {
+            FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"),
+            FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"),
+            _ => return,
+        };
+        let mut applicability = Applicability::MachineApplicable;
+
+        span_lint_and_sugg(
+            cx,
+            IS_DIGIT_ASCII_RADIX,
+            expr.span,
+            &format!("use of `char::is_digit` with literal radix of {}", num),
+            "try",
+            format!(
+                "{}.{}()",
+                snippet_with_applicability(cx, self_arg.span, "..", &mut applicability),
+                replacement
+            ),
+            applicability,
+        );
+    }
+}
index 958c3773087b6aa70f19636f445a847e07fd99e5..152072e09c77275795922332495b9622baea5335 100644 (file)
@@ -1,72 +1,47 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher::Range;
 use clippy_utils::is_integer_const;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{
-    higher::{self, Range},
-    SpanlessEq,
-};
 use rustc_ast::ast::RangeLimits;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::LateContext;
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::sym;
 use rustc_span::Span;
 
 use super::ITER_WITH_DRAIN;
 
-const DRAIN_TYPES: &[Symbol] = &[sym::Vec, sym::VecDeque];
-
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: &Expr<'_>) {
-    let ty = cx.typeck_results().expr_ty(recv).peel_refs();
-    if let Some(drained_type) = DRAIN_TYPES.iter().find(|&&sym| is_type_diagnostic_item(cx, ty, sym)) {
-        // Refuse to emit `into_iter` suggestion on draining struct fields due
-        // to the strong possibility of processing unmovable field.
-        if let ExprKind::Field(..) = recv.kind {
-            return;
-        }
+    if !matches!(recv.kind, ExprKind::Field(..))
+        && 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)
+    {
+        span_lint_and_sugg(
+            cx,
+            ITER_WITH_DRAIN,
+            span.with_hi(expr.span.hi()),
+            &format!("`drain(..)` used on a `{}`", ty_name),
+            "try this",
+            "into_iter()".to_string(),
+            Applicability::MaybeIncorrect,
+        );
+    };
+}
 
-        if let Some(range) = higher::Range::hir(arg) {
-            let left_full = match range {
-                Range { start: Some(start), .. } if is_integer_const(cx, start, 0) => true,
-                Range { start: None, .. } => true,
-                _ => false,
-            };
-            let full = left_full
-                && match range {
-                    Range {
-                        end: Some(end),
-                        limits: RangeLimits::HalfOpen,
-                        ..
-                    } => {
-                        // `x.drain(..x.len())` call
-                        if_chain! {
-                            if let ExprKind::MethodCall(len_path, len_args, _) = end.kind;
-                            if len_path.ident.name == sym::len && len_args.len() == 1;
-                            if let ExprKind::Path(QPath::Resolved(_, drain_path)) = recv.kind;
-                            if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;
-                            if SpanlessEq::new(cx).eq_path(drain_path, len_path);
-                            then { true }
-                            else { false }
-                        }
-                    },
-                    Range {
-                        end: None,
-                        limits: RangeLimits::HalfOpen,
-                        ..
-                    } => true,
-                    _ => false,
-                };
-            if full {
-                span_lint_and_sugg(
-                    cx,
-                    ITER_WITH_DRAIN,
-                    span.with_hi(expr.span.hi()),
-                    &format!("`drain(..)` used on a `{}`", drained_type),
-                    "try this",
-                    "into_iter()".to_string(),
-                    Applicability::MaybeIncorrect,
-                );
+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
             }
-        }
-    }
+        })
 }
index 70d021a1668eb7ffb68b2cce0a79cc5c50d6195d..f3be71f6b8bb8dd78d6cfc5b2825857dea528e2a 100644 (file)
@@ -26,6 +26,7 @@
 mod inefficient_to_string;
 mod inspect_for_each;
 mod into_iter_on_ref;
+mod is_digit_ascii_radix;
 mod iter_cloned_collect;
 mod iter_count;
 mod iter_next_slice;
@@ -42,6 +43,7 @@
 mod map_identity;
 mod map_unwrap_or;
 mod needless_option_as_deref;
+mod needless_option_take;
 mod ok_expect;
 mod option_as_ref_deref;
 mod option_map_or_none;
     /// Checks for methods with certain name prefixes and which
     /// doesn't match how self is taken. The actual rules are:
     ///
-    /// |Prefix |Postfix     |`self` taken           | `self` type  |
-    /// |-------|------------|-----------------------|--------------|
-    /// |`as_`  | none       |`&self` or `&mut self` | any          |
-    /// |`from_`| none       | none                  | any          |
-    /// |`into_`| none       |`self`                 | any          |
-    /// |`is_`  | none       |`&self` or none        | any          |
-    /// |`to_`  | `_mut`     |`&mut self`            | any          |
-    /// |`to_`  | not `_mut` |`self`                 | `Copy`       |
-    /// |`to_`  | not `_mut` |`&self`                | not `Copy`   |
+    /// |Prefix |Postfix     |`self` taken                   | `self` type  |
+    /// |-------|------------|-------------------------------|--------------|
+    /// |`as_`  | none       |`&self` or `&mut self`         | any          |
+    /// |`from_`| none       | none                          | any          |
+    /// |`into_`| none       |`self`                         | any          |
+    /// |`is_`  | none       |`&mut self` or `&self` or none | any          |
+    /// |`to_`  | `_mut`     |`&mut self`                    | any          |
+    /// |`to_`  | not `_mut` |`self`                         | `Copy`       |
+    /// |`to_`  | not `_mut` |`&self`                        | not `Copy`   |
     ///
     /// Note: Clippy doesn't trigger methods with `to_` prefix in:
     /// - Traits definition.
     #[clippy::version = "1.55.0"]
     pub EXTEND_WITH_DRAIN,
     perf,
-    "using vec.append(&mut vec) to move the full range of a vecor to another"
+    "using vec.append(&mut vec) to move the full range of a vector to another"
 }
 
 declare_clippy_lint! {
     /// ### Example
     /// ```rust,ignore
     /// // Bad
-    ///  let (key, value) = _.splitn(2, '=').next_tuple()?;
-    ///  let value = _.splitn(2, '=').nth(1)?;
+    /// let s = "key=value=add";
+    /// let (key, value) = s.splitn(2, '=').next_tuple()?;
+    /// let value = s.splitn(2, '=').nth(1)?;
     ///
+    /// let mut parts = s.splitn(2, '=');
+    /// let key = parts.next()?;
+    /// let value = parts.next()?;
+    /// ```
+    /// Use instead:
+    /// ```rust,ignore
     /// // Good
-    /// let (key, value) = _.split_once('=')?;
-    /// let value = _.split_once('=')?.1;
+    /// let s = "key=value=add";
+    /// let (key, value) = s.split_once('=')?;
+    /// let value = s.split_once('=')?.1;
+    ///
+    /// let (key, value) = s.split_once('=')?;
     /// ```
+    ///
+    /// ### Limitations
+    /// The multiple statement variant currently only detects `iter.next()?`/`iter.next().unwrap()`
+    /// in two separate `let` statements that immediately follow the `splitn()`
     #[clippy::version = "1.57.0"]
     pub MANUAL_SPLIT_ONCE,
     complexity,
     /// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")`
     /// will prevent loop unrolling and will result in a negative performance impact.
     ///
-    /// Additionlly, differences have been observed between aarch64 and x86_64 assembly output,
+    /// Additionally, differences have been observed between aarch64 and x86_64 assembly output,
     /// with aarch64 tending to producing faster assembly in more cases when using `.collect::<String>()`
     #[clippy::version = "1.61.0"]
     pub UNNECESSARY_JOIN,
     "no-op use of `deref` or `deref_mut` method to `Option`."
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Finds usages of [`char::is_digit`]
+    /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that
+    /// can be replaced with [`is_ascii_digit`]
+    /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or
+    /// [`is_ascii_hexdigit`]
+    /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit).
+    ///
+    /// ### Why is this bad?
+    /// `is_digit(..)` is slower and requires specifying the radix.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let c: char = '6';
+    /// c.is_digit(10);
+    /// c.is_digit(16);
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let c: char = '6';
+    /// c.is_ascii_digit();
+    /// c.is_ascii_hexdigit();
+    /// ```
+    #[clippy::version = "1.61.0"]
+    pub IS_DIGIT_ASCII_RADIX,
+    style,
+    "use of `char::is_digit(..)` with literal radix of 10 or 16"
+}
+
+declare_clippy_lint! {
+    ///
+    /// ### Why is this bad?
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x = Some(3);
+    /// x.as_ref().take();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let x = Some(3);
+    /// x.as_ref();
+    /// ```
+    #[clippy::version = "1.61.0"]
+    pub NEEDLESS_OPTION_TAKE,
+    complexity,
+    "using `.as_ref().take()` on a temporary value"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Option<RustcVersion>,
@@ -2219,6 +2285,8 @@ pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Sel
     UNNECESSARY_JOIN,
     ERR_EXPECT,
     NEEDLESS_OPTION_AS_DEREF,
+    IS_DIGIT_ASCII_RADIX,
+    NEEDLESS_OPTION_TAKE,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -2254,7 +2322,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
                 single_char_add_str::check(cx, expr, args);
                 into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args);
                 single_char_pattern::check(cx, expr, method_call.ident.name, args);
-                unnecessary_to_owned::check(cx, expr, method_call.ident.name, args);
+                unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv.as_ref());
             },
             hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
                 let mut info = BinaryExprInfo {
@@ -2516,6 +2584,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
             },
             ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
             ("is_file", []) => filetype_is_file::check(cx, expr, recv),
+            ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, msrv),
             ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
             ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
             ("join", [join_arg]) => {
@@ -2574,12 +2643,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
             ("splitn" | "rsplitn", [count_arg, pat_arg]) => {
                 if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
                     suspicious_splitn::check(cx, name, expr, recv, count);
-                    if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) {
-                        str_splitn::check_manual_split_once(cx, name, expr, recv, pat_arg);
-                    }
-                    if count >= 2 {
-                        str_splitn::check_needless_splitn(cx, name, expr, recv, pat_arg, count);
-                    }
+                    str_splitn::check(cx, name, expr, recv, pat_arg, count, msrv);
                 }
             },
             ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
@@ -2595,6 +2659,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                     }
                 }
             },
+            ("take", []) => needless_option_take::check(cx, expr, recv),
             ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
                 implicit_clone::check(cx, name, expr, recv);
             },
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs
new file mode 100644 (file)
index 0000000..829c118
--- /dev/null
@@ -0,0 +1,41 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::match_def_path;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::NEEDLESS_OPTION_TAKE;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
+    // Checks if expression type is equal to sym::Option and if the expr is not a syntactic place
+    if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) && has_expr_as_ref_path(cx, recv) {
+        let mut applicability = Applicability::MachineApplicable;
+        span_lint_and_sugg(
+            cx,
+            NEEDLESS_OPTION_TAKE,
+            expr.span,
+            "called `Option::take()` on a temporary value",
+            "try",
+            format!(
+                "{}",
+                snippet_with_applicability(cx, recv.span, "..", &mut applicability)
+            ),
+            applicability,
+        );
+    }
+}
+
+fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    let expr_type = cx.typeck_results().expr_ty(expr);
+    is_type_diagnostic_item(cx, expr_type, sym::Option)
+}
+
+fn has_expr_as_ref_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    if let Some(ref_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
+        return match_def_path(cx, ref_id, &["core", "option", "Option", "as_ref"]);
+    }
+    false
+}
index 8125930b3046144053a7ab4bf8e4063e2ef42675..52891eeed0696db2031bcb2fcb67943972c31036 100644 (file)
@@ -1,36 +1,83 @@
 use clippy_utils::consts::{constant, Constant};
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_with_context;
-use clippy_utils::{is_diag_item_method, match_def_path, paths};
+use clippy_utils::usage::local_used_after_expr;
+use clippy_utils::visitors::expr_visitor;
+use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath};
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{
+    BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind,
+};
 use rustc_lint::LateContext;
-use rustc_middle::ty::{self, adjustment::Adjust};
-use rustc_span::{symbol::sym, Span, SyntaxContext};
+use rustc_middle::ty;
+use rustc_semver::RustcVersion;
+use rustc_span::{sym, Span, Symbol, SyntaxContext};
 
-use super::MANUAL_SPLIT_ONCE;
+use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN};
 
-pub(super) fn check_manual_split_once(
+pub(super) fn check(
     cx: &LateContext<'_>,
     method_name: &str,
     expr: &Expr<'_>,
     self_arg: &Expr<'_>,
     pat_arg: &Expr<'_>,
+    count: u128,
+    msrv: Option<&RustcVersion>,
 ) {
-    if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
+    if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
         return;
     }
 
+    let needless = |usage_kind| match usage_kind {
+        IterUsageKind::Nth(n) => count > n + 1,
+        IterUsageKind::NextTuple => count > 2,
+    };
+    let manual = count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE);
+
+    match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) {
+        Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg),
+        Some(usage) if manual => check_manual_split_once(cx, method_name, expr, self_arg, pat_arg, &usage),
+        None if manual => {
+            check_manual_split_once_indirect(cx, method_name, expr, self_arg, pat_arg);
+        },
+        _ => {},
+    }
+}
+
+fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) {
+    let mut app = Applicability::MachineApplicable;
+    let r = if method_name == "splitn" { "" } else { "r" };
+
+    span_lint_and_sugg(
+        cx,
+        NEEDLESS_SPLITN,
+        expr.span,
+        &format!("unnecessary use of `{r}splitn`"),
+        "try this",
+        format!(
+            "{}.{r}split({})",
+            snippet_with_context(cx, self_arg.span, expr.span.ctxt(), "..", &mut app).0,
+            snippet_with_context(cx, pat_arg.span, expr.span.ctxt(), "..", &mut app).0,
+        ),
+        app,
+    );
+}
+
+fn check_manual_split_once(
+    cx: &LateContext<'_>,
+    method_name: &str,
+    expr: &Expr<'_>,
+    self_arg: &Expr<'_>,
+    pat_arg: &Expr<'_>,
+    usage: &IterUsage,
+) {
     let ctxt = expr.span.ctxt();
-    let (method_name, msg, reverse) = if method_name == "splitn" {
-        ("split_once", "manual implementation of `split_once`", false)
+    let (msg, reverse) = if method_name == "splitn" {
+        ("manual implementation of `split_once`", false)
     } else {
-        ("rsplit_once", "manual implementation of `rsplit_once`", true)
-    };
-    let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) {
-        Some(x) => x,
-        None => return,
+        ("manual implementation of `rsplit_once`", true)
     };
 
     let mut app = Applicability::MachineApplicable;
@@ -39,84 +86,198 @@ pub(super) fn check_manual_split_once(
 
     let sugg = match usage.kind {
         IterUsageKind::NextTuple => {
-            format!("{}.{}({})", self_snip, method_name, pat_snip)
-        },
-        IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip),
-        IterUsageKind::Next | IterUsageKind::Second => {
-            let self_deref = {
-                let adjust = cx.typeck_results().expr_adjustments(self_arg);
-                if adjust.len() < 2 {
-                    String::new()
-                } else if cx.typeck_results().expr_ty(self_arg).is_box()
-                    || adjust
-                        .iter()
-                        .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box())
-                {
-                    format!("&{}", "*".repeat(adjust.len().saturating_sub(1)))
-                } else {
-                    "*".repeat(adjust.len().saturating_sub(2))
-                }
-            };
-            if matches!(usage.kind, IterUsageKind::Next) {
-                match usage.unwrap_kind {
-                    Some(UnwrapKind::Unwrap) => {
-                        if reverse {
-                            format!("{}.{}({}).unwrap().0", self_snip, method_name, pat_snip)
-                        } else {
-                            format!(
-                                "{}.{}({}).map_or({}{}, |x| x.0)",
-                                self_snip, method_name, pat_snip, self_deref, &self_snip
-                            )
-                        }
-                    },
-                    Some(UnwrapKind::QuestionMark) => {
-                        format!(
-                            "{}.{}({}).map_or({}{}, |x| x.0)",
-                            self_snip, method_name, pat_snip, self_deref, &self_snip
-                        )
-                    },
-                    None => {
-                        format!(
-                            "Some({}.{}({}).map_or({}{}, |x| x.0))",
-                            &self_snip, method_name, pat_snip, self_deref, &self_snip
-                        )
-                    },
-                }
+            if reverse {
+                format!("{self_snip}.rsplit_once({pat_snip}).map(|(x, y)| (y, x))")
             } else {
-                match usage.unwrap_kind {
-                    Some(UnwrapKind::Unwrap) => {
-                        if reverse {
-                            // In this case, no better suggestion is offered.
-                            return;
-                        }
-                        format!("{}.{}({}).unwrap().1", self_snip, method_name, pat_snip)
-                    },
-                    Some(UnwrapKind::QuestionMark) => {
-                        format!("{}.{}({})?.1", self_snip, method_name, pat_snip)
-                    },
-                    None => {
-                        format!("{}.{}({}).map(|x| x.1)", self_snip, method_name, pat_snip)
-                    },
-                }
+                format!("{self_snip}.split_once({pat_snip})")
+            }
+        },
+        IterUsageKind::Nth(1) => {
+            let (r, field) = if reverse { ("r", 0) } else { ("", 1) };
+
+            match usage.unwrap_kind {
+                Some(UnwrapKind::Unwrap) => {
+                    format!("{self_snip}.{r}split_once({pat_snip}).unwrap().{field}")
+                },
+                Some(UnwrapKind::QuestionMark) => {
+                    format!("{self_snip}.{r}split_once({pat_snip})?.{field}")
+                },
+                None => {
+                    format!("{self_snip}.{r}split_once({pat_snip}).map(|x| x.{field})")
+                },
             }
         },
+        IterUsageKind::Nth(_) => return,
     };
 
     span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
 }
 
+/// checks for
+///
+/// ```
+/// let mut iter = "a.b.c".splitn(2, '.');
+/// let a = iter.next();
+/// let b = iter.next();
+/// ```
+fn check_manual_split_once_indirect(
+    cx: &LateContext<'_>,
+    method_name: &str,
+    expr: &Expr<'_>,
+    self_arg: &Expr<'_>,
+    pat_arg: &Expr<'_>,
+) -> Option<()> {
+    let ctxt = expr.span.ctxt();
+    let mut parents = cx.tcx.hir().parent_iter(expr.hir_id);
+    if let (_, Node::Local(local)) = parents.next()?
+        && let PatKind::Binding(BindingAnnotation::Mutable, iter_binding_id, iter_ident, None) = local.pat.kind
+        && let (iter_stmt_id, Node::Stmt(_)) = parents.next()?
+        && let (_, Node::Block(enclosing_block)) = parents.next()?
+
+        && let mut stmts = enclosing_block
+            .stmts
+            .iter()
+            .skip_while(|stmt| stmt.hir_id != iter_stmt_id)
+            .skip(1)
+
+        && let first = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)?
+        && let second = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)?
+        && first.unwrap_kind == second.unwrap_kind
+        && first.name != second.name
+        && !local_used_after_expr(cx, iter_binding_id, second.init_expr)
+    {
+        let (r, lhs, rhs) = if method_name == "splitn" {
+            ("", first.name, second.name)
+        } else {
+            ("r", second.name, first.name)
+        };
+        let msg = format!("manual implementation of `{r}split_once`");
+
+        let mut app = Applicability::MachineApplicable;
+        let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
+        let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
+
+        span_lint_and_then(cx, MANUAL_SPLIT_ONCE, local.span, &msg, |diag| {
+            diag.span_label(first.span, "first usage here");
+            diag.span_label(second.span, "second usage here");
+
+            let unwrap = match first.unwrap_kind {
+                UnwrapKind::Unwrap => ".unwrap()",
+                UnwrapKind::QuestionMark => "?",
+            };
+            diag.span_suggestion_verbose(
+                local.span,
+                &format!("try `{r}split_once`"),
+                format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"),
+                app,
+            );
+
+            let remove_msg = format!("remove the `{iter_ident}` usages");
+            diag.span_suggestion(
+                first.span,
+                &remove_msg,
+                String::new(),
+                app,
+            );
+            diag.span_suggestion(
+                second.span,
+                &remove_msg,
+                String::new(),
+                app,
+            );
+        });
+    }
+
+    Some(())
+}
+
+#[derive(Debug)]
+struct IndirectUsage<'a> {
+    name: Symbol,
+    span: Span,
+    init_expr: &'a Expr<'a>,
+    unwrap_kind: UnwrapKind,
+}
+
+/// returns `Some(IndirectUsage)` for e.g.
+///
+/// ```ignore
+/// let name = binding.next()?;
+/// let name = binding.next().unwrap();
+/// ```
+fn indirect_usage<'tcx>(
+    cx: &LateContext<'tcx>,
+    stmt: &Stmt<'tcx>,
+    binding: HirId,
+    ctxt: SyntaxContext,
+) -> Option<IndirectUsage<'tcx>> {
+    if let StmtKind::Local(Local {
+        pat:
+            Pat {
+                kind: PatKind::Binding(BindingAnnotation::Unannotated, _, ident, None),
+                ..
+            },
+        init: Some(init_expr),
+        hir_id: local_hir_id,
+        ..
+    }) = stmt.kind
+    {
+        let mut path_to_binding = None;
+        expr_visitor(cx, |expr| {
+            if path_to_local_id(expr, binding) {
+                path_to_binding = Some(expr);
+            }
+
+            path_to_binding.is_none()
+        })
+        .visit_expr(init_expr);
+
+        let mut parents = cx.tcx.hir().parent_iter(path_to_binding?.hir_id);
+        let iter_usage = parse_iter_usage(cx, ctxt, &mut parents)?;
+
+        let (parent_id, _) = parents.find(|(_, node)| {
+            !matches!(
+                node,
+                Node::Expr(Expr {
+                    kind: ExprKind::Match(.., MatchSource::TryDesugar),
+                    ..
+                })
+            )
+        })?;
+
+        if let IterUsage {
+            kind: IterUsageKind::Nth(0),
+            unwrap_kind: Some(unwrap_kind),
+            ..
+        } = iter_usage
+        {
+            if parent_id == *local_hir_id {
+                return Some(IndirectUsage {
+                    name: ident.name,
+                    span: stmt.span,
+                    init_expr,
+                    unwrap_kind,
+                });
+            }
+        }
+    }
+
+    None
+}
+
+#[derive(Debug, Clone, Copy)]
 enum IterUsageKind {
-    Next,
-    Second,
+    Nth(u128),
     NextTuple,
-    RNextTuple,
 }
 
+#[derive(Debug, PartialEq)]
 enum UnwrapKind {
     Unwrap,
     QuestionMark,
 }
 
+#[derive(Debug)]
 struct IterUsage {
     kind: IterUsageKind,
     unwrap_kind: Option<UnwrapKind>,
@@ -128,7 +289,6 @@ fn parse_iter_usage<'tcx>(
     cx: &LateContext<'tcx>,
     ctxt: SyntaxContext,
     mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
-    reverse: bool,
 ) -> Option<IterUsage> {
     let (kind, span) = match iter.next() {
         Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
@@ -141,13 +301,7 @@ fn parse_iter_usage<'tcx>(
             let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
 
             match (name.ident.as_str(), args) {
-                ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
-                    if reverse {
-                        (IterUsageKind::Second, e.span)
-                    } else {
-                        (IterUsageKind::Next, e.span)
-                    }
-                },
+                ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span),
                 ("next_tuple", []) => {
                     return if_chain! {
                         if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
@@ -157,7 +311,7 @@ fn parse_iter_usage<'tcx>(
                         if subs.len() == 2;
                         then {
                             Some(IterUsage {
-                                kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple },
+                                kind: IterUsageKind::NextTuple,
                                 span: e.span,
                                 unwrap_kind: None
                             })
@@ -185,11 +339,7 @@ fn parse_iter_usage<'tcx>(
                                 }
                             }
                         };
-                        match if reverse { idx ^ 1 } else { idx } {
-                            0 => (IterUsageKind::Next, span),
-                            1 => (IterUsageKind::Second, span),
-                            _ => return None,
-                        }
+                        (IterUsageKind::Nth(idx), span)
                     } else {
                         return None;
                     }
@@ -238,86 +388,3 @@ fn parse_iter_usage<'tcx>(
         span,
     })
 }
-
-use super::NEEDLESS_SPLITN;
-
-pub(super) fn check_needless_splitn(
-    cx: &LateContext<'_>,
-    method_name: &str,
-    expr: &Expr<'_>,
-    self_arg: &Expr<'_>,
-    pat_arg: &Expr<'_>,
-    count: u128,
-) {
-    if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
-        return;
-    }
-    let ctxt = expr.span.ctxt();
-    let mut app = Applicability::MachineApplicable;
-    let (reverse, message) = if method_name == "splitn" {
-        (false, "unnecessary use of `splitn`")
-    } else {
-        (true, "unnecessary use of `rsplitn`")
-    };
-    if_chain! {
-        if count >= 2;
-        if check_iter(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), count);
-        then {
-            span_lint_and_sugg(
-                cx,
-                NEEDLESS_SPLITN,
-                expr.span,
-                message,
-                "try this",
-                format!(
-                    "{}.{}({})",
-                    snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0,
-                    if reverse {"rsplit"} else {"split"},
-                    snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0
-                ),
-                app,
-            );
-        }
-    }
-}
-
-fn check_iter<'tcx>(
-    cx: &LateContext<'tcx>,
-    ctxt: SyntaxContext,
-    mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
-    count: u128,
-) -> bool {
-    match iter.next() {
-        Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
-            let (name, args) = if let ExprKind::MethodCall(name, [_, args @ ..], _) = e.kind {
-                (name, args)
-            } else {
-                return false;
-            };
-            if_chain! {
-                if let Some(did) = cx.typeck_results().type_dependent_def_id(e.hir_id);
-                if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
-                then {
-                    match (name.ident.as_str(), args) {
-                        ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
-                            return true;
-                        },
-                        ("next_tuple", []) if count > 2 => {
-                            return true;
-                        },
-                        ("nth", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
-                            if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
-                                if count > idx + 1 {
-                                    return true;
-                                }
-                            }
-                        },
-                        _ =>  return false,
-                    }
-                }
-            }
-        },
-        _ => return false,
-    };
-    false
-}
index 1555758fc4ad825b6b013a7bb1014544123f6804..02b882e8b55e041bc14f7afc810a6451b6f24927 100644 (file)
@@ -5,6 +5,8 @@
 use clippy_utils::ty::{
     contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs,
 };
+use clippy_utils::{meets_msrv, msrvs};
+
 use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
 use rustc_errors::Applicability;
 use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
 use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
+use rustc_semver::RustcVersion;
 use rustc_span::{sym, Symbol};
 use std::cmp::max;
 
 use super::UNNECESSARY_TO_OWNED;
 
-pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) {
+pub fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    method_name: Symbol,
+    args: &'tcx [Expr<'tcx>],
+    msrv: Option<&RustcVersion>,
+) {
     if_chain! {
         if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
         if let [receiver] = args;
@@ -33,7 +42,7 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name:
                 if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) {
                     return;
                 }
-                if check_into_iter_call_arg(cx, expr, method_name, receiver) {
+                if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) {
                     return;
                 }
                 check_other_call_arg(cx, expr, method_name, receiver);
@@ -178,7 +187,13 @@ fn check_addr_of_expr(
 
 /// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
 /// call of a `to_owned`-like function is unnecessary.
-fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool {
+fn check_into_iter_call_arg(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    method_name: Symbol,
+    receiver: &Expr<'_>,
+    msrv: Option<&RustcVersion>,
+) -> bool {
     if_chain! {
         if let Some(parent) = get_parent_expr(cx, expr);
         if let Some(callee_def_id) = fn_def_id(cx, parent);
@@ -192,7 +207,7 @@ fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name:
             if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
                 return true;
             }
-            let cloned_or_copied = if is_copy(cx, item_ty) { "copied" } else { "cloned" };
+            let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) { "copied" } else { "cloned" };
             // The next suggestion may be incorrect because the removal of the `to_owned`-like
             // function could cause the iterator to hold a reference to a resource that is used
             // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
index aecfea9c141cfad801b75fedc45472f76d0be272..4b368d3ffae254abd04ba4727b7b30faed484ef3 100644 (file)
@@ -14,7 +14,7 @@
     (&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]),
     (&[Convention::StartsWith("from_")], &[SelfKind::No]),
     (&[Convention::StartsWith("into_")], &[SelfKind::Value]),
-    (&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]),
+    (&[Convention::StartsWith("is_")], &[SelfKind::RefMut, SelfKind::Ref, SelfKind::No]),
     (&[Convention::Eq("to_mut")], &[SelfKind::RefMut]),
     (&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]),
 
index d955fad7d41a2fb12295dc352f901dfa19c883bf..6860b60acbdb4612e144604db5ddd53b146e5b46 100644 (file)
@@ -361,7 +361,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
         // See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
         // FIXME: Find a better way to detect those cases.
         let lit_snip = match snippet_opt(cx, lit.span) {
-            Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip,
+            Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip,
             _ => return,
         };
 
index ac2f16b49e3f1cd3fc3bc560b8aac36d268735da..0d95329918984c68a74d541042995ff1a94ec28b 100644 (file)
@@ -44,7 +44,7 @@
     /// pub struct PubBaz;
     /// impl PubBaz {
     ///    fn private() {} // ok
-    ///    pub fn not_ptrivate() {} // missing #[inline]
+    ///    pub fn not_private() {} // missing #[inline]
     /// }
     ///
     /// impl Bar for PubBaz {
@@ -97,7 +97,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) {
                 let attrs = cx.tcx.hir().attrs(it.hir_id());
                 check_missing_inline_attrs(cx, attrs, it.span, desc);
             },
-            hir::ItemKind::Trait(ref _is_auto, ref _unsafe, ref _generics, _bounds, trait_items) => {
+            hir::ItemKind::Trait(ref _is_auto, ref _unsafe, _generics, _bounds, trait_items) => {
                 // note: we need to check if the trait is exported so we can't use
                 // `LateLintPass::check_trait_item` here.
                 for tit in trait_items {
index a8a8d174a823e309f088517808464b9a6d8ad4d8..95395e2e136d9f18024a356d0ea7f3dc1f4f6bd9 100644 (file)
@@ -53,7 +53,7 @@ fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     false
 }
 
-fn suggession_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
+fn suggesstion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
     if let ExprKind::Binary(ref op, left, right) = expr.kind {
         if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) {
             let op_snippet = match op.node {
@@ -75,7 +75,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
                 expr.span,
                 "use of bitwise operator instead of lazy operator between booleans",
                 |diag| {
-                    if let Some(sugg) = suggession_snippet(cx, expr) {
+                    if let Some(sugg) = suggesstion_snippet(cx, expr) {
                         diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable);
                     }
                 },
index 9957afcbf04aaee1d7daaa305f1a89e797d335e9..b70871b38beab178f63735330657ab8388e2f355 100644 (file)
@@ -1,10 +1,14 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::path_to_local;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::visitors::{expr_visitor, is_local_used};
-use rustc_errors::Applicability;
+use clippy_utils::ty::needs_ordered_drop;
+use clippy_utils::visitors::{expr_visitor, expr_visitor_no_bodies, is_local_used};
+use rustc_errors::{Applicability, MultiSpan};
 use rustc_hir::intravisit::Visitor;
-use rustc_hir::{Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind};
+use rustc_hir::{
+    BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt,
+    StmtKind,
+};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::Span;
@@ -73,6 +77,31 @@ fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) ->
     seen
 }
 
+fn contains_let(cond: &Expr<'_>) -> bool {
+    let mut seen = false;
+    expr_visitor_no_bodies(|expr| {
+        if let ExprKind::Let(_) = expr.kind {
+            seen = true;
+        }
+
+        !seen
+    })
+    .visit_expr(cond);
+
+    seen
+}
+
+fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
+    let StmtKind::Local(local) = stmt.kind else { return false };
+    !local.pat.walk_short(|pat| {
+        if let PatKind::Binding(.., None) = pat.kind {
+            !needs_ordered_drop(cx, cx.typeck_results().pat_ty(pat))
+        } else {
+            true
+        }
+    })
+}
+
 #[derive(Debug)]
 struct LocalAssign {
     lhs_id: HirId,
@@ -187,11 +216,14 @@ fn first_usage<'tcx>(
     local_stmt_id: HirId,
     block: &'tcx Block<'tcx>,
 ) -> Option<Usage<'tcx>> {
+    let significant_drop = needs_ordered_drop(cx, cx.typeck_results().node_type(binding_id));
+
     block
         .stmts
         .iter()
         .skip_while(|stmt| stmt.hir_id != local_stmt_id)
         .skip(1)
+        .take_while(|stmt| !significant_drop || !stmt_needs_ordered_drop(cx, stmt))
         .find(|&stmt| is_local_used(cx, stmt, binding_id))
         .and_then(|stmt| match stmt.kind {
             StmtKind::Expr(expr) => Some(Usage {
@@ -235,12 +267,15 @@ fn check<'tcx>(
     match usage.expr.kind {
         ExprKind::Assign(..) => {
             let assign = LocalAssign::new(cx, usage.expr, binding_id)?;
+            let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]);
+            msg_span.push_span_label(local_stmt.span, "created here");
+            msg_span.push_span_label(assign.span, "initialised here");
 
             span_lint_and_then(
                 cx,
                 NEEDLESS_LATE_INIT,
-                local_stmt.span,
-                "unneeded late initalization",
+                msg_span,
+                "unneeded late initialization",
                 |diag| {
                     diag.tool_only_span_suggestion(
                         local_stmt.span,
@@ -258,14 +293,14 @@ fn check<'tcx>(
                 },
             );
         },
-        ExprKind::If(_, then_expr, Some(else_expr)) => {
+        ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => {
             let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?;
 
             span_lint_and_then(
                 cx,
                 NEEDLESS_LATE_INIT,
                 local_stmt.span,
-                "unneeded late initalization",
+                "unneeded late initialization",
                 |diag| {
                     diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
 
@@ -296,7 +331,7 @@ fn check<'tcx>(
                 cx,
                 NEEDLESS_LATE_INIT,
                 local_stmt.span,
-                "unneeded late initalization",
+                "unneeded late initialization",
                 |diag| {
                     diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
 
@@ -333,12 +368,11 @@ fn check<'tcx>(
 impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit {
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
         let mut parents = cx.tcx.hir().parent_iter(local.hir_id);
-
         if_chain! {
             if let Local {
                 init: None,
                 pat: &Pat {
-                    kind: PatKind::Binding(_, binding_id, _, None),
+                    kind: PatKind::Binding(BindingAnnotation::Unannotated, binding_id, _, None),
                     ..
                 },
                 source: LocalSource::Normal,
index 96c00c205ff2e9401e0589e1a9786fb47c2c2356..2f733f221d572250866fd51150826a871b83a504 100644 (file)
@@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
         if let hir::ItemKind::Impl(hir::Impl {
             of_trait: None,
-            ref generics,
+            generics,
             self_ty: impl_self_ty,
             items,
             ..
index 0d0c88b02c78b6d5adabe7ed4a6b8bc41b599507..e3bc40c4b49148962fe3cf94d316c42de3e7220f 100644 (file)
@@ -197,7 +197,7 @@ fn check_ident(&mut self, ident: Ident) {
         if interned_name.chars().any(char::is_uppercase) {
             return;
         }
-        if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
+        if interned_name.chars().all(|c| c.is_ascii_digit() || c == '_') {
             span_lint(
                 self.0.cx,
                 JUST_UNDERSCORES_AND_DIGITS,
index c19cea661042d4c81a34572ec178f25aed6839d3..e8532db4f711dbc658d9862f8ff6b4851253b5c8 100644 (file)
@@ -25,7 +25,7 @@
     ///
     /// ### Known problems
     /// The actual meaning can be the intended one. `\x00` can be used in these
-    /// cases to be unambigious.
+    /// cases to be unambiguous.
     ///
     /// The lint does not trigger for format strings in `print!()`, `write!()`
     /// and friends since the string is already preprocessed when Clippy lints
index 8e61f2347767dfa85bfaa47754b4002f69dec360..beb812793f81ccfe5d40a44ef3c6840051d3bc6a 100644 (file)
@@ -1,6 +1,7 @@
 use std::collections::VecDeque;
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_lint_allowed;
 use itertools::{izip, Itertools};
 use rustc_ast::{walk_list, Label, Mutability};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -8,7 +9,7 @@
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
-use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
+use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor};
 use rustc_hir::{
     Arm, Block, Body, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path, PathSegment,
     QPath, Stmt, StmtKind, TyKind, UnOp,
@@ -33,6 +34,9 @@
     /// and the assigned variables are also only in recursion, it is useless.
     ///
     /// ### Known problems
+    /// Too many code paths in the linting code are currently untested and prone to produce false
+    /// positives or are prone to have performance implications.
+    ///
     /// In some cases, this would not catch all useless arguments.
     ///
     /// ```rust
@@ -85,7 +89,7 @@
     /// ```
     #[clippy::version = "1.60.0"]
     pub ONLY_USED_IN_RECURSION,
-    complexity,
+    nursery,
     "arguments that is only used in recursion can be removed"
 }
 declare_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
@@ -100,6 +104,9 @@ fn check_fn(
         _: Span,
         id: HirId,
     ) {
+        if is_lint_allowed(cx, ONLY_USED_IN_RECURSION, id) {
+            return;
+        }
         if let FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) = kind {
             let def_id = id.owner.to_def_id();
             let data = cx.tcx.def_path(def_id).data;
@@ -145,7 +152,8 @@ fn check_fn(
                 is_method: matches!(kind, FnKind::Method(..)),
                 has_self,
                 ty_res,
-                ty_ctx: cx.tcx,
+                tcx: cx.tcx,
+                visited_exprs: FxHashSet::default(),
             };
 
             visitor.visit_expr(&body.value);
@@ -206,19 +214,13 @@ fn check_fn(
 }
 
 pub fn is_primitive(ty: Ty<'_>) -> bool {
-    match ty.kind() {
-        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
-        ty::Ref(_, t, _) => is_primitive(*t),
-        _ => false,
-    }
+    let ty = ty.peel_refs();
+    ty.is_primitive() || ty.is_str()
 }
 
 pub fn is_array(ty: Ty<'_>) -> bool {
-    match ty.kind() {
-        ty::Array(..) | ty::Slice(..) => true,
-        ty::Ref(_, t, _) => is_array(*t),
-        _ => false,
-    }
+    let ty = ty.peel_refs();
+    ty.is_array() || ty.is_array_slice()
 }
 
 /// This builds the graph of side effect.
@@ -250,40 +252,30 @@ pub struct SideEffectVisit<'tcx> {
     is_method: bool,
     has_self: bool,
     ty_res: &'tcx TypeckResults<'tcx>,
-    ty_ctx: TyCtxt<'tcx>,
+    tcx: TyCtxt<'tcx>,
+    visited_exprs: FxHashSet<HirId>,
 }
 
 impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
-    fn visit_block(&mut self, b: &'tcx Block<'tcx>) {
-        b.stmts.iter().for_each(|stmt| {
-            self.visit_stmt(stmt);
-            self.ret_vars.clear();
-        });
-        walk_list!(self, visit_expr, b.expr);
-    }
-
     fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) {
         match s.kind {
             StmtKind::Local(Local {
                 pat, init: Some(init), ..
             }) => {
                 self.visit_pat_expr(pat, init, false);
-                self.ret_vars.clear();
             },
-            StmtKind::Item(i) => {
-                let item = self.ty_ctx.hir().item(i);
-                self.visit_item(item);
-                self.ret_vars.clear();
-            },
-            StmtKind::Expr(e) | StmtKind::Semi(e) => {
-                self.visit_expr(e);
-                self.ret_vars.clear();
+            StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => {
+                walk_stmt(self, s);
             },
             StmtKind::Local(_) => {},
         }
+        self.ret_vars.clear();
     }
 
     fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
+        if !self.visited_exprs.insert(ex.hir_id) {
+            return;
+        }
         match ex.kind {
             ExprKind::Array(exprs) | ExprKind::Tup(exprs) => {
                 self.ret_vars = exprs
@@ -307,7 +299,7 @@ fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
             ExprKind::Match(expr, arms, _) => self.visit_match(expr, arms),
             // since analysing the closure is not easy, just set all variables in it to side-effect
             ExprKind::Closure(_, _, body_id, _, _) => {
-                let body = self.ty_ctx.hir().body(body_id);
+                let body = self.tcx.hir().body(body_id);
                 self.visit_body(body);
                 let vars = std::mem::take(&mut self.ret_vars);
                 self.add_side_effect(vars);
index c9f807f2aa3aadfe756369ac9e889dc8cafaf182..ea5a8f0858b66aa14d084b55a585fa8ae7950be0 100644 (file)
@@ -78,7 +78,7 @@ fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
 
 /// A struct containing information about occurrences of the
 /// `if let Some(..) = .. else` construct that this lint detects.
-struct OptionIfLetElseOccurence {
+struct OptionIfLetElseOccurrence {
     option: String,
     method_sugg: String,
     some_expr: String,
@@ -100,9 +100,9 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo
 }
 
 /// If this expression is the option if let/else construct we're detecting, then
-/// this function returns an `OptionIfLetElseOccurence` struct with details if
+/// this function returns an `OptionIfLetElseOccurrence` struct with details if
 /// this construct is found, or None if this construct is not found.
-fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurence> {
+fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurrence> {
     if_chain! {
         if !expr.span.from_expansion(); // Don't lint macros, because it behaves weirdly
         if !in_constant(cx, expr.hir_id);
@@ -154,7 +154,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
                     }
                 }
             }
-            Some(OptionIfLetElseOccurence {
+            Some(OptionIfLetElseOccurrence {
                 option: format_option_in_sugg(cx, cond_expr, 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, "..")),
index 48a2666a2e0cef399b2cfeb4a5fd3e808d9b1655..c35eeeac67a35c0567501ba3b38e7dcd1bc61f5e 100644 (file)
@@ -3,6 +3,7 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::expr_sig;
+use clippy_utils::visitors::contains_unsafe_block;
 use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths};
 use if_chain::if_chain;
 use rustc_errors::{Applicability, MultiSpan};
@@ -10,9 +11,9 @@
 use rustc_hir::hir_id::HirIdMap;
 use rustc_hir::intravisit::{walk_expr, Visitor};
 use rustc_hir::{
-    self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArg,
+    self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg,
     ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn,
-    TraitItem, TraitItemKind, TyKind,
+    TraitItem, TraitItemKind, TyKind, Unsafety,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// This lint checks for functions that take immutable
-    /// references and return mutable ones.
+    /// This lint checks for functions that take immutable references and return
+    /// mutable ones. This will not trigger if no unsafe code exists as there
+    /// are multiple safe functions which will do this transformation
+    ///
+    /// To be on the conservative side, if there's at least one mutable
+    /// reference with the output lifetime, this lint will not trigger.
     ///
     /// ### Why is this bad?
-    /// This is trivially unsound, as one can create two
-    /// mutable references from the same (immutable!) source.
-    /// This [error](https://github.com/rust-lang/rust/issues/39465)
-    /// actually lead to an interim Rust release 1.15.1.
+    /// Creating a mutable reference which can be repeatably derived from an
+    /// immutable reference is unsound as it allows creating multiple live
+    /// mutable references to the same object.
+    ///
+    /// This [error](https://github.com/rust-lang/rust/issues/39465) actually
+    /// lead to an interim Rust release 1.15.1.
     ///
     /// ### Known problems
-    /// To be on the conservative side, if there's at least one
-    /// mutable reference with the output lifetime, this lint will not trigger.
-    /// In practice, this case is unlikely anyway.
+    /// This pattern is used by memory allocators to allow allocating multiple
+    /// objects while returning mutable references to each one. So long as
+    /// different mutable references are returned each time such a function may
+    /// be safe.
     ///
     /// ### Example
     /// ```ignore
@@ -145,7 +153,7 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>
                 return;
             }
 
-            check_mut_from_ref(cx, sig.decl);
+            check_mut_from_ref(cx, sig, None);
             for arg in check_fn_args(
                 cx,
                 cx.tcx.fn_sig(item.def_id).skip_binder().inputs(),
@@ -170,10 +178,10 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>
     fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
         let hir = cx.tcx.hir();
         let mut parents = hir.parent_iter(body.value.hir_id);
-        let (item_id, decl, is_trait_item) = match parents.next() {
+        let (item_id, sig, is_trait_item) = match parents.next() {
             Some((_, Node::Item(i))) => {
                 if let ItemKind::Fn(sig, ..) = &i.kind {
-                    (i.def_id, sig.decl, false)
+                    (i.def_id, sig, false)
                 } else {
                     return;
                 }
@@ -185,14 +193,14 @@ fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
                     return;
                 }
                 if let ImplItemKind::Fn(sig, _) = &i.kind {
-                    (i.def_id, sig.decl, false)
+                    (i.def_id, sig, false)
                 } else {
                     return;
                 }
             },
             Some((_, Node::TraitItem(i))) => {
                 if let TraitItemKind::Fn(sig, _) = &i.kind {
-                    (i.def_id, sig.decl, true)
+                    (i.def_id, sig, true)
                 } else {
                     return;
                 }
@@ -200,7 +208,8 @@ fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
             _ => return,
         };
 
-        check_mut_from_ref(cx, decl);
+        check_mut_from_ref(cx, sig, Some(body));
+        let decl = sig.decl;
         let sig = cx.tcx.fn_sig(item_id).skip_binder();
         let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params)
             .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
@@ -473,31 +482,31 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
         })
 }
 
-fn check_mut_from_ref(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
-    if let FnRetTy::Return(ty) = decl.output {
-        if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) {
-            let mut immutables = vec![];
-            for (_, mutbl, argspan) in decl
-                .inputs
-                .iter()
-                .filter_map(get_rptr_lm)
-                .filter(|&(lt, _, _)| lt.name == out.name)
-            {
-                if mutbl == Mutability::Mut {
-                    return;
-                }
-                immutables.push(argspan);
-            }
-            if immutables.is_empty() {
-                return;
-            }
+fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) {
+    if let FnRetTy::Return(ty) = sig.decl.output
+        && let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty)
+    {
+        let args: Option<Vec<_>> = sig
+            .decl
+            .inputs
+            .iter()
+            .filter_map(get_rptr_lm)
+            .filter(|&(lt, _, _)| lt.name == out.name)
+            .map(|(_, mutability, span)| (mutability == Mutability::Not).then(|| span))
+            .collect();
+        if let Some(args) = args
+            && !args.is_empty()
+            && body.map_or(true, |body| {
+                sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, &body.value)
+            })
+        {
             span_lint_and_then(
                 cx,
                 MUT_FROM_REF,
                 ty.span,
                 "mutable borrow from immutable input(s)",
                 |diag| {
-                    let ms = MultiSpan::from_spans(immutables);
+                    let ms = MultiSpan::from_spans(args);
                     diag.span_note(ms, "immutable borrow here");
                 },
             );
diff --git a/src/tools/clippy/clippy_lints/src/pub_use.rs b/src/tools/clippy/clippy_lints/src/pub_use.rs
new file mode 100644 (file)
index 0000000..9d2b0ce
--- /dev/null
@@ -0,0 +1,56 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use rustc_ast::ast::{Item, ItemKind, VisibilityKind};
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Restricts the usage of `pub use ...`
+    ///
+    /// ### Why is this bad?
+    ///
+    /// `pub use` is usually fine, but a project may wish to limit `pub use` instances to prevent
+    /// unintentional exports or to encourage placing exported items directly in public modules
+    ///
+    /// ### Example
+    /// ```rust
+    /// pub mod outer {
+    ///     mod inner {
+    ///         pub struct Test {}
+    ///     }
+    ///     pub use inner::Test;
+    /// }
+    ///
+    /// use outer::Test;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// pub mod outer {
+    ///     pub struct Test {}
+    /// }
+    ///
+    /// use outer::Test;
+    /// ```
+    #[clippy::version = "1.62.0"]
+    pub PUB_USE,
+    restriction,
+    "restricts the usage of `pub use`"
+}
+declare_lint_pass!(PubUse => [PUB_USE]);
+
+impl EarlyLintPass for PubUse {
+    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+        if let ItemKind::Use(_) = item.kind &&
+            let VisibilityKind::Public = item.vis.kind {
+                span_lint_and_help(
+                    cx,
+                    PUB_USE,
+                    item.span,
+                    "using `pub use`",
+                    None,
+                    "move the exported item to a public module instead",
+                );
+            }
+    }
+}
index e2e2400f8e267ae8e398778ed0de72ba17b1bdbb..323326381d4079149ce2591abd3c6a402b4e453b 100644 (file)
@@ -1,10 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_errors::Applicability;
+use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::def_id::CRATE_DEF_ID;
+use rustc_span::hygiene::MacroKind;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -43,8 +45,11 @@ pub struct RedundantPubCrate {
 
 impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
-        if cx.tcx.visibility(item.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()) {
-            if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false) {
+        if_chain! {
+            if cx.tcx.visibility(item.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id());
+            if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false);
+            if is_not_macro_export(item);
+            then {
                 let span = item.span.with_hi(item.ident.span.hi());
                 let descr = cx.tcx.def_kind(item.def_id).descr(item.def_id.to_def_id());
                 span_lint_and_then(
@@ -75,3 +80,15 @@ fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
         }
     }
 }
+
+fn is_not_macro_export<'tcx>(item: &'tcx Item<'tcx>) -> bool {
+    if let ItemKind::Use(path, _) = item.kind {
+        if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = path.res {
+            return false;
+        }
+    } else if let ItemKind::Macro(..) = item.kind {
+        return false;
+    }
+
+    true
+}
diff --git a/src/tools/clippy/clippy_lints/src/renamed_lints.rs b/src/tools/clippy/clippy_lints/src/renamed_lints.rs
new file mode 100644 (file)
index 0000000..bfc0311
--- /dev/null
@@ -0,0 +1,39 @@
+// This file is managed by `cargo dev rename_lint`. Prefer using that when possible.
+
+#[rustfmt::skip]
+pub static RENAMED_LINTS: &[(&str, &str)] = &[
+    ("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"),
+    ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"),
+    ("clippy::box_vec", "clippy::box_collection"),
+    ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"),
+    ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"),
+    ("clippy::disallowed_method", "clippy::disallowed_methods"),
+    ("clippy::disallowed_type", "clippy::disallowed_types"),
+    ("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"),
+    ("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"),
+    ("clippy::identity_conversion", "clippy::useless_conversion"),
+    ("clippy::if_let_some_result", "clippy::match_result_ok"),
+    ("clippy::new_without_default_derive", "clippy::new_without_default"),
+    ("clippy::option_and_then_some", "clippy::bind_instead_of_map"),
+    ("clippy::option_expect_used", "clippy::expect_used"),
+    ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"),
+    ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"),
+    ("clippy::option_unwrap_used", "clippy::unwrap_used"),
+    ("clippy::ref_in_deref", "clippy::needless_borrow"),
+    ("clippy::result_expect_used", "clippy::expect_used"),
+    ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"),
+    ("clippy::result_unwrap_used", "clippy::unwrap_used"),
+    ("clippy::single_char_push_str", "clippy::single_char_add_str"),
+    ("clippy::stutter", "clippy::module_name_repetitions"),
+    ("clippy::to_string_in_display", "clippy::recursive_format_impl"),
+    ("clippy::zero_width_space", "clippy::invisible_characters"),
+    ("clippy::drop_bounds", "drop_bounds"),
+    ("clippy::into_iter_on_array", "array_into_iter"),
+    ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"),
+    ("clippy::invalid_ref", "invalid_value"),
+    ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"),
+    ("clippy::panic_params", "non_fmt_panics"),
+    ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"),
+    ("clippy::unknown_clippy_lints", "unknown_lints"),
+    ("clippy::unused_label", "unused_labels"),
+];
index a01e2f2db3afb2ee9c52ff3e6645c37ebaef4fe6..f63925a2f1438ea8a71e6f500f1a5c11a70cd55e 100644 (file)
@@ -51,114 +51,110 @@ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
         let mut map = FxHashMap::<Res, ExistingName>::default();
 
         for id in cx.tcx.hir().items() {
-            if !matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl) {
-                continue;
-            }
-
-            let item = cx.tcx.hir().item(id);
-            if let ItemKind::Impl(Impl {
-                items,
-                of_trait,
-                self_ty,
-                ..
-            }) = &item.kind
+            if matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl)
+                && let item = cx.tcx.hir().item(id)
+                && let ItemKind::Impl(Impl {
+                    items,
+                    of_trait,
+                    self_ty,
+                    ..
+                }) = &item.kind
+                && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind
             {
-                if let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind {
-                    if !map.contains_key(res) {
-                        map.insert(
-                            *res,
-                            ExistingName {
-                                impl_methods: BTreeMap::new(),
-                                trait_methods: BTreeMap::new(),
-                            },
-                        );
-                    }
-                    let existing_name = map.get_mut(res).unwrap();
-
-                    match of_trait {
-                        Some(trait_ref) => {
-                            let mut methods_in_trait: BTreeSet<Symbol> = if_chain! {
-                                if let Some(Node::TraitRef(TraitRef { path, .. })) =
-                                    cx.tcx.hir().find(trait_ref.hir_ref_id);
-                                if let Res::Def(DefKind::Trait, did) = path.res;
-                                then{
-                                    // FIXME: if
-                                    // `rustc_middle::ty::assoc::AssocItems::items` is public,
-                                    // we can iterate its keys instead of `in_definition_order`,
-                                    // which's more efficient
-                                    cx.tcx
-                                        .associated_items(did)
-                                        .in_definition_order()
-                                        .filter(|assoc_item| {
-                                            matches!(assoc_item.kind, AssocKind::Fn)
-                                        })
-                                        .map(|assoc_item| assoc_item.name)
-                                        .collect()
-                                }else{
-                                    BTreeSet::new()
-                                }
-                            };
-
-                            let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| {
-                                if let Some(impl_span) = existing_name.impl_methods.get(&method_name) {
-                                    span_lint_and_then(
-                                        cx,
-                                        SAME_NAME_METHOD,
-                                        *impl_span,
-                                        "method's name is the same as an existing method in a trait",
-                                        |diag| {
-                                            diag.span_note(
-                                                trait_method_span,
-                                                &format!("existing `{}` defined here", method_name),
-                                            );
-                                        },
-                                    );
-                                }
-                                if let Some(v) = existing_name.trait_methods.get_mut(&method_name) {
-                                    v.push(trait_method_span);
-                                } else {
-                                    existing_name.trait_methods.insert(method_name, vec![trait_method_span]);
-                                }
-                            };
+                if !map.contains_key(res) {
+                    map.insert(
+                        *res,
+                        ExistingName {
+                            impl_methods: BTreeMap::new(),
+                            trait_methods: BTreeMap::new(),
+                        },
+                    );
+                }
+                let existing_name = map.get_mut(res).unwrap();
 
-                            for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
-                                matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
-                            }) {
-                                let method_name = impl_item_ref.ident.name;
-                                methods_in_trait.remove(&method_name);
-                                check_trait_method(method_name, impl_item_ref.span);
+                match of_trait {
+                    Some(trait_ref) => {
+                        let mut methods_in_trait: BTreeSet<Symbol> = if_chain! {
+                            if let Some(Node::TraitRef(TraitRef { path, .. })) =
+                                cx.tcx.hir().find(trait_ref.hir_ref_id);
+                            if let Res::Def(DefKind::Trait, did) = path.res;
+                            then{
+                                // FIXME: if
+                                // `rustc_middle::ty::assoc::AssocItems::items` is public,
+                                // we can iterate its keys instead of `in_definition_order`,
+                                // which's more efficient
+                                cx.tcx
+                                    .associated_items(did)
+                                    .in_definition_order()
+                                    .filter(|assoc_item| {
+                                        matches!(assoc_item.kind, AssocKind::Fn)
+                                    })
+                                    .map(|assoc_item| assoc_item.name)
+                                    .collect()
+                            }else{
+                                BTreeSet::new()
                             }
+                        };
 
-                            for method_name in methods_in_trait {
-                                check_trait_method(method_name, item.span);
+                        let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| {
+                            if let Some(impl_span) = existing_name.impl_methods.get(&method_name) {
+                                span_lint_and_then(
+                                    cx,
+                                    SAME_NAME_METHOD,
+                                    *impl_span,
+                                    "method's name is the same as an existing method in a trait",
+                                    |diag| {
+                                        diag.span_note(
+                                            trait_method_span,
+                                            &format!("existing `{}` defined here", method_name),
+                                        );
+                                    },
+                                );
                             }
-                        },
-                        None => {
-                            for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
-                                matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
-                            }) {
-                                let method_name = impl_item_ref.ident.name;
-                                let impl_span = impl_item_ref.span;
-                                if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) {
-                                    span_lint_and_then(
-                                        cx,
-                                        SAME_NAME_METHOD,
-                                        impl_span,
-                                        "method's name is the same as an existing method in a trait",
-                                        |diag| {
-                                            // TODO should we `span_note` on every trait?
-                                            // iterate on trait_spans?
-                                            diag.span_note(
-                                                trait_spans[0],
-                                                &format!("existing `{}` defined here", method_name),
-                                            );
-                                        },
-                                    );
-                                }
-                                existing_name.impl_methods.insert(method_name, impl_span);
+                            if let Some(v) = existing_name.trait_methods.get_mut(&method_name) {
+                                v.push(trait_method_span);
+                            } else {
+                                existing_name.trait_methods.insert(method_name, vec![trait_method_span]);
                             }
-                        },
-                    }
+                        };
+
+                        for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
+                            matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
+                        }) {
+                            let method_name = impl_item_ref.ident.name;
+                            methods_in_trait.remove(&method_name);
+                            check_trait_method(method_name, impl_item_ref.span);
+                        }
+
+                        for method_name in methods_in_trait {
+                            check_trait_method(method_name, item.span);
+                        }
+                    },
+                    None => {
+                        for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
+                            matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
+                        }) {
+                            let method_name = impl_item_ref.ident.name;
+                            let impl_span = impl_item_ref.span;
+                            if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) {
+                                span_lint_and_then(
+                                    cx,
+                                    SAME_NAME_METHOD,
+                                    impl_span,
+                                    "method's name is the same as an existing method in a trait",
+                                    |diag| {
+                                        // TODO should we `span_note` on every trait?
+                                        // iterate on trait_spans?
+                                        diag.span_note(
+                                            trait_spans[0],
+                                            &format!("existing `{}` defined here", method_name),
+                                        );
+                                    },
+                                );
+                            }
+                            existing_name.impl_methods.insert(method_name, impl_span);
+                        }
+                    },
                 }
             }
         }
index bcd28b429784a488520f6107255ab106f23b7ecf..a6c685df721d6fac310117306cb98cd2b46f9a96 100644 (file)
@@ -9,15 +9,25 @@
 declare_clippy_lint! {
     /// ### What it does
     /// When sorting primitive values (integers, bools, chars, as well
-    /// as arrays, slices, and tuples of such items), it is better to
+    /// as arrays, slices, and tuples of such items), it is typically better to
     /// use an unstable sort than a stable sort.
     ///
     /// ### Why is this bad?
-    /// Using a stable sort consumes more memory and cpu cycles. Because
-    /// values which compare equal are identical, preserving their
+    /// Typically, using a stable sort consumes more memory and cpu cycles.
+    /// Because values which compare equal are identical, preserving their
     /// relative order (the guarantee that a stable sort provides) means
     /// nothing, while the extra costs still apply.
     ///
+    /// ### Known problems
+    ///
+    /// As pointed out in
+    /// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241),
+    /// a stable sort can instead be significantly faster for certain scenarios
+    /// (eg. when a sorted vector is extended with new data and resorted).
+    ///
+    /// For more information and benchmarking results, please refer to the
+    /// issue linked above.
+    ///
     /// ### Example
     /// ```rust
     /// let mut vec = vec![2, 1, 3];
@@ -30,7 +40,7 @@
     /// ```
     #[clippy::version = "1.47.0"]
     pub STABLE_SORT_PRIMITIVE,
-    perf,
+    pedantic,
     "use of sort() when sort_unstable() is equivalent"
 }
 
@@ -126,7 +136,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
                         Applicability::MachineApplicable,
                     );
                     diag.note(
-                        "an unstable sort would perform faster without any observable difference for this data type",
+                        "an unstable sort typically performs faster without any observable difference for this data type",
                     );
                 },
             );
index 3573f632a3671e6adde5cd641030f8beb9d85e6e..7c196ccaa8ccd2fb610f430e510b4a6aaa918314 100644 (file)
@@ -5,6 +5,7 @@
 use clippy_utils::{peel_blocks, SpanlessEq};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
+use rustc_hir::def_id::DefId;
 use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -451,3 +452,58 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
         }
     }
 }
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Warns about calling `str::trim` (or variants) before `str::split_whitespace`.
+    ///
+    /// ### Why is this bad?
+    /// `split_whitespace` already ignores leading and trailing whitespace.
+    ///
+    /// ### Example
+    /// ```rust
+    /// " A B C ".trim().split_whitespace();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// " A B C ".split_whitespace();
+    /// ```
+    #[clippy::version = "1.62.0"]
+    pub TRIM_SPLIT_WHITESPACE,
+    style,
+    "using `str::trim()` or alike before `str::split_whitespace`"
+}
+declare_lint_pass!(TrimSplitWhitespace => [TRIM_SPLIT_WHITESPACE]);
+
+impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
+        let tyckres = cx.typeck_results();
+        if_chain! {
+            if let ExprKind::MethodCall(path, [split_recv], split_ws_span) = expr.kind;
+            if path.ident.name == sym!(split_whitespace);
+            if let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id);
+            if cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id);
+            if let ExprKind::MethodCall(path, [_trim_recv], trim_span) = split_recv.kind;
+            if let trim_fn_name @ ("trim" | "trim_start" | "trim_end") = path.ident.name.as_str();
+            if let Some(trim_def_id) = tyckres.type_dependent_def_id(split_recv.hir_id);
+            if is_one_of_trim_diagnostic_items(cx, trim_def_id);
+            then {
+                span_lint_and_sugg(
+                    cx,
+                    TRIM_SPLIT_WHITESPACE,
+                    trim_span.with_hi(split_ws_span.lo()),
+                    &format!("found call to `str::{}` before `str::split_whitespace`", trim_fn_name),
+                    &format!("remove `{}()`", trim_fn_name),
+                    String::new(),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+    }
+}
+
+fn is_one_of_trim_diagnostic_items(cx: &LateContext<'_>, trim_def_id: DefId) -> bool {
+    cx.tcx.is_diagnostic_item(sym::str_trim, trim_def_id)
+        || cx.tcx.is_diagnostic_item(sym::str_trim_start, trim_def_id)
+        || cx.tcx.is_diagnostic_item(sym::str_trim_end, trim_def_id)
+}
index b5dd27ff80de4052ea5960c26d63cb64e6f76610..c4c1aa11004acbc523d2ffe22a25c4148e96f9ff 100644 (file)
@@ -550,7 +550,7 @@ fn ident_difference_expr_with_base_location(
     // IdentIter, then the output of this function will be almost always be correct
     // in practice.
     //
-    // If it turns out that problematic cases are more prelavent than we assume,
+    // If it turns out that problematic cases are more prevalent than we assume,
     // then we should be able to change this function to do the correct traversal,
     // without needing to change the rest of the code.
 
index 3d1b2ee925bcebfd9d5511895d51e7ebf1f8c987..78e388a49af1d470291e93a36958e66e6c2c6572 100644 (file)
@@ -13,6 +13,7 @@
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::Span;
+use std::fmt::Write as _;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -34,7 +35,7 @@
     /// ```
     #[clippy::version = "1.38.0"]
     pub TYPE_REPETITION_IN_BOUNDS,
-    pedantic,
+    nursery,
     "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
 }
 
@@ -64,7 +65,7 @@
     /// ```
     #[clippy::version = "1.47.0"]
     pub TRAIT_DUPLICATION_IN_BOUNDS,
-    pedantic,
+    nursery,
     "Check if the same trait bounds are specified twice during a function declaration"
 }
 
@@ -182,19 +183,19 @@ impl Eq for SpanlessTy<'_, '_> {}
                     for b in v.iter() {
                         if let GenericBound::Trait(ref poly_trait_ref, _) = b {
                             let path = &poly_trait_ref.trait_ref.path;
-                            hint_string.push_str(&format!(
+                            let _ = write!(hint_string,
                                 " {} +",
                                 snippet_with_applicability(cx, path.span, "..", &mut applicability)
-                            ));
+                            );
                         }
                     }
                     for b in p.bounds.iter() {
                         if let GenericBound::Trait(ref poly_trait_ref, _) = b {
                             let path = &poly_trait_ref.trait_ref.path;
-                            hint_string.push_str(&format!(
+                            let _ = write!(hint_string,
                                 " {} +",
                                 snippet_with_applicability(cx, path.span, "..", &mut applicability)
-                            ));
+                            );
                         }
                     }
                     hint_string.truncate(hint_string.len() - 2);
@@ -241,7 +242,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
                         );
                     }
                     else {
-                        trait_resolutions_direct.push((res_where, span_where))
+                        trait_resolutions_direct.push((res_where, span_where));
                     }
                 }
             }
index f359b606e4548b1736876e4fc3074ed6ace86ff5..0cbf5ccefa6d89ea6435cfc3a7647c81fee84e9e 100644 (file)
@@ -1,10 +1,9 @@
 use clippy_utils::last_path_segment;
 use clippy_utils::source::snippet;
-use clippy_utils::ty::is_normalizable;
 use if_chain::if_chain;
 use rustc_hir::{Expr, GenericArg, QPath, TyKind};
 use rustc_lint::LateContext;
-use rustc_middle::ty::{self, cast::CastKind, Ty};
+use rustc_middle::ty::{cast::CastKind, Ty};
 use rustc_span::DUMMY_SP;
 use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited};
 
@@ -34,15 +33,12 @@ pub(super) fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty
 // check if the component types of the transmuted collection and the result have different ABI,
 // size or alignment
 pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
-    let empty_param_env = ty::ParamEnv::empty();
-    // check if `from` and `to` are normalizable to avoid ICE (#4968)
-    if !(is_normalizable(cx, empty_param_env, from) && is_normalizable(cx, empty_param_env, to)) {
-        return false;
-    }
-    let from_ty_layout = cx.tcx.layout_of(empty_param_env.and(from));
-    let to_ty_layout = cx.tcx.layout_of(empty_param_env.and(to));
-    if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) {
-        from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi
+    if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from)
+        && let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to)
+        && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from))
+        && let Ok(to_layout) = cx.tcx.layout_of(cx.param_env.and(to))
+    {
+        from_layout.size != to_layout.size || from_layout.align.abi != to_layout.align.abi
     } else {
         // no idea about layout, so don't lint
         false
@@ -91,7 +87,7 @@ fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>
             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 returing Err in some cases. Those cases
+            // 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(),
index 67cc8913318962f2f849ab1441a166dd7b9d9eaa..353a6f6b899ea3e743ed57982b8acd130d185e14 100644 (file)
@@ -432,8 +432,8 @@ pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64, avoid_br
     fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, context: CheckTyContext) {
         // Ignore functions in trait implementations as they are usually forced by the trait definition.
         //
-        // FIXME: idially we would like to warn *if the compicated type can be simplified*, but it's hard to
-        // check.
+        // FIXME: ideally we would like to warn *if the complicated type can be simplified*, but it's hard
+        // to check.
         if context.is_in_trait_impl {
             return;
         }
index c8912a18f1854e3dcf5ee2252b1dcb52ced5d754..465d8a914fb290b2a4e466750f923bc6be9cc4da 100644 (file)
@@ -156,8 +156,9 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) ->
         .array_windows::<2>()
         .rev()
         .map_while(|[start, end]| {
-            src.get(start.to_usize() - offset..end.to_usize() - offset)
-                .map(|text| (start.to_usize(), text.trim_start()))
+            let start = start.to_usize() - offset;
+            let end = end.to_usize() - offset;
+            src.get(start..end).map(|text| (start, text.trim_start()))
         })
         .filter(|(_, text)| !text.is_empty());
 
@@ -182,7 +183,7 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) ->
     let (mut line_start, mut line) = (line_start, line);
     loop {
         if line.starts_with("/*") {
-            let src = src[line_start..line_starts.last().unwrap().to_usize()].trim_start();
+            let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start();
             let mut tokens = tokenize(src);
             return src[..tokens.next().unwrap().len]
                 .to_ascii_uppercase()
index b25a6e3375bb4176b3d45f610e633a8f9ce1086b..f3f1f53aac5652f6c07a0e79daa9bf85a7d21ff9 100644 (file)
@@ -1,18 +1,46 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::visitors::for_each_value_source;
+use core::ops::ControlFlow;
 use rustc_errors::Applicability;
-use rustc_hir::{Stmt, StmtKind};
+use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::{self, Ty, TypeFoldable, TypeVisitor};
 
 use super::LET_UNIT_VALUE;
 
 pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
-    if let StmtKind::Local(local) = stmt.kind {
-        if cx.typeck_results().pat_ty(local.pat).is_unit() {
-            if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() {
-                return;
+    if let StmtKind::Local(local) = stmt.kind
+        && let Some(init) = local.init
+        && !local.pat.span.from_expansion()
+        && !in_external_macro(cx.sess(), stmt.span)
+        && cx.typeck_results().pat_ty(local.pat).is_unit()
+    {
+        let needs_inferred = for_each_value_source(init, &mut |e| if needs_inferred_result_ty(cx, e) {
+            ControlFlow::Continue(())
+        } else {
+            ControlFlow::Break(())
+        }).is_continue();
+
+        if needs_inferred {
+            if !matches!(local.pat.kind, PatKind::Wild) {
+                span_lint_and_then(
+                    cx,
+                    LET_UNIT_VALUE,
+                    stmt.span,
+                    "this let-binding has unit value",
+                    |diag| {
+                            diag.span_suggestion(
+                                local.pat.span,
+                                "use a wild (`_`) binding",
+                                "_",
+                                Applicability::MaybeIncorrect, // snippet
+                            );
+                    },
+                );
             }
+        } else {
             span_lint_and_then(
                 cx,
                 LET_UNIT_VALUE,
@@ -33,3 +61,45 @@ pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
         }
     }
 }
+
+fn needs_inferred_result_ty(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+    let id = match e.kind {
+        ExprKind::Call(
+            Expr {
+                kind: ExprKind::Path(ref path),
+                hir_id,
+                ..
+            },
+            _,
+        ) => cx.qpath_res(path, *hir_id).opt_def_id(),
+        ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(e.hir_id),
+        _ => return false,
+    };
+    if let Some(id) = id
+        && let sig = cx.tcx.fn_sig(id).skip_binder()
+        && let ty::Param(output_ty) = *sig.output().kind()
+    {
+        sig.inputs().iter().all(|&ty| !ty_contains_param(ty, output_ty.index))
+    } else {
+        false
+    }
+}
+
+fn ty_contains_param(ty: Ty<'_>, index: u32) -> bool {
+    struct Visitor(u32);
+    impl<'tcx> TypeVisitor<'tcx> for Visitor {
+        type BreakTy = ();
+        fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+            if let ty::Param(ty) = *ty.kind() {
+                if ty.index == self.0 {
+                    ControlFlow::BREAK
+                } else {
+                    ControlFlow::CONTINUE
+                }
+            } else {
+                ty.super_visit_with(self)
+            }
+        }
+    }
+    ty.visit_with(&mut Visitor(index)).is_break()
+}
index d9f5b53b413a03dd5734a2edadfa202f7bf7ee98..a9e2073dec251de0d50b62c3b8d12df6531409f3 100644 (file)
@@ -23,7 +23,7 @@
     /// ```
     #[clippy::version = "pre 1.29.0"]
     pub LET_UNIT_VALUE,
-    pedantic,
+    style,
     "creating a `let` binding to a value of unit type, which usually can't be used afterwards"
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
new file mode 100644 (file)
index 0000000..8a4f4c0
--- /dev/null
@@ -0,0 +1,81 @@
+use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item};
+use clippy_utils::{match_def_path, paths};
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Detects cases of owned empty strings being passed as an argument to a function expecting `&str`
+    ///
+    /// ### Why is this bad?
+    ///
+    /// This results in longer and less readable code
+    ///
+    /// ### Example
+    /// ```rust
+    /// vec!["1", "2", "3"].join(&String::new());
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// vec!["1", "2", "3"].join("");
+    /// ```
+    #[clippy::version = "1.62.0"]
+    pub UNNECESSARY_OWNED_EMPTY_STRINGS,
+    style,
+    "detects cases of references to owned empty strings being passed as an argument to a function expecting `&str`"
+}
+declare_lint_pass!(UnnecessaryOwnedEmptyStrings => [UNNECESSARY_OWNED_EMPTY_STRINGS]);
+
+impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+        if_chain! {
+            if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner_expr) = expr.kind;
+            if let ExprKind::Call(fun, args) = inner_expr.kind;
+            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 ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
+            if inner_str.is_str();
+            then {
+                if match_def_path(cx, fun_def_id, &paths::STRING_NEW) {
+                     span_lint_and_sugg(
+                            cx,
+                            UNNECESSARY_OWNED_EMPTY_STRINGS,
+                            expr.span,
+                            "usage of `&String::new()` for a function expecting a `&str` argument",
+                            "try",
+                            "\"\"".to_owned(),
+                            Applicability::MachineApplicable,
+                        );
+                } else {
+                    if_chain! {
+                        if match_def_path(cx, fun_def_id, &paths::FROM_FROM);
+                        if let [.., last_arg] = args;
+                        if let ExprKind::Lit(spanned) = &last_arg.kind;
+                        if let LitKind::Str(symbol, _) = spanned.node;
+                        if symbol.is_empty();
+                        let inner_expr_type = cx.typeck_results().expr_ty(inner_expr);
+                        if is_type_diagnostic_item(cx, inner_expr_type, sym::String);
+                        then {
+                            span_lint_and_sugg(
+                                cx,
+                                UNNECESSARY_OWNED_EMPTY_STRINGS,
+                                expr.span,
+                                "usage of `&String::from(\"\")` for a function expecting a `&str` argument",
+                                "try",
+                                "\"\"".to_owned(),
+                                Applicability::MachineApplicable,
+                            );
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
index 1d4fe9cfd3cf40a8ac5459cf206f9c0704f711b0..ae431aac83b82c0ccaf7eaf3a1dfcc1317c5795d 100644 (file)
@@ -5,7 +5,7 @@
 use clippy_utils::{meets_msrv, msrvs, over};
 use rustc_ast::mut_visit::*;
 use rustc_ast::ptr::P;
-use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
+use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
 use rustc_ast_pretty::pprust;
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
@@ -25,7 +25,7 @@
     /// *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*.
     ///
     /// ### Why is this bad?
-    /// In the example above, `Some` is repeated, which unncessarily complicates the pattern.
+    /// In the example above, `Some` is repeated, which unnecessarily complicates the pattern.
     ///
     /// ### Example
     /// ```rust
@@ -230,6 +230,10 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize)
         // with which a pattern `C(p_0)` may be formed,
         // which we would want to join with other `C(p_j)`s.
         Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_)
+        // Skip immutable refs, as grouping them saves few characters,
+        // and almost always requires adding parens (increasing noisiness).
+        // In the case of only two patterns, replacement adds net characters.
+        | Ref(_, Mutability::Not)
         // Dealt with elsewhere.
         | Or(_) | Paren(_) => false,
         // Transform `box x | ... | box y` into `box (x | y)`.
@@ -241,10 +245,10 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize)
             |k| matches!(k, Box(_)),
             |k| always_pat!(k, Box(p) => p),
         ),
-        // Transform `&m x | ... | &m y` into `&m (x | y)`.
-        Ref(target, m1) => extend_with_matching(
+        // Transform `&mut x | ... | &mut y` into `&mut (x | y)`.
+        Ref(target, Mutability::Mut) => extend_with_matching(
             target, start, alternatives,
-            |k| matches!(k, Ref(_, m2) if m1 == m2), // Mutabilities must match.
+            |k| matches!(k, Ref(_, Mutability::Mut)),
             |k| always_pat!(k, Ref(p, _) => p),
         ),
         // Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`.
index f8e1021af0ea11b9bd46acc78dfe62ea9ea4ccaf..138f8bccb3f5741ba14987fc31d73fa9e6d34a5f 100644 (file)
@@ -30,7 +30,7 @@
     ///
     /// ### Known problems
     /// - Unaddressed false negative in fn bodies of trait implementations
-    /// - False positive with assotiated types in traits (#4140)
+    /// - False positive with associated types in traits (#4140)
     ///
     /// ### Example
     /// ```rust
index d23c85c033b2f7e63ce1809f30443d055e6eb605..ff5be825b781712030c5383b956721384f1184d6 100644 (file)
@@ -70,7 +70,7 @@ macro_rules! bind {
     };
 }
 
-/// Transforms the given `Option<T>` varibles into `OptionPat<Binding<T>>`.
+/// Transforms the given `Option<T>` variables into `OptionPat<Binding<T>>`.
 /// This displays as `Some($name)` or `None` when printed. The name of the inner binding
 /// is set to the name of the variable passed to the macro.
 macro_rules! opt_bind {
index 271c3a3dd181cef4db12b84a6db0f9a3fa42ebf5..74b0168a1794be449f2c285effd37b27938a27ac 100644 (file)
@@ -310,6 +310,12 @@ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
     /// the slice pattern that is suggested. If more elements would be 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
+    (await_holding_invalid_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()),
+    /// Lint: LARGE_INCLUDE_FILE.
+    ///
+    /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
+    (max_include_file_size: u64 = 1_000_000),
 }
 
 /// Search for the configuration file.
diff --git a/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs b/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs
new file mode 100644 (file)
index 0000000..01efc52
--- /dev/null
@@ -0,0 +1,55 @@
+use clippy_utils::get_attr;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// It formats the attached node with `{:#?}` and writes the result to the
+    /// standard output. This is intended for debugging.
+    ///
+    /// ### Examples
+    /// ```rs
+    /// #[clippy::dump]
+    /// use std::mem;
+    ///
+    /// #[clippy::dump]
+    /// fn foo(input: u32) -> u64 {
+    ///     input as u64
+    /// }
+    /// ```
+    pub DUMP_HIR,
+    internal_warn,
+    "helper to dump info about code"
+}
+
+declare_lint_pass!(DumpHir => [DUMP_HIR]);
+
+impl<'tcx> LateLintPass<'tcx> for DumpHir {
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
+        if has_attr(cx, item.hir_id()) {
+            println!("{item:#?}");
+        }
+    }
+
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+        if has_attr(cx, expr.hir_id) {
+            println!("{expr:#?}");
+        }
+    }
+
+    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
+        match stmt.kind {
+            hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return,
+            _ => {},
+        }
+        if has_attr(cx, stmt.hir_id) {
+            println!("{stmt:#?}");
+        }
+    }
+}
+
+fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
+    let attrs = cx.tcx.hir().attrs(hir_id);
+    get_attr(cx.sess(), attrs, "dump").count() > 0
+}
diff --git a/src/tools/clippy/clippy_lints/src/utils/inspector.rs b/src/tools/clippy/clippy_lints/src/utils/inspector.rs
deleted file mode 100644 (file)
index 37b114a..0000000
+++ /dev/null
@@ -1,577 +0,0 @@
-//! checks for attributes
-
-use clippy_utils::get_attr;
-use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece};
-use rustc_hir as hir;
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::ty;
-use rustc_session::Session;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Dumps every ast/hir node which has the `#[clippy::dump]`
-    /// attribute
-    ///
-    /// ### Example
-    /// ```rust,ignore
-    /// #[clippy::dump]
-    /// extern crate foo;
-    /// ```
-    ///
-    /// prints
-    ///
-    /// ```text
-    /// item `foo`
-    /// visibility inherited from outer item
-    /// extern crate dylib source: "/path/to/foo.so"
-    /// ```
-    pub DEEP_CODE_INSPECTION,
-    internal_warn,
-    "helper to dump info about code"
-}
-
-declare_lint_pass!(DeepCodeInspector => [DEEP_CODE_INSPECTION]);
-
-impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector {
-    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
-        if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) {
-            return;
-        }
-        print_item(cx, item);
-    }
-
-    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
-        if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) {
-            return;
-        }
-        println!("impl item `{}`", item.ident.name);
-        match cx.tcx.visibility(item.def_id) {
-            ty::Visibility::Public => println!("public"),
-            ty::Visibility::Restricted(def_id) => {
-                if def_id.is_top_level_module() {
-                    println!("visible crate wide")
-                } else {
-                    println!("visible in module `{}`", cx.tcx.def_path_str(def_id))
-                }
-            },
-            ty::Visibility::Invisible => println!("invisible"),
-        }
-        match item.kind {
-            hir::ImplItemKind::Const(_, body_id) => {
-                println!("associated constant");
-                print_expr(cx, &cx.tcx.hir().body(body_id).value, 1);
-            },
-            hir::ImplItemKind::Fn(..) => println!("method"),
-            hir::ImplItemKind::TyAlias(_) => println!("associated type"),
-        }
-    }
-
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if !has_attr(cx.sess(), cx.tcx.hir().attrs(expr.hir_id)) {
-            return;
-        }
-        print_expr(cx, expr, 0);
-    }
-
-    fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) {
-        if !has_attr(cx.sess(), cx.tcx.hir().attrs(arm.hir_id)) {
-            return;
-        }
-        print_pat(cx, arm.pat, 1);
-        if let Some(ref guard) = arm.guard {
-            println!("guard:");
-            print_guard(cx, guard, 1);
-        }
-        println!("body:");
-        print_expr(cx, arm.body, 1);
-    }
-
-    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
-        if !has_attr(cx.sess(), cx.tcx.hir().attrs(stmt.hir_id)) {
-            return;
-        }
-        match stmt.kind {
-            hir::StmtKind::Local(local) => {
-                println!("local variable of type {}", cx.typeck_results().node_type(local.hir_id));
-                println!("pattern:");
-                print_pat(cx, local.pat, 0);
-                if let Some(e) = local.init {
-                    println!("init expression:");
-                    print_expr(cx, e, 0);
-                }
-            },
-            hir::StmtKind::Item(_) => println!("item decl"),
-            hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) => print_expr(cx, e, 0),
-        }
-    }
-}
-
-fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool {
-    get_attr(sess, attrs, "dump").count() > 0
-}
-
-#[allow(clippy::similar_names)]
-#[allow(clippy::too_many_lines)]
-fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) {
-    let ind = "  ".repeat(indent);
-    println!("{}+", ind);
-    println!("{}ty: {}", ind, cx.typeck_results().expr_ty(expr));
-    println!(
-        "{}adjustments: {:?}",
-        ind,
-        cx.typeck_results().adjustments().get(expr.hir_id)
-    );
-    match expr.kind {
-        hir::ExprKind::Box(e) => {
-            println!("{}Box", ind);
-            print_expr(cx, e, indent + 1);
-        },
-        hir::ExprKind::Array(v) => {
-            println!("{}Array", ind);
-            for e in v {
-                print_expr(cx, e, indent + 1);
-            }
-        },
-        hir::ExprKind::Call(func, args) => {
-            println!("{}Call", ind);
-            println!("{}function:", ind);
-            print_expr(cx, func, indent + 1);
-            println!("{}arguments:", ind);
-            for arg in args {
-                print_expr(cx, arg, indent + 1);
-            }
-        },
-        hir::ExprKind::Let(hir::Let { pat, init, ty, .. }) => {
-            print_pat(cx, pat, indent + 1);
-            if let Some(ty) = ty {
-                println!("{}  type annotation: {:?}", ind, ty);
-            }
-            print_expr(cx, init, indent + 1);
-        },
-        hir::ExprKind::MethodCall(path, args, _) => {
-            println!("{}MethodCall", ind);
-            println!("{}method name: {}", ind, path.ident.name);
-            for arg in args {
-                print_expr(cx, arg, indent + 1);
-            }
-        },
-        hir::ExprKind::Tup(v) => {
-            println!("{}Tup", ind);
-            for e in v {
-                print_expr(cx, e, indent + 1);
-            }
-        },
-        hir::ExprKind::Binary(op, lhs, rhs) => {
-            println!("{}Binary", ind);
-            println!("{}op: {:?}", ind, op.node);
-            println!("{}lhs:", ind);
-            print_expr(cx, lhs, indent + 1);
-            println!("{}rhs:", ind);
-            print_expr(cx, rhs, indent + 1);
-        },
-        hir::ExprKind::Unary(op, inner) => {
-            println!("{}Unary", ind);
-            println!("{}op: {:?}", ind, op);
-            print_expr(cx, inner, indent + 1);
-        },
-        hir::ExprKind::Lit(ref lit) => {
-            println!("{}Lit", ind);
-            println!("{}{:?}", ind, lit);
-        },
-        hir::ExprKind::Cast(e, target) => {
-            println!("{}Cast", ind);
-            print_expr(cx, e, indent + 1);
-            println!("{}target type: {:?}", ind, target);
-        },
-        hir::ExprKind::Type(e, target) => {
-            println!("{}Type", ind);
-            print_expr(cx, e, indent + 1);
-            println!("{}target type: {:?}", ind, target);
-        },
-        hir::ExprKind::Loop(..) => {
-            println!("{}Loop", ind);
-        },
-        hir::ExprKind::If(cond, _, ref else_opt) => {
-            println!("{}If", ind);
-            println!("{}condition:", ind);
-            print_expr(cx, cond, indent + 1);
-            if let Some(els) = *else_opt {
-                println!("{}else:", ind);
-                print_expr(cx, els, indent + 1);
-            }
-        },
-        hir::ExprKind::Match(cond, _, ref source) => {
-            println!("{}Match", ind);
-            println!("{}condition:", ind);
-            print_expr(cx, cond, indent + 1);
-            println!("{}source: {:?}", ind, source);
-        },
-        hir::ExprKind::Closure(ref clause, _, _, _, _) => {
-            println!("{}Closure", ind);
-            println!("{}clause: {:?}", ind, clause);
-        },
-        hir::ExprKind::Yield(sub, _) => {
-            println!("{}Yield", ind);
-            print_expr(cx, sub, indent + 1);
-        },
-        hir::ExprKind::Block(_, _) => {
-            println!("{}Block", ind);
-        },
-        hir::ExprKind::Assign(lhs, rhs, _) => {
-            println!("{}Assign", ind);
-            println!("{}lhs:", ind);
-            print_expr(cx, lhs, indent + 1);
-            println!("{}rhs:", ind);
-            print_expr(cx, rhs, indent + 1);
-        },
-        hir::ExprKind::AssignOp(ref binop, lhs, rhs) => {
-            println!("{}AssignOp", ind);
-            println!("{}op: {:?}", ind, binop.node);
-            println!("{}lhs:", ind);
-            print_expr(cx, lhs, indent + 1);
-            println!("{}rhs:", ind);
-            print_expr(cx, rhs, indent + 1);
-        },
-        hir::ExprKind::Field(e, ident) => {
-            println!("{}Field", ind);
-            println!("{}field name: {}", ind, ident.name);
-            println!("{}struct expr:", ind);
-            print_expr(cx, e, indent + 1);
-        },
-        hir::ExprKind::Index(arr, idx) => {
-            println!("{}Index", ind);
-            println!("{}array expr:", ind);
-            print_expr(cx, arr, indent + 1);
-            println!("{}index expr:", ind);
-            print_expr(cx, idx, indent + 1);
-        },
-        hir::ExprKind::Path(hir::QPath::Resolved(ref ty, path)) => {
-            println!("{}Resolved Path, {:?}", ind, ty);
-            println!("{}path: {:?}", ind, path);
-        },
-        hir::ExprKind::Path(hir::QPath::TypeRelative(ty, seg)) => {
-            println!("{}Relative Path, {:?}", ind, ty);
-            println!("{}seg: {:?}", ind, seg);
-        },
-        hir::ExprKind::Path(hir::QPath::LangItem(lang_item, ..)) => {
-            println!("{}Lang Item Path, {:?}", ind, lang_item.name());
-        },
-        hir::ExprKind::AddrOf(kind, ref muta, e) => {
-            println!("{}AddrOf", ind);
-            println!("kind: {:?}", kind);
-            println!("mutability: {:?}", muta);
-            print_expr(cx, e, indent + 1);
-        },
-        hir::ExprKind::Break(_, ref e) => {
-            println!("{}Break", ind);
-            if let Some(e) = *e {
-                print_expr(cx, e, indent + 1);
-            }
-        },
-        hir::ExprKind::Continue(_) => println!("{}Again", ind),
-        hir::ExprKind::Ret(ref e) => {
-            println!("{}Ret", ind);
-            if let Some(e) = *e {
-                print_expr(cx, e, indent + 1);
-            }
-        },
-        hir::ExprKind::InlineAsm(asm) => {
-            println!("{}InlineAsm", ind);
-            println!("{}template: {}", ind, InlineAsmTemplatePiece::to_string(asm.template));
-            println!("{}options: {:?}", ind, asm.options);
-            println!("{}operands:", ind);
-            for (op, _op_sp) in asm.operands {
-                match op {
-                    hir::InlineAsmOperand::In { expr, .. }
-                    | hir::InlineAsmOperand::InOut { expr, .. } => {
-                        print_expr(cx, expr, indent + 1);
-                    }
-                    hir::InlineAsmOperand::Out { expr, .. } => {
-                        if let Some(expr) = expr {
-                            print_expr(cx, expr, indent + 1);
-                        }
-                    },
-                    hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
-                        print_expr(cx, in_expr, indent + 1);
-                        if let Some(out_expr) = out_expr {
-                            print_expr(cx, out_expr, indent + 1);
-                        }
-                    },
-                    hir::InlineAsmOperand::Const { anon_const }
-                    | hir::InlineAsmOperand::SymFn { anon_const } => {
-                        println!("{}anon_const:", ind);
-                        print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
-                    },
-                    hir::InlineAsmOperand::SymStatic { path, .. } => {
-                        match path {
-                            hir::QPath::Resolved(ref ty, path) => {
-                                println!("{}Resolved Path, {:?}", ind, ty);
-                                println!("{}path: {:?}", ind, path);
-                            },
-                            hir::QPath::TypeRelative(ty, seg) => {
-                                println!("{}Relative Path, {:?}", ind, ty);
-                                println!("{}seg: {:?}", ind, seg);
-                            },
-                            hir::QPath::LangItem(lang_item, ..) => {
-                                println!("{}Lang Item Path, {:?}", ind, lang_item.name());
-                            },
-                        }
-                    }
-                }
-            }
-        },
-        hir::ExprKind::Struct(path, fields, ref base) => {
-            println!("{}Struct", ind);
-            println!("{}path: {:?}", ind, path);
-            for field in fields {
-                println!("{}field \"{}\":", ind, field.ident.name);
-                print_expr(cx, field.expr, indent + 1);
-            }
-            if let Some(base) = *base {
-                println!("{}base:", ind);
-                print_expr(cx, base, indent + 1);
-            }
-        },
-        hir::ExprKind::ConstBlock(ref anon_const) => {
-            println!("{}ConstBlock", ind);
-            println!("{}anon_const:", ind);
-            print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
-        },
-        hir::ExprKind::Repeat(val, length) => {
-            println!("{}Repeat", ind);
-            println!("{}value:", ind);
-            print_expr(cx, val, indent + 1);
-            println!("{}repeat count:", ind);
-            match length {
-                hir::ArrayLen::Infer(_, _) => println!("{}repeat count: _", ind),
-                hir::ArrayLen::Body(anon_const) => {
-                    print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
-                },
-            }
-        },
-        hir::ExprKind::Err => {
-            println!("{}Err", ind);
-        },
-        hir::ExprKind::DropTemps(e) => {
-            println!("{}DropTemps", ind);
-            print_expr(cx, e, indent + 1);
-        },
-    }
-}
-
-fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
-    let did = item.def_id;
-    println!("item `{}`", item.ident.name);
-    match cx.tcx.visibility(item.def_id) {
-        ty::Visibility::Public => println!("public"),
-        ty::Visibility::Restricted(def_id) => {
-            if def_id.is_top_level_module() {
-                println!("visible crate wide")
-            } else {
-                println!("visible in module `{}`", cx.tcx.def_path_str(def_id))
-            }
-        },
-        ty::Visibility::Invisible => println!("invisible"),
-    }
-    match item.kind {
-        hir::ItemKind::ExternCrate(ref _renamed_from) => {
-            if let Some(crate_id) = cx.tcx.extern_mod_stmt_cnum(did) {
-                let source = cx.tcx.used_crate_source(crate_id);
-                if let Some(ref src) = source.dylib {
-                    println!("extern crate dylib source: {:?}", src.0);
-                }
-                if let Some(ref src) = source.rlib {
-                    println!("extern crate rlib source: {:?}", src.0);
-                }
-            } else {
-                println!("weird extern crate without a crate id");
-            }
-        },
-        hir::ItemKind::Use(path, ref kind) => println!("{:?}, {:?}", path, kind),
-        hir::ItemKind::Static(..) => println!("static item of type {:#?}", cx.tcx.type_of(did)),
-        hir::ItemKind::Const(..) => println!("const item of type {:#?}", cx.tcx.type_of(did)),
-        hir::ItemKind::Fn(..) => {
-            let item_ty = cx.tcx.type_of(did);
-            println!("function of type {:#?}", item_ty);
-        },
-        hir::ItemKind::Macro(ref macro_def, _) => {
-            if macro_def.macro_rules {
-                println!("macro introduced by `macro_rules!`");
-            } else {
-                println!("macro introduced by `macro`");
-            }
-        },
-        hir::ItemKind::Mod(..) => println!("module"),
-        hir::ItemKind::ForeignMod { abi, .. } => println!("foreign module with abi: {}", abi),
-        hir::ItemKind::GlobalAsm(asm) => println!("global asm: {:?}", asm),
-        hir::ItemKind::TyAlias(..) => {
-            println!("type alias for {:?}", cx.tcx.type_of(did));
-        },
-        hir::ItemKind::OpaqueTy(..) => {
-            println!("existential type with real type {:?}", cx.tcx.type_of(did));
-        },
-        hir::ItemKind::Enum(..) => {
-            println!("enum definition of type {:?}", cx.tcx.type_of(did));
-        },
-        hir::ItemKind::Struct(..) => {
-            println!("struct definition of type {:?}", cx.tcx.type_of(did));
-        },
-        hir::ItemKind::Union(..) => {
-            println!("union definition of type {:?}", cx.tcx.type_of(did));
-        },
-        hir::ItemKind::Trait(..) => {
-            println!("trait decl");
-            if cx.tcx.trait_is_auto(did.to_def_id()) {
-                println!("trait is auto");
-            } else {
-                println!("trait is not auto");
-            }
-        },
-        hir::ItemKind::TraitAlias(..) => {
-            println!("trait alias");
-        },
-        hir::ItemKind::Impl(hir::Impl {
-            of_trait: Some(ref _trait_ref),
-            ..
-        }) => {
-            println!("trait impl");
-        },
-        hir::ItemKind::Impl(hir::Impl { of_trait: None, .. }) => {
-            println!("impl");
-        },
-    }
-}
-
-#[allow(clippy::similar_names)]
-#[allow(clippy::too_many_lines)]
-fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) {
-    let ind = "  ".repeat(indent);
-    println!("{}+", ind);
-    match pat.kind {
-        hir::PatKind::Wild => println!("{}Wild", ind),
-        hir::PatKind::Binding(ref mode, .., ident, ref inner) => {
-            println!("{}Binding", ind);
-            println!("{}mode: {:?}", ind, mode);
-            println!("{}name: {}", ind, ident.name);
-            if let Some(inner) = *inner {
-                println!("{}inner:", ind);
-                print_pat(cx, inner, indent + 1);
-            }
-        },
-        hir::PatKind::Or(fields) => {
-            println!("{}Or", ind);
-            for field in fields {
-                print_pat(cx, field, indent + 1);
-            }
-        },
-        hir::PatKind::Struct(ref path, fields, ignore) => {
-            println!("{}Struct", ind);
-            println!(
-                "{}name: {}",
-                ind,
-                rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
-            );
-            println!("{}ignore leftover fields: {}", ind, ignore);
-            println!("{}fields:", ind);
-            for field in fields {
-                println!("{}  field name: {}", ind, field.ident.name);
-                if field.is_shorthand {
-                    println!("{}  in shorthand notation", ind);
-                }
-                print_pat(cx, field.pat, indent + 1);
-            }
-        },
-        hir::PatKind::TupleStruct(ref path, fields, opt_dots_position) => {
-            println!("{}TupleStruct", ind);
-            println!(
-                "{}path: {}",
-                ind,
-                rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
-            );
-            if let Some(dot_position) = opt_dots_position {
-                println!("{}dot position: {}", ind, dot_position);
-            }
-            for field in fields {
-                print_pat(cx, field, indent + 1);
-            }
-        },
-        hir::PatKind::Path(hir::QPath::Resolved(ref ty, path)) => {
-            println!("{}Resolved Path, {:?}", ind, ty);
-            println!("{}path: {:?}", ind, path);
-        },
-        hir::PatKind::Path(hir::QPath::TypeRelative(ty, seg)) => {
-            println!("{}Relative Path, {:?}", ind, ty);
-            println!("{}seg: {:?}", ind, seg);
-        },
-        hir::PatKind::Path(hir::QPath::LangItem(lang_item, ..)) => {
-            println!("{}Lang Item Path, {:?}", ind, lang_item.name());
-        },
-        hir::PatKind::Tuple(pats, opt_dots_position) => {
-            println!("{}Tuple", ind);
-            if let Some(dot_position) = opt_dots_position {
-                println!("{}dot position: {}", ind, dot_position);
-            }
-            for field in pats {
-                print_pat(cx, field, indent + 1);
-            }
-        },
-        hir::PatKind::Box(inner) => {
-            println!("{}Box", ind);
-            print_pat(cx, inner, indent + 1);
-        },
-        hir::PatKind::Ref(inner, ref muta) => {
-            println!("{}Ref", ind);
-            println!("{}mutability: {:?}", ind, muta);
-            print_pat(cx, inner, indent + 1);
-        },
-        hir::PatKind::Lit(e) => {
-            println!("{}Lit", ind);
-            print_expr(cx, e, indent + 1);
-        },
-        hir::PatKind::Range(ref l, ref r, ref range_end) => {
-            println!("{}Range", ind);
-            if let Some(expr) = l {
-                print_expr(cx, expr, indent + 1);
-            }
-            if let Some(expr) = r {
-                print_expr(cx, expr, indent + 1);
-            }
-            match *range_end {
-                hir::RangeEnd::Included => println!("{} end included", ind),
-                hir::RangeEnd::Excluded => println!("{} end excluded", ind),
-            }
-        },
-        hir::PatKind::Slice(first_pats, ref range, last_pats) => {
-            println!("{}Slice [a, b, ..i, y, z]", ind);
-            println!("[a, b]:");
-            for pat in first_pats {
-                print_pat(cx, pat, indent + 1);
-            }
-            println!("i:");
-            if let Some(pat) = *range {
-                print_pat(cx, pat, indent + 1);
-            }
-            println!("[y, z]:");
-            for pat in last_pats {
-                print_pat(cx, pat, indent + 1);
-            }
-        },
-    }
-}
-
-fn print_guard(cx: &LateContext<'_>, guard: &hir::Guard<'_>, indent: usize) {
-    let ind = "  ".repeat(indent);
-    println!("{}+", ind);
-    match guard {
-        hir::Guard::If(expr) => {
-            println!("{}If", ind);
-            print_expr(cx, expr, indent + 1);
-        },
-        hir::Guard::IfLet(pat, expr) => {
-            println!("{}IfLet", ind);
-            print_pat(cx, pat, indent + 1);
-            print_expr(cx, expr, indent + 1);
-        },
-    }
-}
index 25d74b8c49939da8a643e60a2da1e4b264440d91..0e8f40e92101a053ab1d5210b761838ca2447b87 100644 (file)
     /// Checks for unnecessary conversion from Symbol to a string.
     ///
     /// ### Why is this bad?
-    /// It's faster use symbols directly intead of strings.
+    /// It's faster use symbols directly instead of strings.
     ///
     /// ### Example
     /// Bad:
@@ -823,7 +823,7 @@ fn suggest_note(
         cx,
         COLLAPSIBLE_SPAN_LINT_CALLS,
         expr.span,
-        "this call is collspible",
+        "this call is collapsible",
         "collapse into",
         format!(
             "span_lint_and_note({}, {}, {}, {}, {}, {})",
index ca03b8010dd821c2c68da237efae631c279e1711..8c1910b3b2af8d37aab0874ad8ac0b6456fc5ab5 100644 (file)
@@ -33,7 +33,7 @@
 /// This is the output file of the lint collector.
 const OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
 /// These lints are excluded from the export.
-const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "internal_metadata_collector"];
+const BLACK_LISTED_LINTS: &[&str] = &["lint_author", "dump_hir", "internal_metadata_collector"];
 /// These groups will be ignored by the lint group matcher. This is useful for collections like
 /// `clippy::all`
 const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"];
@@ -117,7 +117,7 @@ macro_rules! CONFIGURATION_VALUE_TEMPLATE {
 /// This applicability will be set for unresolved applicability values.
 const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
 /// The version that will be displayed if none has been defined
-const VERION_DEFAULT_STR: &str = "Unknown";
+const VERSION_DEFAULT_STR: &str = "Unknown";
 
 declare_clippy_lint! {
     /// ### What it does
@@ -571,7 +571,7 @@ fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
 
 fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
     extract_clippy_version_value(cx, item).map_or_else(
-        || VERION_DEFAULT_STR.to_string(),
+        || VERSION_DEFAULT_STR.to_string(),
         |version| version.as_str().to_string(),
     )
 }
@@ -872,7 +872,7 @@ fn add_multi_part_suggestion(&mut self) {
         self.suggestion_count += 2;
     }
 
-    /// Checks if the suggestions include multiple spanns
+    /// Checks if the suggestions include multiple spans
     fn is_multi_part(&self) -> bool {
         self.suggestion_count > 1
     }
index dc385ebacba664db68ea0ed564508f9ef7a312cb..787e9fd982c89c40ab67b3f1f997aa643bd406e8 100644 (file)
@@ -1,5 +1,5 @@
 pub mod author;
 pub mod conf;
-pub mod inspector;
+pub mod dump_hir;
 #[cfg(feature = "internal")]
 pub mod internal_lints;
index be46b791aa4b656decb9406aeb5ee275136e8ce1..fdb822c3e5b6d6f5908312885dbff2455323c455 100644 (file)
@@ -5,7 +5,7 @@
 use rustc_ast::ast::{self, LitFloatType, LitKind};
 use rustc_data_structures::sync::Lrc;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp};
+use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp};
 use rustc_lint::LateContext;
 use rustc_middle::mir::interpret::Scalar;
 use rustc_middle::ty::subst::{Subst, SubstsRef};
@@ -400,6 +400,22 @@ fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<C
         let res = self.typeck_results.qpath_res(qpath, id);
         match res {
             Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
+                // Check if this constant is based on `cfg!(..)`,
+                // which is NOT constant for our purposes.
+                if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id) &&
+                let Node::Item(&Item {
+                    kind: ItemKind::Const(_, body_id),
+                    ..
+                }) = node &&
+                let Node::Expr(&Expr {
+                    kind: ExprKind::Lit(_),
+                    span,
+                    ..
+                }) = self.lcx.tcx.hir().get(body_id.hir_id) &&
+                is_direct_expn_of(span, "cfg").is_some() {
+                    return None;
+                }
+
                 let substs = self.typeck_results.node_substs(id);
                 let substs = if self.substs.is_empty() {
                     substs
index c05317f59b716ad60f0fdd92689ee1167fbe914e..f4da625f1e306a0e083716316ede4a5f07ced8d7 100644 (file)
@@ -1,4 +1,4 @@
-use crate::consts::{constant_context, constant_simple};
+use crate::consts::constant_simple;
 use crate::source::snippet_opt;
 use rustc_ast::ast::InlineAsmTemplatePiece;
 use rustc_data_structures::fx::FxHasher;
 use std::hash::{Hash, Hasher};
 
 /// Type used to check whether two ast are the same. This is different from the
-/// operator
-/// `==` on ast types as this operator would compare true equality with ID and
-/// span.
+/// operator `==` on ast types as this operator would compare true equality with
+/// ID and span.
 ///
 /// Note that some expressions kinds are not considered but could be added.
 pub struct SpanlessEq<'a, 'tcx> {
     /// Context used to evaluate constant expressions.
     cx: &'a LateContext<'tcx>,
-    maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
+    maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>,
     allow_side_effects: bool,
     expr_fallback: Option<Box<dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a>>,
 }
@@ -33,7 +32,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
     pub fn new(cx: &'a LateContext<'tcx>) -> Self {
         Self {
             cx,
-            maybe_typeck_results: cx.maybe_typeck_results(),
+            maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)),
             allow_side_effects: true,
             expr_fallback: None,
         }
@@ -102,9 +101,9 @@ pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
             (&StmtKind::Local(l), &StmtKind::Local(r)) => {
                 // This additional check ensures that the type of the locals are equivalent even if the init
                 // expression or type have some inferred parts.
-                if let Some(typeck) = self.inner.maybe_typeck_results {
-                    let l_ty = typeck.pat_ty(l.pat);
-                    let r_ty = typeck.pat_ty(r.pat);
+                if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
+                    let l_ty = typeck_lhs.pat_ty(l.pat);
+                    let r_ty = typeck_rhs.pat_ty(r.pat);
                     if l_ty != r_ty {
                         return false;
                     }
@@ -182,9 +181,17 @@ pub fn eq_array_length(&mut self, left: ArrayLen, right: ArrayLen) -> bool {
     }
 
     pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool {
-        let cx = self.inner.cx;
-        let eval_const = |body| constant_context(cx, cx.tcx.typeck_body(body)).expr(&cx.tcx.hir().body(body).value);
-        eval_const(left) == eval_const(right)
+        // swap out TypeckResults when hashing a body
+        let old_maybe_typeck_results = self.inner.maybe_typeck_results.replace((
+            self.inner.cx.tcx.typeck_body(left),
+            self.inner.cx.tcx.typeck_body(right),
+        ));
+        let res = self.eq_expr(
+            &self.inner.cx.tcx.hir().body(left).value,
+            &self.inner.cx.tcx.hir().body(right).value,
+        );
+        self.inner.maybe_typeck_results = old_maybe_typeck_results;
+        res
     }
 
     #[allow(clippy::similar_names)]
@@ -193,10 +200,10 @@ pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
             return false;
         }
 
-        if let Some(typeck_results) = self.inner.maybe_typeck_results {
+        if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
             if let (Some(l), Some(r)) = (
-                constant_simple(self.inner.cx, typeck_results, left),
-                constant_simple(self.inner.cx, typeck_results, right),
+                constant_simple(self.inner.cx, typeck_lhs, left),
+                constant_simple(self.inner.cx, typeck_rhs, right),
             ) {
                 if l == r {
                     return true;
@@ -674,8 +681,9 @@ pub fn hash_expr(&mut self, e: &Expr<'_>) {
                                 self.hash_expr(out_expr);
                             }
                         },
-                        InlineAsmOperand::Const { anon_const } => self.hash_body(anon_const.body),
-                        InlineAsmOperand::SymFn { anon_const } => self.hash_body(anon_const.body),
+                        InlineAsmOperand::Const { anon_const } | InlineAsmOperand::SymFn { anon_const } => {
+                            self.hash_body(anon_const.body);
+                        },
                         InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path),
                     }
                 }
index e7d4c5a49521d9ae21cec7b51e7512a5606630c0..a268e339bb130df99138102b739e6def1d44c640 100644 (file)
@@ -367,7 +367,7 @@ pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
         expr_visitor_no_bodies(|e| {
             // if we're still inside of the macro definition...
             if e.span.ctxt() == expr.span.ctxt() {
-                // ArgumnetV1::new_<format_trait>(<value>)
+                // ArgumentV1::new_<format_trait>(<value>)
                 if_chain! {
                     if let ExprKind::Call(callee, [val]) = e.kind;
                     if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind;
index 0424e06720263e5322b38eabf54d518bb899d0e6..134fd1ce505a092b22022b6c840a8d722cffc2e7 100644 (file)
@@ -32,4 +32,5 @@ macro_rules! msrv_aliases {
     1,28,0 { FROM_BOOL }
     1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
     1,16,0 { STR_REPEAT }
+    1,24,0 { IS_ASCII_DIGIT }
 }
index 908ff822712ffa0a171f9d043a78ed9b7c8fceb2..b92d42e83232ce27501f77e0e904dbe301eca758 100644 (file)
@@ -57,7 +57,7 @@ pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<
                 .trim_start()
                 .chars()
                 .next()
-                .map_or(false, |c| c.is_digit(10))
+                .map_or(false, |c| c.is_ascii_digit())
         {
             let (unsuffixed, suffix) = split_suffix(src, lit_kind);
             let float = matches!(lit_kind, LitKind::Float(..));
index e5fa6deefc5de3d85b1e1afcbcc1cb14cececa02..60971fb716dbdc69e364e137e032b2a5baab05cd 100644 (file)
@@ -61,6 +61,7 @@
 pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
 pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
 pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
+pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
 pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
 #[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
 pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
 pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
 pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
 pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
+pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];
+pub const STR_BYTES: [&str; 4] = ["core", "str", "<impl str>", "bytes"];
 pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
 pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
 pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
index fe411220484890f33e6abaa68d2a53477eea7f17..75808b1b17461d657f322903733e48fdf467fc2d 100644 (file)
@@ -211,8 +211,9 @@ fn check_statement<'tcx>(
 
         StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body),
         // just an assignment
-        StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => 
-            check_place(tcx, **place, span, body),
+        StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => {
+            check_place(tcx, **place, span, body)
+        },
 
         StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { dst, src, count }) => {
             check_operand(tcx, dst, span, body)?;
index dbad607c58ea34dfee9b37780b166824cd0f2efc..c69a3d8d2a15ec4d8883fd415ba94eff3496719a 100644 (file)
@@ -7,9 +7,28 @@
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LintContext};
 use rustc_span::hygiene;
+use rustc_span::source_map::SourceMap;
 use rustc_span::{BytePos, Pos, Span, SyntaxContext};
 use std::borrow::Cow;
 
+/// Checks if the span starts with the given text. This will return false if the span crosses
+/// multiple files or if source is not available.
+///
+/// This is used to check for proc macros giving unhelpful spans to things.
+pub fn span_starts_with<T: LintContext>(cx: &T, span: Span, text: &str) -> bool {
+    fn helper(sm: &SourceMap, span: Span, text: &str) -> bool {
+        let pos = sm.lookup_byte_offset(span.lo());
+        let Some(ref src) = pos.sf.src else {
+            return false;
+        };
+        let end = span.hi() - pos.sf.start_pos;
+        src.get(pos.pos.0 as usize..end.0 as usize)
+            // Expression spans can include wrapping parenthesis. Remove them first.
+            .map_or(false, |s| s.trim_start_matches('(').starts_with(text))
+    }
+    helper(cx.sess().source_map(), span, text)
+}
+
 /// 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>(
@@ -89,7 +108,7 @@ pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
     true
 }
 
-/// Returns the positon just before rarrow
+/// Returns the position just before rarrow
 ///
 /// ```rust,ignore
 /// fn into(self) -> () {}
index 794d2e1026f8c7013de5515b02b2610ddace3365..18915553e61c06cdc97ee45265f796215f617874 100644 (file)
@@ -18,7 +18,7 @@
 use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 use std::borrow::Cow;
 use std::convert::TryInto;
-use std::fmt::Display;
+use std::fmt::{Display, Write as _};
 use std::iter;
 use std::ops::{Add, Neg, Not, Sub};
 
@@ -902,7 +902,7 @@ fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {
             if cmt.place.projections.is_empty() {
                 // handle item without any projection, that needs an explicit borrowing
                 // i.e.: suggest `&x` instead of `x`
-                self.suggestion_start.push_str(&format!("{}&{}", start_snip, ident_str));
+                let _ = write!(self.suggestion_start, "{}&{}", start_snip, ident_str);
             } else {
                 // cases where a parent `Call` or `MethodCall` is using the item
                 // i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()`
@@ -917,8 +917,7 @@ fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {
                         // given expression is the self argument and will be handled completely by the compiler
                         // i.e.: `|x| x.is_something()`
                         ExprKind::MethodCall(_, [self_expr, ..], _) if self_expr.hir_id == cmt.hir_id => {
-                            self.suggestion_start
-                                .push_str(&format!("{}{}", start_snip, ident_str_with_proj));
+                            let _ = write!(self.suggestion_start, "{}{}", start_snip, ident_str_with_proj);
                             self.next_pos = span.hi();
                             return;
                         },
@@ -1026,8 +1025,7 @@ fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {
                     }
                 }
 
-                self.suggestion_start
-                    .push_str(&format!("{}{}", start_snip, replacement_str));
+                let _ = write!(self.suggestion_start, "{}{}", start_snip, replacement_str);
             }
             self.next_pos = span.hi();
         }
index e3fc76f4e1a26b16754cb1318df3745a8b1989a0..901e3e5390c5dbbe9a4ac6d54a6adaaa0a737ec7 100644 (file)
@@ -3,11 +3,11 @@
 #![allow(clippy::module_name_repetitions)]
 
 use rustc_ast::ast::Mutability;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::{Expr, TyKind, Unsafety};
+use rustc_hir::{Expr, LangItem, TyKind, Unsafety};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
 use rustc_middle::mir::interpret::{ConstValue, Scalar};
@@ -22,7 +22,7 @@
 use rustc_trait_selection::traits::query::normalize::AtExt;
 use std::iter;
 
-use crate::{match_def_path, must_use_attr, path_res};
+use crate::{match_def_path, must_use_attr, path_res, paths};
 
 // Checks if the given type implements copy.
 pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
@@ -83,6 +83,20 @@ pub fn get_associated_type<'tcx>(
         })
 }
 
+/// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type
+/// implements a trait marked with a diagnostic item use [`implements_trait`].
+///
+/// For a further exploitation what diagnostic items are see [diagnostic items] in
+/// rustc-dev-guide.
+///
+/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
+pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symbol> {
+    match ty.kind() {
+        ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()),
+        _ => None,
+    }
+}
+
 /// Returns true if ty has `iter` or `iter_mut` methods
 pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
     // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
@@ -319,6 +333,57 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
     }
 }
 
+/// Checks if the drop order for a type matters. Some std types implement drop solely to
+/// deallocate memory. For these types, and composites containing them, changing the drop order
+/// won't result in any observable side effects.
+pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+    fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
+        if !seen.insert(ty) {
+            return false;
+        }
+        if !ty.has_significant_drop(cx.tcx, cx.param_env) {
+            false
+        }
+        // Check for std types which implement drop, but only for memory allocation.
+        else if is_type_lang_item(cx, ty, LangItem::OwnedBox)
+            || matches!(
+                get_type_diagnostic_name(cx, ty),
+                Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type)
+            )
+            || match_type(cx, ty, &paths::WEAK_RC)
+            || match_type(cx, ty, &paths::WEAK_ARC)
+        {
+            // Check all of the generic arguments.
+            if let ty::Adt(_, subs) = ty.kind() {
+                subs.types().any(|ty| needs_ordered_drop_inner(cx, ty, seen))
+            } else {
+                true
+            }
+        } else if !cx
+            .tcx
+            .lang_items()
+            .drop_trait()
+            .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+        {
+            // This type doesn't implement drop, so no side effects here.
+            // Check if any component type has any.
+            match ty.kind() {
+                ty::Tuple(fields) => fields.iter().any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
+                ty::Array(ty, _) => needs_ordered_drop_inner(cx, *ty, seen),
+                ty::Adt(adt, subs) => adt
+                    .all_fields()
+                    .map(|f| f.ty(cx.tcx, subs))
+                    .any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
+                _ => true,
+            }
+        } else {
+            true
+        }
+    }
+
+    needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
+}
+
 /// Peels off all references on the type. Returns the underlying type and the number of references
 /// removed.
 pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
index 405e306359bc9b2dfe46278b43c4d5e3c6307a90..4236e3aae2fbde4f811106b27547ba1f8276648b 100644 (file)
@@ -3,7 +3,7 @@
 use rustc_hir as hir;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::HirIdSet;
-use rustc_hir::{Expr, ExprKind, HirId};
+use rustc_hir::{Expr, ExprKind, HirId, Node};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
 use rustc_middle::hir::nested_filter;
@@ -169,6 +169,32 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
 
 pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool {
     let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false };
+
+    // for _ in 1..3 {
+    //    local
+    // }
+    //
+    // let closure = || local;
+    // closure();
+    // closure();
+    let in_loop_or_closure = cx
+        .tcx
+        .hir()
+        .parent_iter(after.hir_id)
+        .take_while(|&(id, _)| id != block.hir_id)
+        .any(|(_, node)| {
+            matches!(
+                node,
+                Node::Expr(Expr {
+                    kind: ExprKind::Loop(..) | ExprKind::Closure(..),
+                    ..
+                })
+            )
+        });
+    if in_loop_or_closure {
+        return true;
+    }
+
     let mut used_after_expr = false;
     let mut past_expr = false;
     expr_visitor(cx, |expr| {
@@ -178,7 +204,10 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr
 
         if expr.hir_id == after.hir_id {
             past_expr = true;
-        } else if past_expr && utils::path_to_local_id(expr, local_id) {
+            return false;
+        }
+
+        if past_expr && utils::path_to_local_id(expr, local_id) {
             used_after_expr = true;
         }
         !used_after_expr
index 40451b17a9c63ce30693fbf2ab7147b2a020f284..c00bc2bd213f9a4fd3c8469fe16f0273167b1191 100644 (file)
@@ -1,9 +1,11 @@
 use crate::path_to_local_id;
+use core::ops::ControlFlow;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
 use rustc_hir::{
-    Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, Unsafety,
+    Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, UnsafeSource,
+    Unsafety,
 };
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
@@ -370,3 +372,67 @@ fn visit_nested_item(&mut self, id: ItemId) {
     v.visit_expr(e);
     v.is_unsafe
 }
+
+/// Checks if the given expression contains an unsafe block
+pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
+    struct V<'cx, 'tcx> {
+        cx: &'cx LateContext<'tcx>,
+        found_unsafe: bool,
+    }
+    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
+        type NestedFilter = nested_filter::OnlyBodies;
+        fn nested_visit_map(&mut self) -> Self::Map {
+            self.cx.tcx.hir()
+        }
+
+        fn visit_block(&mut self, b: &'tcx Block<'_>) {
+            if self.found_unsafe {
+                return;
+            }
+            if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
+                self.found_unsafe = true;
+                return;
+            }
+            walk_block(self, b);
+        }
+    }
+    let mut v = V {
+        cx,
+        found_unsafe: false,
+    };
+    v.visit_expr(e);
+    v.found_unsafe
+}
+
+/// Runs the given function for each sub-expression producing the final value consumed by the parent
+/// of the give expression.
+///
+/// e.g. for the following expression
+/// ```rust,ignore
+/// if foo {
+///     f(0)
+/// } else {
+///     1 + 1
+/// }
+/// ```
+/// this will pass both `f(0)` and `1+1` to the given function.
+pub fn for_each_value_source<'tcx, B>(
+    e: &'tcx Expr<'tcx>,
+    f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
+) -> ControlFlow<B> {
+    match e.kind {
+        ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
+        ExprKind::Match(_, arms, _) => {
+            for arm in arms {
+                for_each_value_source(arm.body, f)?;
+            }
+            ControlFlow::Continue(())
+        },
+        ExprKind::If(_, if_expr, Some(else_expr)) => {
+            for_each_value_source(if_expr, f)?;
+            for_each_value_source(else_expr, f)
+        },
+        ExprKind::DropTemps(e) => for_each_value_source(e, f),
+        _ => f(e),
+    }
+}
index cf16a1d5d3dcf5b09538f3d55da4dd6f3e22341d..307cf2f3a9047896fd3912c029e95745c0b29ff2 100644 (file)
@@ -22,6 +22,7 @@ because that's clearly a non-descriptive name.
   - [Adding the lint logic](#adding-the-lint-logic)
   - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv)
   - [Author lint](#author-lint)
+  - [Print HIR lint](#print-hir-lint)
   - [Documentation](#documentation)
   - [Running rustfmt](#running-rustfmt)
   - [Debugging](#debugging)
@@ -484,6 +485,19 @@ you are implementing your lint.
 
 [author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
 
+## Print HIR lint
+
+To implement a lint, it's helpful to first understand the internal representation
+that rustc uses. Clippy has the `#[clippy::dump]` attribute that prints the
+[_High-Level Intermediate Representation (HIR)_] of the item, statement, or 
+expression that the attribute is attached to. To attach the attribute to expressions
+you often need to enable `#![feature(stmt_expr_attributes)]`.
+
+[Here][print_hir_example] you can find an example, just select _Tools_ and run _Clippy_.
+
+[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html
+[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb
+
 ## Documentation
 
 The final thing before submitting our PR is to add some documentation to our
index 9af8dcc7726f080906adcc1c8ed2b1bc2b711175..816efbdaedf36d770aab843378175781f4274fb6 100644 (file)
@@ -8,6 +8,7 @@
 #![allow(clippy::collapsible_else_if)]
 
 use std::ffi::OsStr;
+use std::fmt::Write as _;
 use std::process::Command;
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::{collections::HashMap, io::ErrorKind};
@@ -110,11 +111,12 @@ fn to_output(&self, markdown: bool) -> String {
             let lint = format!("`{}`", self.linttype);
 
             let mut output = String::from("| ");
-            output.push_str(&format!(
+            let _ = write!(
+                output,
                 "[`{}`](../target/lintcheck/sources/{}#L{})",
                 file_with_pos, file, self.line
-            ));
-            output.push_str(&format!(r#" | {:<50} | "{}" |"#, lint, self.message));
+            );
+            let _ = write!(output, r#" | {:<50} | "{}" |"#, lint, self.message);
             output.push('\n');
             output
         } else {
@@ -304,7 +306,7 @@ fn run_clippy_lints(
         let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir");
 
         let mut args = if fix {
-            vec!["--fix", "--allow-no-vcs", "--"]
+            vec!["--fix", "--"]
         } else {
             vec!["--", "--message-format=json", "--"]
         };
@@ -391,7 +393,7 @@ struct LintcheckConfig {
     lintcheck_results_path: PathBuf,
     /// whether to just run --fix and not collect all the warnings
     fix: bool,
-    /// A list of lint that this lintcheck run shound focus on
+    /// A list of lints that this lintcheck run should focus on
     lint_filter: Vec<String>,
     /// Indicate if the output should support markdown syntax
     markdown: bool,
@@ -835,10 +837,11 @@ pub fn main() {
         text.push_str("| file | lint | message |\n");
         text.push_str("| --- | --- | --- |\n");
     }
-    text.push_str(&format!("{}", all_msgs.join("")));
+    write!(text, "{}", all_msgs.join(""));
     text.push_str("\n\n### ICEs:\n");
-    ices.iter()
-        .for_each(|(cratename, msg)| text.push_str(&format!("{}: '{}'", cratename, msg)));
+    for (cratename, msg) in ices.iter() {
+        let _ = write!(text, "{}: '{}'", cratename, msg);
+    }
 
     println!("Writing logs to {}", config.lintcheck_results_path.display());
     std::fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap();
index bb29c71e9f455889ff8cb1f001ee916fb273b0c1..03acb51306d7a92081b181f66bbb964bbca66a76 100644 (file)
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2022-04-07"
+channel = "nightly-2022-05-05"
 components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
index 32a09fdb9d9ffe6b407ea415b95487f863920367..7de40fe63ac23a4559051c6f14c26b34ad4c067a 100644 (file)
@@ -165,8 +165,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
     // Separate the output with an empty line
     eprintln!();
 
-    let fallback_bundle =
-        rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+    let fallback_bundle = rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
     let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
         rustc_errors::ColorConfig::Auto,
         None,
index 67af9d05bf402473a3cc6d34080872e15c30ec61..eb97d1933d5d71bdd8621993e7cd6a50538d982d 100644 (file)
@@ -80,9 +80,13 @@ fn run_clippy_for_package(project: &str) {
         .args(&["-D", "clippy::pedantic"])
         .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
 
-    // internal lints only exist if we build with the internal feature
     if cfg!(feature = "internal") {
+        // internal lints only exist if we build with the internal feature
         command.args(&["-D", "clippy::internal"]);
+    } else {
+        // running a clippy built without internal lints on the clippy source
+        // that contains e.g. `allow(clippy::invalid_paths)`
+        command.args(&["-A", "unknown_lints"]);
     }
 
     let output = command.output().unwrap();
index dc82ba891fb1f098d2c1b1099c5a0453ec67b311..dd1d441203600cf6f3d124177a4ef29f1ea0d67c 100644 (file)
@@ -66,7 +66,7 @@ fn lint_message_convention() {
 
     // make sure that lint messages:
     // * are not capitalized
-    // * don't have puncuation at the end of the last sentence
+    // * don't have punctuation at the end of the last sentence
 
     // these directories have interesting tests
     let test_dirs = ["ui", "ui-cargo", "ui-internal", "ui-toml"]
index 558d129916078b4d4883203ec7f311dc5d41cc11..0852fe65aafd0241e2938cef70a1cdee20964720 100644 (file)
@@ -29,7 +29,7 @@ LL | |             db.help(help_msg);
 LL | |         });
    | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)`
 
-error: this call is collspible
+error: this call is collapsible
   --> $DIR/collapsible_span_lint_calls.rs:45:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
@@ -37,7 +37,7 @@ LL | |             db.span_note(expr.span, note_msg);
 LL | |         });
    | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)`
 
-error: this call is collspible
+error: this call is collapsible
   --> $DIR/collapsible_span_lint_calls.rs:48:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
index 6b7fd6efe394c0ebdf597fa5a4b6cc740fadfa61..eaea218e128886373d6c4ebc82fe5e377a831ce8 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 #![deny(clippy::internal)]
-#![allow(clippy::missing_clippy_version_attribute)]
+#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)]
 #![feature(rustc_private)]
 
 extern crate rustc_span;
index 98d7d7adad17037fbdc275cf9500582717b0bd71..7efebb8fae486f456a3ade83ac5c510acb81fdc4 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 #![deny(clippy::internal)]
-#![allow(clippy::missing_clippy_version_attribute)]
+#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)]
 #![feature(rustc_private)]
 
 extern crate rustc_span;
diff --git a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs
new file mode 100644 (file)
index 0000000..fbef5c4
--- /dev/null
@@ -0,0 +1,41 @@
+#![warn(clippy::await_holding_invalid_type)]
+use std::net::Ipv4Addr;
+
+async fn bad() -> u32 {
+    let _x = String::from("hello");
+    baz().await
+}
+
+async fn bad_reason() -> u32 {
+    let _x = Ipv4Addr::new(127, 0, 0, 1);
+    baz().await
+}
+
+async fn good() -> u32 {
+    {
+        let _x = String::from("hi!");
+        let _y = Ipv4Addr::new(127, 0, 0, 1);
+    }
+    baz().await;
+    let _x = String::from("hi!");
+    47
+}
+
+async fn baz() -> u32 {
+    42
+}
+
+#[allow(clippy::manual_async_fn)]
+fn block_bad() -> impl std::future::Future<Output = u32> {
+    async move {
+        let _x = String::from("hi!");
+        baz().await
+    }
+}
+
+fn main() {
+    good();
+    bad();
+    bad_reason();
+    block_bad();
+}
diff --git a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr
new file mode 100644 (file)
index 0000000..62c45b5
--- /dev/null
@@ -0,0 +1,25 @@
+error: `std::string::String` may not be held across an `await` point per `clippy.toml`
+  --> $DIR/await_holding_invalid_type.rs:5:9
+   |
+LL |     let _x = String::from("hello");
+   |         ^^
+   |
+   = note: `-D clippy::await-holding-invalid-type` implied by `-D warnings`
+   = note: strings are bad
+
+error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml`
+  --> $DIR/await_holding_invalid_type.rs:10:9
+   |
+LL |     let _x = Ipv4Addr::new(127, 0, 0, 1);
+   |         ^^
+
+error: `std::string::String` may not be held across an `await` point per `clippy.toml`
+  --> $DIR/await_holding_invalid_type.rs:31:13
+   |
+LL |         let _x = String::from("hi!");
+   |             ^^
+   |
+   = note: strings are bad
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/clippy.toml b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/clippy.toml
new file mode 100644 (file)
index 0000000..7999009
--- /dev/null
@@ -0,0 +1,4 @@
+await-holding-invalid-types = [
+    { path = "std::string::String", reason = "strings are bad" },
+    "std::net::Ipv4Addr",
+]
index e678c896fd3e3b3dc8e7bcba92fb5b641a6f8797..4ac0378544c7d724ac4f49e28a7168791e21044a 100644 (file)
@@ -1,4 +1,5 @@
 #![warn(clippy::too_many_lines)]
+#![allow(clippy::let_unit_value)]
 
 // This function should be considered one line.
 fn many_comments_but_one_line_of_code() {
index d736bf899735a5cfb5ed02eef151b4c20cbd635f..dc255bdcabafea568c725c28f65b79074ec024a4 100644 (file)
@@ -1,5 +1,5 @@
 error: this function has too many lines (2/1)
-  --> $DIR/test.rs:18:1
+  --> $DIR/test.rs:19:1
    |
 LL | / fn too_many_lines() {
 LL | |     println!("This is bad.");
@@ -10,7 +10,7 @@ LL | | }
    = note: `-D clippy::too-many-lines` implied by `-D warnings`
 
 error: this function has too many lines (4/1)
-  --> $DIR/test.rs:24:1
+  --> $DIR/test.rs:25:1
    |
 LL | / async fn async_too_many_lines() {
 LL | |     println!("This is bad.");
@@ -19,7 +19,7 @@ LL | | }
    | |_^
 
 error: this function has too many lines (4/1)
-  --> $DIR/test.rs:30:1
+  --> $DIR/test.rs:31:1
    |
 LL | / fn closure_too_many_lines() {
 LL | |     let _ = {
@@ -30,7 +30,7 @@ LL | | }
    | |_^
 
 error: this function has too many lines (2/1)
-  --> $DIR/test.rs:52:1
+  --> $DIR/test.rs:53:1
    |
 LL | / fn comment_before_code() {
 LL | |     let _ = "test";
diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/clippy.toml b/src/tools/clippy/tests/ui-toml/large_include_file/clippy.toml
new file mode 100644 (file)
index 0000000..ea34bf9
--- /dev/null
@@ -0,0 +1 @@
+max-include-file-size = 600
diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs
new file mode 100644 (file)
index 0000000..f3dbb6a
--- /dev/null
@@ -0,0 +1,16 @@
+#![warn(clippy::large_include_file)]
+
+// Good
+const GOOD_INCLUDE_BYTES: &[u8; 581] = include_bytes!("large_include_file.rs");
+const GOOD_INCLUDE_STR: &str = include_str!("large_include_file.rs");
+
+#[allow(clippy::large_include_file)]
+const ALLOWED_TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
+#[allow(clippy::large_include_file)]
+const ALLOWED_TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
+
+// Bad
+const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
+const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr
new file mode 100644 (file)
index 0000000..6a685a5
--- /dev/null
@@ -0,0 +1,21 @@
+error: attempted to include a large file
+  --> $DIR/large_include_file.rs:13:43
+   |
+LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
+   |                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::large-include-file` implied by `-D warnings`
+   = note: the configuration allows a maximum size of 600 bytes
+   = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: attempted to include a large file
+  --> $DIR/large_include_file.rs:14:35
+   |
+LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: the configuration allows a maximum size of 600 bytes
+   = note: this error originates in the macro `include_str` (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-toml/large_include_file/too_big.txt b/src/tools/clippy/tests/ui-toml/large_include_file/too_big.txt
new file mode 100644 (file)
index 0000000..9829c46
--- /dev/null
@@ -0,0 +1 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Maecenas accumsan lacus vel facilisis volutpat. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Tellus id interdum velit laoreet id donec ultrices. Est ultricies integer quis auctor elit sed vulputate. Erat velit scelerisque in dictum non consectetur a erat nam. Sed blandit libero volutpat sed. Tortor condimentum lacinia quis vel eros. Enim ut tellus elementum sagittis vitae et leo duis. Congue mauris rhoncus aenean vel elit scelerisque. Id consectetur purus ut faucibus pulvinar elementum integer.
\ No newline at end of file
index a1e7361c0cb32fe4bbe9e9c5a09320691b3b123a..5dae5af7eb5a9cc9413582d37256b2869abd32c8 100644 (file)
@@ -1,4 +1,4 @@
-error: you are using an explicit closure for copying elements
+error: you are using an explicit closure for cloning elements
   --> $DIR/min_rust_version.rs:74:26
    |
 LL |     let _: Option<u64> = Some(&16).map(|b| *b);
index 00ddbd608a7c725dbee190ad54a3cc3c2c6dd88e..8701809b4daaaa8901bafbfc9479ac4874244899 100644 (file)
@@ -1,4 +1,4 @@
-error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `third-party` at line 5 column 1
+error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `await-holding-invalid-types`, `max-include-file-size`, `third-party` at line 5 column 1
 
 error: aborting due to previous error
 
index 7477c01ca78283e173392dd12c86c40a210ebe8d..7bea9563d47d3d1f34b81de73794aea329f10520 100644 (file)
@@ -1,4 +1,4 @@
-#![allow(non_fmt_panics)]
+#![allow(non_fmt_panics, clippy::needless_bool)]
 
 macro_rules! assert_const {
     ($len:expr) => {
@@ -28,6 +28,12 @@ fn main() {
     assert_const!(3);
     assert_const!(-1);
 
-    // Don't lint on this:
+    // Don't lint if based on `cfg!(..)`:
     assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf")));
+
+    let flag: bool = cfg!(not(feature = "asdf"));
+    assert!(flag);
+
+    const CFG_FLAG: &bool = &cfg!(feature = "hey");
+    assert!(!CFG_FLAG);
 }
index 4b7b7fec78fe8dd7dc9e1941c3871976c60de176..ed7b17651e675e8942bceb5867087398b282eff2 100644 (file)
@@ -13,7 +13,7 @@
 
 #[proc_macro_derive(DeriveSomething)]
 pub fn derive(_: TokenStream) -> TokenStream {
-    // Shound not trigger `used_underscore_binding`
+    // Should not trigger `used_underscore_binding`
     let _inside_derive = 1;
     assert_eq!(_inside_derive, _inside_derive);
 
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
new file mode 100644 (file)
index 0000000..8ea631f
--- /dev/null
@@ -0,0 +1,32 @@
+// 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/bytes_count_to_len.fixed b/src/tools/clippy/tests/ui/bytes_count_to_len.fixed
new file mode 100644 (file)
index 0000000..8606423
--- /dev/null
@@ -0,0 +1,34 @@
+// run-rustfix
+#![warn(clippy::bytes_count_to_len)]
+use std::fs::File;
+use std::io::Read;
+
+fn main() {
+    // should fix, because type is String
+    let _ = String::from("foo").len();
+
+    let s1 = String::from("foo");
+    let _ = s1.len();
+
+    // should fix, because type is &str
+    let _ = "foo".len();
+
+    let s2 = "foo";
+    let _ = s2.len();
+
+    // make sure using count() normally doesn't trigger warning
+    let vector = [0, 1, 2];
+    let _ = vector.iter().count();
+
+    // The type is slice, so should not fix
+    let _ = &[1, 2, 3].bytes().count();
+
+    let bytes: &[u8] = &[1, 2, 3];
+    bytes.bytes().count();
+
+    // The type is File, so should not fix
+    let _ = File::open("foobar").unwrap().bytes().count();
+
+    let f = File::open("foobar").unwrap();
+    let _ = f.bytes().count();
+}
diff --git a/src/tools/clippy/tests/ui/bytes_count_to_len.rs b/src/tools/clippy/tests/ui/bytes_count_to_len.rs
new file mode 100644 (file)
index 0000000..162730c
--- /dev/null
@@ -0,0 +1,34 @@
+// run-rustfix
+#![warn(clippy::bytes_count_to_len)]
+use std::fs::File;
+use std::io::Read;
+
+fn main() {
+    // should fix, because type is String
+    let _ = String::from("foo").bytes().count();
+
+    let s1 = String::from("foo");
+    let _ = s1.bytes().count();
+
+    // should fix, because type is &str
+    let _ = "foo".bytes().count();
+
+    let s2 = "foo";
+    let _ = s2.bytes().count();
+
+    // make sure using count() normally doesn't trigger warning
+    let vector = [0, 1, 2];
+    let _ = vector.iter().count();
+
+    // The type is slice, so should not fix
+    let _ = &[1, 2, 3].bytes().count();
+
+    let bytes: &[u8] = &[1, 2, 3];
+    bytes.bytes().count();
+
+    // The type is File, so should not fix
+    let _ = File::open("foobar").unwrap().bytes().count();
+
+    let f = File::open("foobar").unwrap();
+    let _ = f.bytes().count();
+}
diff --git a/src/tools/clippy/tests/ui/bytes_count_to_len.stderr b/src/tools/clippy/tests/ui/bytes_count_to_len.stderr
new file mode 100644 (file)
index 0000000..224deb7
--- /dev/null
@@ -0,0 +1,28 @@
+error: using long and hard to read `.bytes().count()`
+  --> $DIR/bytes_count_to_len.rs:8:13
+   |
+LL |     let _ = String::from("foo").bytes().count();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `String::from("foo").len()`
+   |
+   = note: `-D clippy::bytes-count-to-len` implied by `-D warnings`
+
+error: using long and hard to read `.bytes().count()`
+  --> $DIR/bytes_count_to_len.rs:11:13
+   |
+LL |     let _ = s1.bytes().count();
+   |             ^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `s1.len()`
+
+error: using long and hard to read `.bytes().count()`
+  --> $DIR/bytes_count_to_len.rs:14:13
+   |
+LL |     let _ = "foo".bytes().count();
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `"foo".len()`
+
+error: using long and hard to read `.bytes().count()`
+  --> $DIR/bytes_count_to_len.rs:17:13
+   |
+LL |     let _ = s2.bytes().count();
+   |             ^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `s2.len()`
+
+error: aborting due to 4 previous errors
+
index cf85a5ca931dd1e560134cf39304659254af3b43..e6031e9adaeb66c084d8c96cb421d9d5ee186d91 100644 (file)
@@ -1,13 +1,13 @@
 #![feature(repr128)]
 #![allow(incomplete_features)]
-
-#[warn(
+#![warn(
     clippy::cast_precision_loss,
     clippy::cast_possible_truncation,
     clippy::cast_sign_loss,
     clippy::cast_possible_wrap
 )]
-#[allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)]
+#![allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)]
+
 fn main() {
     // Test clippy::cast_precision_loss
     let x0 = 1i32;
@@ -252,3 +252,11 @@ fn test(self) {
         }
     }
 }
+
+fn avoid_subtract_overflow(q: u32) {
+    let c = (q >> 16) as u8;
+    c as usize;
+
+    let c = (q / 1000) as u8;
+    c as usize;
+}
index 7a68c0984f140dda78c8c6d5b927b6867b76beac..0c63b4af30865da871c4d9b241fd9f6d9dc979aa 100644 (file)
@@ -194,5 +194,17 @@ error: casting `main::E10` to `u16` may truncate the value
 LL |             let _ = self as u16;
    |                     ^^^^^^^^^^^
 
-error: aborting due to 31 previous errors
+error: casting `u32` to `u8` may truncate the value
+  --> $DIR/cast.rs:257:13
+   |
+LL |     let c = (q >> 16) as u8;
+   |             ^^^^^^^^^^^^^^^
+
+error: casting `u32` to `u8` may truncate the value
+  --> $DIR/cast.rs:260:13
+   |
+LL |     let c = (q / 1000) as u8;
+   |             ^^^^^^^^^^^^^^^^
+
+error: aborting due to 33 previous errors
 
index e4e7290a30e9e711c911c34d96a1239af8e0d19a..95bb883df1bf10877db0fff7a242606284dc55a8 100644 (file)
@@ -44,8 +44,8 @@ fn main() {
         let _ = core::ptr::read_unaligned(ptr as *const u16);
         let _ = core::intrinsics::unaligned_volatile_load(ptr as *const u16);
         let ptr = &mut data as *mut [u8; 2] as *mut u8;
-        let _ = (ptr as *mut u16).write_unaligned(0);
-        let _ = core::ptr::write_unaligned(ptr as *mut u16, 0);
-        let _ = core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0);
+        (ptr as *mut u16).write_unaligned(0);
+        core::ptr::write_unaligned(ptr as *mut u16, 0);
+        core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0);
     }
 }
index cfe1cca2eba9490ffb277a4d18dc21a71910ffeb..24d7eb28a197aa03b75fd663cbeda11339ee8c4a 100644 (file)
@@ -1,3 +1,5 @@
+#![allow(clippy::let_unit_value)]
+
 fn main() {
     let x: [i32; 3] = [1_i32, 2, 3];
     let r_x = &x;
@@ -37,3 +39,44 @@ fn main() {
     let long_chain_restore =
         r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8] as *const [u32];
 }
+
+// foo and foo2 should not fire, they're the same size
+fn foo(x: *mut [u8]) -> *mut [u8] {
+    x as *mut [u8]
+}
+
+fn foo2(x: *mut [u8]) -> *mut [u8] {
+    x as *mut _
+}
+
+// Test that casts as part of function returns work
+fn bar(x: *mut [u16]) -> *mut [u8] {
+    x as *mut [u8]
+}
+
+fn uwu(x: *mut [u16]) -> *mut [u8] {
+    x as *mut _
+}
+
+fn bar2(x: *mut [u16]) -> *mut [u8] {
+    x as _
+}
+
+// constify
+fn bar3(x: *mut [u16]) -> *const [u8] {
+    x as _
+}
+
+// unconstify
+fn bar4(x: *const [u16]) -> *mut [u8] {
+    x as _
+}
+
+// function returns plus blocks
+fn blocks(x: *mut [u16]) -> *mut [u8] {
+    ({ x }) as _
+}
+
+fn more_blocks(x: *mut [u16]) -> *mut [u8] {
+    { ({ x }) as _ }
+}
index a37cec7cb3be0935e00772d85edc5d339e45005c..40721dcd05d5dbc909d140a82d70de3bfb28ddc7 100644 (file)
@@ -1,5 +1,5 @@
 error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
-  --> $DIR/cast_slice_different_sizes.rs:7:13
+  --> $DIR/cast_slice_different_sizes.rs:9:13
    |
 LL |     let b = a as *const [u8];
    |             ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(a as *const u8, ..)`
@@ -7,25 +7,25 @@ LL |     let b = a as *const [u8];
    = note: `#[deny(clippy::cast_slice_different_sizes)]` on by default
 
 error: casting between raw pointers to `[u8]` (element size 1) and `[u32]` (element size 4) does not adjust the count
-  --> $DIR/cast_slice_different_sizes.rs:8:13
+  --> $DIR/cast_slice_different_sizes.rs:10:13
    |
 LL |     let c = b as *const [u32];
    |             ^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(b as *const u32, ..)`
 
 error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
-  --> $DIR/cast_slice_different_sizes.rs:11:16
+  --> $DIR/cast_slice_different_sizes.rs:13:16
    |
 LL |     let loss = r_x as *const [i32] as *const [u8];
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)`
 
 error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
-  --> $DIR/cast_slice_different_sizes.rs:18:24
+  --> $DIR/cast_slice_different_sizes.rs:20:24
    |
 LL |     let loss_block_1 = { r_x as *const [i32] } as *const [u8];
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts({ r_x as *const [i32] } as *const u8, ..)`
 
 error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
-  --> $DIR/cast_slice_different_sizes.rs:19:24
+  --> $DIR/cast_slice_different_sizes.rs:21:24
    |
 LL |       let loss_block_2 = {
    |  ________________________^
@@ -43,10 +43,79 @@ LL ~     } as *const u8, ..);
    |
 
 error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
-  --> $DIR/cast_slice_different_sizes.rs:36:27
+  --> $DIR/cast_slice_different_sizes.rs:38:27
    |
 LL |     let long_chain_loss = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8];
-   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const u8, ..)`
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)`
 
-error: aborting due to 6 previous errors
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+  --> $DIR/cast_slice_different_sizes.rs:53:36
+   |
+LL |   fn bar(x: *mut [u16]) -> *mut [u8] {
+   |  ____________________________________^
+LL | |     x as *mut [u8]
+LL | | }
+   | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+  --> $DIR/cast_slice_different_sizes.rs:57:36
+   |
+LL |   fn uwu(x: *mut [u16]) -> *mut [u8] {
+   |  ____________________________________^
+LL | |     x as *mut _
+LL | | }
+   | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+  --> $DIR/cast_slice_different_sizes.rs:61:37
+   |
+LL |   fn bar2(x: *mut [u16]) -> *mut [u8] {
+   |  _____________________________________^
+LL | |     x as _
+LL | | }
+   | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+  --> $DIR/cast_slice_different_sizes.rs:66:39
+   |
+LL |   fn bar3(x: *mut [u16]) -> *const [u8] {
+   |  _______________________________________^
+LL | |     x as _
+LL | | }
+   | |_^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(x as *const u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+  --> $DIR/cast_slice_different_sizes.rs:71:39
+   |
+LL |   fn bar4(x: *const [u16]) -> *mut [u8] {
+   |  _______________________________________^
+LL | |     x as _
+LL | | }
+   | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+  --> $DIR/cast_slice_different_sizes.rs:76:39
+   |
+LL |   fn blocks(x: *mut [u16]) -> *mut [u8] {
+   |  _______________________________________^
+LL | |     ({ x }) as _
+LL | | }
+   | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+  --> $DIR/cast_slice_different_sizes.rs:80:44
+   |
+LL |   fn more_blocks(x: *mut [u16]) -> *mut [u8] {
+   |  ____________________________________________^
+LL | |     { ({ x }) as _ }
+LL | | }
+   | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+  --> $DIR/cast_slice_different_sizes.rs:81:5
+   |
+LL |     { ({ x }) as _ }
+   |     ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)`
+
+error: aborting due to 14 previous errors
 
index bb6c4c0703d5168579216e7b37fd571e94570d52..d6a5a78506791a0e782c4c4911091d4f96ef7104 100644 (file)
@@ -75,3 +75,10 @@ fn main() {
         }
     }
 }
+
+#[rustfmt::skip]
+#[allow(dead_code)]
+fn issue_7318() {
+    if true { println!("I've been resolved!")
+    }else if false {}
+}
index 6d4f688db8c0a7a80114ea1e2842f914fc94881c..4399fc8b2bd1d4b760828576d4d09055a6aaeabd 100644 (file)
@@ -89,3 +89,12 @@ fn main() {
         }
     }
 }
+
+#[rustfmt::skip]
+#[allow(dead_code)]
+fn issue_7318() {
+    if true { println!("I've been resolved!")
+    }else{
+        if false {}
+    }
+}
index 6970f66097908ee23aa202b1f86370f6cd742646..45b2094c9948b6961e4da37a27bc5ce631ccb56c 100644 (file)
@@ -150,5 +150,14 @@ LL +         println!("!")
 LL +     }
    |
 
-error: aborting due to 7 previous errors
+error: this `else { if .. }` block can be collapsed
+  --> $DIR/collapsible_else_if.rs:97:10
+   |
+LL |       }else{
+   |  __________^
+LL | |         if false {}
+LL | |     }
+   | |_____^ help: collapse nested if block: `if false {}`
+
+error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/ice-8681-aux.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-8681-aux.rs
new file mode 100644 (file)
index 0000000..95b6315
--- /dev/null
@@ -0,0 +1,6 @@
+pub fn foo(x: &u32) -> u32 {
+    /* Safety:
+     * This is totally ok.
+     */
+    unsafe { *(x as *const u32) }
+}
index 6b1ceb5056933df6ee6bc782f118fcad3c1a7e72..c6298139601625392c47a5bd3614d3acd5ef1038 100644 (file)
@@ -1,4 +1,4 @@
-#[allow(dead_code)]
+#![allow(dead_code, clippy::extra_unused_lifetimes)]
 
 /// Test for https://github.com/rust-lang/rust-clippy/issues/2865
 
index fef4d7db84ddf0d23a5671dab0d0791b6bf90db5..268ba86fc7aa80756b1aa151b1aff72949a4d75c 100644 (file)
@@ -1,4 +1,4 @@
-/// Test for https://github.com/rust-lang/rust-clippy/issues/2865
+/// Test for https://github.com/rust-lang/rust-clippy/issues/3151
 
 #[derive(Clone)]
 pub struct HashMap<V, S> {
index 5caf29c619735384ea91d3b304dcd490a12c57eb..ce46bc1acc1b7fbd718f5ffe8de0f19c38e0fe1e 100644 (file)
@@ -1,4 +1,5 @@
 #![warn(clippy::repeat_once)]
+#![allow(clippy::let_unit_value)]
 
 trait Repeat {
     fn repeat(&self) {}
index 04ea4456656525c570cd1a39eb4348119ab291f4..8ed8f3b3a0642f2bea6f9da9a5fd19c08ce850e0 100644 (file)
@@ -1,11 +1,3 @@
-error: manual implementation of `split_once`
-  --> $DIR/ice-8250.rs:2:13
-   |
-LL |     let _ = s[1..].splitn(2, '.').next()?;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s[1..].split_once('.').map_or(s[1..], |x| x.0)`
-   |
-   = note: `-D clippy::manual-split-once` implied by `-D warnings`
-
 error: unnecessary use of `splitn`
   --> $DIR/ice-8250.rs:2:13
    |
@@ -14,5 +6,5 @@ LL |     let _ = s[1..].splitn(2, '.').next()?;
    |
    = note: `-D clippy::needless-splitn` implied by `-D warnings`
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
diff --git a/src/tools/clippy/tests/ui/crashes/ice-8681.rs b/src/tools/clippy/tests/ui/crashes/ice-8681.rs
new file mode 100644 (file)
index 0000000..ee14f01
--- /dev/null
@@ -0,0 +1,10 @@
+// aux-build: ice-8681-aux.rs
+
+#![warn(clippy::undocumented_unsafe_blocks)]
+
+#[path = "auxiliary/ice-8681-aux.rs"]
+mod ice_8681_aux;
+
+fn main() {
+    let _ = ice_8681_aux::foo(&0u32);
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-96721.rs b/src/tools/clippy/tests/ui/crashes/ice-96721.rs
new file mode 100644 (file)
index 0000000..4b3fb76
--- /dev/null
@@ -0,0 +1,10 @@
+macro_rules! foo {
+    () => {
+        "bar.rs"
+    };
+}
+
+#[path = foo!()] //~ ERROR malformed `path` attribute
+mod abc {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-96721.stderr b/src/tools/clippy/tests/ui/crashes/ice-96721.stderr
new file mode 100644 (file)
index 0000000..78c567b
--- /dev/null
@@ -0,0 +1,8 @@
+error: malformed `path` attribute input
+  --> $DIR/ice-96721.rs:7:1
+   |
+LL | #[path = foo!()] //~ ERROR malformed `path` attribute
+   | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[path = "file"]`
+
+error: aborting due to previous error
+
index e0b4a2f6942392c575e3f491914dfbf11b7cf6cd..a28bff76755b62f982bf364d3e8f3fbfaeb01db7 100644 (file)
@@ -2,12 +2,15 @@
 // aux-build:macro_rules.rs
 
 #![warn(clippy::default_numeric_fallback)]
-#![allow(unused)]
-#![allow(clippy::never_loop)]
-#![allow(clippy::no_effect)]
-#![allow(clippy::unnecessary_operation)]
-#![allow(clippy::branches_sharing_code)]
-#![allow(clippy::match_single_binding)]
+#![allow(
+    unused,
+    clippy::never_loop,
+    clippy::no_effect,
+    clippy::unnecessary_operation,
+    clippy::branches_sharing_code,
+    clippy::match_single_binding,
+    clippy::let_unit_value
+)]
 
 #[macro_use]
 extern crate macro_rules;
index 50bbb6eec6c700fdbdb7a54051e48c21715b0dbd..b48435cc7b282a049e8c4425381898e7b31b2702 100644 (file)
@@ -2,12 +2,15 @@
 // aux-build:macro_rules.rs
 
 #![warn(clippy::default_numeric_fallback)]
-#![allow(unused)]
-#![allow(clippy::never_loop)]
-#![allow(clippy::no_effect)]
-#![allow(clippy::unnecessary_operation)]
-#![allow(clippy::branches_sharing_code)]
-#![allow(clippy::match_single_binding)]
+#![allow(
+    unused,
+    clippy::never_loop,
+    clippy::no_effect,
+    clippy::unnecessary_operation,
+    clippy::branches_sharing_code,
+    clippy::match_single_binding,
+    clippy::let_unit_value
+)]
 
 #[macro_use]
 extern crate macro_rules;
index f8a2407b6933d8d6be71bf32087083eeee09b79b..f8b6c7746edbb728f1ee699b2b345bb1963ecd66 100644 (file)
@@ -1,5 +1,5 @@
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:18:17
+  --> $DIR/default_numeric_fallback_f64.rs:21:17
    |
 LL |         let x = 0.12;
    |                 ^^^^ help: consider adding suffix: `0.12_f64`
@@ -7,133 +7,133 @@ 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:19:18
+  --> $DIR/default_numeric_fallback_f64.rs:22: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:19:22
+  --> $DIR/default_numeric_fallback_f64.rs:22: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:19:26
+  --> $DIR/default_numeric_fallback_f64.rs:22: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:20:28
+  --> $DIR/default_numeric_fallback_f64.rs:23: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:20:32
+  --> $DIR/default_numeric_fallback_f64.rs:23: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:20:46
+  --> $DIR/default_numeric_fallback_f64.rs:23: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:20:50
+  --> $DIR/default_numeric_fallback_f64.rs:23: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:21:23
+  --> $DIR/default_numeric_fallback_f64.rs:24: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:22:18
+  --> $DIR/default_numeric_fallback_f64.rs:25:18
    |
 LL |             _ => 1.,
    |                  ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:40:21
+  --> $DIR/default_numeric_fallback_f64.rs:43:21
    |
 LL |             let y = 1.;
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:48:21
+  --> $DIR/default_numeric_fallback_f64.rs:51:21
    |
 LL |             let y = 1.;
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:54:21
+  --> $DIR/default_numeric_fallback_f64.rs:57: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:9
+  --> $DIR/default_numeric_fallback_f64.rs:69:9
    |
 LL |         1.
    |         ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:72:27
+  --> $DIR/default_numeric_fallback_f64.rs:75:27
    |
 LL |         let f = || -> _ { 1. };
    |                           ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:76:29
+  --> $DIR/default_numeric_fallback_f64.rs:79: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:90:21
+  --> $DIR/default_numeric_fallback_f64.rs:93:21
    |
 LL |         generic_arg(1.);
    |                     ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:93:32
+  --> $DIR/default_numeric_fallback_f64.rs:96: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:111:28
+  --> $DIR/default_numeric_fallback_f64.rs:114:28
    |
 LL |         GenericStruct { x: 1. };
    |                            ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:114:36
+  --> $DIR/default_numeric_fallback_f64.rs:117: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:132:24
+  --> $DIR/default_numeric_fallback_f64.rs:135:24
    |
 LL |         GenericEnum::X(1.);
    |                        ^^ help: consider adding suffix: `1.0_f64`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_f64.rs:152:23
+  --> $DIR/default_numeric_fallback_f64.rs:155: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:159:21
+  --> $DIR/default_numeric_fallback_f64.rs:162:21
    |
 LL |             let x = 22.;
    |                     ^^^ help: consider adding suffix: `22.0_f64`
index bded9e2c0e801723b89547d606120a989078473d..fa85d278c8fcad8c82cd3fba79a9de7d81dcd287 100644 (file)
@@ -2,11 +2,14 @@
 // aux-build:macro_rules.rs
 
 #![warn(clippy::default_numeric_fallback)]
-#![allow(unused)]
-#![allow(clippy::never_loop)]
-#![allow(clippy::no_effect)]
-#![allow(clippy::unnecessary_operation)]
-#![allow(clippy::branches_sharing_code)]
+#![allow(
+    unused,
+    clippy::never_loop,
+    clippy::no_effect,
+    clippy::unnecessary_operation,
+    clippy::branches_sharing_code,
+    clippy::let_unit_value
+)]
 
 #[macro_use]
 extern crate macro_rules;
index 3fceefa551c7843c4d1b54a36f98c3ac86bf2612..71acccd702b062de3964431c62fdf4bed3e1c88b 100644 (file)
@@ -2,11 +2,14 @@
 // aux-build:macro_rules.rs
 
 #![warn(clippy::default_numeric_fallback)]
-#![allow(unused)]
-#![allow(clippy::never_loop)]
-#![allow(clippy::no_effect)]
-#![allow(clippy::unnecessary_operation)]
-#![allow(clippy::branches_sharing_code)]
+#![allow(
+    unused,
+    clippy::never_loop,
+    clippy::no_effect,
+    clippy::unnecessary_operation,
+    clippy::branches_sharing_code,
+    clippy::let_unit_value
+)]
 
 #[macro_use]
 extern crate macro_rules;
index 6f9e124704b2c76834dd95840a8cebfecec1d589..3cc84ff1132321873e65e03dcde9d1017e75b379 100644 (file)
@@ -1,5 +1,5 @@
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:17:17
+  --> $DIR/default_numeric_fallback_i32.rs:20:17
    |
 LL |         let x = 22;
    |                 ^^ help: consider adding suffix: `22_i32`
@@ -7,145 +7,145 @@ 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:18:18
+  --> $DIR/default_numeric_fallback_i32.rs:21: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:18:21
+  --> $DIR/default_numeric_fallback_i32.rs:21: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:18:24
+  --> $DIR/default_numeric_fallback_i32.rs:21: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:19:28
+  --> $DIR/default_numeric_fallback_i32.rs:22: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:19:31
+  --> $DIR/default_numeric_fallback_i32.rs:22: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:19:44
+  --> $DIR/default_numeric_fallback_i32.rs:22: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:19:47
+  --> $DIR/default_numeric_fallback_i32.rs:22: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:20:23
+  --> $DIR/default_numeric_fallback_i32.rs:23:23
    |
 LL |         let x = match 1 {
    |                       ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:21:13
+  --> $DIR/default_numeric_fallback_i32.rs:24:13
    |
 LL |             1 => 1,
    |             ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:21:18
+  --> $DIR/default_numeric_fallback_i32.rs:24:18
    |
 LL |             1 => 1,
    |                  ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:22:18
+  --> $DIR/default_numeric_fallback_i32.rs:25:18
    |
 LL |             _ => 2,
    |                  ^ help: consider adding suffix: `2_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:39:21
+  --> $DIR/default_numeric_fallback_i32.rs:42:21
    |
 LL |             let y = 1;
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:47:21
+  --> $DIR/default_numeric_fallback_i32.rs:50: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:56:21
    |
 LL |             let y = 1;
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:65:9
+  --> $DIR/default_numeric_fallback_i32.rs:68:9
    |
 LL |         1
    |         ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:71:27
+  --> $DIR/default_numeric_fallback_i32.rs:74:27
    |
 LL |         let f = || -> _ { 1 };
    |                           ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:75:29
+  --> $DIR/default_numeric_fallback_i32.rs:78:29
    |
 LL |         let f = || -> i32 { 1 };
    |                             ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:89:21
+  --> $DIR/default_numeric_fallback_i32.rs:92:21
    |
 LL |         generic_arg(1);
    |                     ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:92:32
+  --> $DIR/default_numeric_fallback_i32.rs:95: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:110:28
+  --> $DIR/default_numeric_fallback_i32.rs:113:28
    |
 LL |         GenericStruct { x: 1 };
    |                            ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:113:36
+  --> $DIR/default_numeric_fallback_i32.rs:116:36
    |
 LL |         let _ = GenericStruct { x: 1 };
    |                                    ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:131:24
+  --> $DIR/default_numeric_fallback_i32.rs:134:24
    |
 LL |         GenericEnum::X(1);
    |                        ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:151:23
+  --> $DIR/default_numeric_fallback_i32.rs:154:23
    |
 LL |         s.generic_arg(1);
    |                       ^ help: consider adding suffix: `1_i32`
 
 error: default numeric fallback might occur
-  --> $DIR/default_numeric_fallback_i32.rs:158:21
+  --> $DIR/default_numeric_fallback_i32.rs:161:21
    |
 LL |             let x = 22;
    |                     ^^ help: consider adding suffix: `22_i32`
index 39a2601fee9aca39e6d3882f27c8d1008ffce339..07270bd76362a1a8cf4eadc533d6c0e01465d259 100644 (file)
@@ -1,3 +1,7 @@
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
 #![warn(clippy::should_assert_eq)]
 #![warn(clippy::extend_from_slice)]
 #![warn(clippy::range_step_by_zero)]
index 6095f134d55e0d2ea7452696adfb9a047521f077..0e142ac8f20e7a3a83d0f1792f7363cb402be641 100644 (file)
@@ -1,5 +1,5 @@
 error: lint `clippy::should_assert_eq` has been removed: `assert!()` will be more flexible with RFC 2011
-  --> $DIR/deprecated.rs:1:9
+  --> $DIR/deprecated.rs:5:9
    |
 LL | #![warn(clippy::should_assert_eq)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,91 +7,91 @@ LL | #![warn(clippy::should_assert_eq)]
    = note: `-D renamed-and-removed-lints` implied by `-D warnings`
 
 error: lint `clippy::extend_from_slice` has been removed: `.extend_from_slice(_)` is a faster way to extend a Vec by a slice
-  --> $DIR/deprecated.rs:2:9
+  --> $DIR/deprecated.rs:6:9
    |
 LL | #![warn(clippy::extend_from_slice)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: lint `clippy::range_step_by_zero` has been removed: `iterator.step_by(0)` panics nowadays
-  --> $DIR/deprecated.rs:3:9
+  --> $DIR/deprecated.rs:7:9
    |
 LL | #![warn(clippy::range_step_by_zero)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7
-  --> $DIR/deprecated.rs:4:9
+  --> $DIR/deprecated.rs:8:9
    |
 LL | #![warn(clippy::unstable_as_slice)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7
-  --> $DIR/deprecated.rs:5:9
+  --> $DIR/deprecated.rs:9:9
    |
 LL | #![warn(clippy::unstable_as_mut_slice)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: lint `clippy::misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr
-  --> $DIR/deprecated.rs:6:9
+  --> $DIR/deprecated.rs:10:9
    |
 LL | #![warn(clippy::misaligned_transmute)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: lint `clippy::assign_ops` has been removed: using compound assignment operators (e.g., `+=`) is harmless
-  --> $DIR/deprecated.rs:7:9
+  --> $DIR/deprecated.rs:11:9
    |
 LL | #![warn(clippy::assign_ops)]
    |         ^^^^^^^^^^^^^^^^^^
 
 error: lint `clippy::if_let_redundant_pattern_matching` has been removed: this lint has been changed to redundant_pattern_matching
-  --> $DIR/deprecated.rs:8:9
+  --> $DIR/deprecated.rs:12:9
    |
 LL | #![warn(clippy::if_let_redundant_pattern_matching)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: lint `clippy::unsafe_vector_initialization` has been removed: the replacement suggested by this lint had substantially different behavior
-  --> $DIR/deprecated.rs:9:9
+  --> $DIR/deprecated.rs:13:9
    |
 LL | #![warn(clippy::unsafe_vector_initialization)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: lint `clippy::unused_collect` has been removed: `collect` has been marked as #[must_use] in rustc and that covers all cases of this lint
-  --> $DIR/deprecated.rs:10:9
+  --> $DIR/deprecated.rs:14:9
    |
 LL | #![warn(clippy::unused_collect)]
    |         ^^^^^^^^^^^^^^^^^^^^^^
 
 error: lint `clippy::replace_consts` has been removed: associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants
-  --> $DIR/deprecated.rs:11:9
+  --> $DIR/deprecated.rs:15:9
    |
 LL | #![warn(clippy::replace_consts)]
    |         ^^^^^^^^^^^^^^^^^^^^^^
 
 error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018
-  --> $DIR/deprecated.rs:12:9
+  --> $DIR/deprecated.rs:16:9
    |
 LL | #![warn(clippy::regex_macro)]
    |         ^^^^^^^^^^^^^^^^^^^
 
 error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint
-  --> $DIR/deprecated.rs:13:9
+  --> $DIR/deprecated.rs:17:9
    |
 LL | #![warn(clippy::find_map)]
    |         ^^^^^^^^^^^^^^^^
 
 error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint
-  --> $DIR/deprecated.rs:14:9
+  --> $DIR/deprecated.rs:18:9
    |
 LL | #![warn(clippy::filter_map)]
    |         ^^^^^^^^^^^^^^^^^^
 
 error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items
-  --> $DIR/deprecated.rs:15:9
+  --> $DIR/deprecated.rs:19:9
    |
 LL | #![warn(clippy::pub_enum_variant_names)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items
-  --> $DIR/deprecated.rs:16:9
+  --> $DIR/deprecated.rs:20:9
    |
 LL | #![warn(clippy::wrong_pub_self_convention)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index 4464a21b3b654bdf89b238a5c0594b61edbf06ce..b91f7aa0dd8d2bfe51710c8ab57adea3861d498d 100644 (file)
@@ -1,5 +1,7 @@
 // aux-build:doc_unsafe_macros.rs
 
+#![allow(clippy::let_unit_value)]
+
 #[macro_use]
 extern crate doc_unsafe_macros;
 
index d68b8a0c67be68fda36fdfd3b961ed6f3ff97945..904b88eaef62280a1a56991466904e7afd57ef9c 100644 (file)
@@ -1,5 +1,5 @@
 error: unsafe function's docs miss `# Safety` section
-  --> $DIR/doc_unsafe.rs:7:1
+  --> $DIR/doc_unsafe.rs:9:1
    |
 LL | / pub unsafe fn destroy_the_planet() {
 LL | |     unimplemented!();
@@ -9,7 +9,7 @@ LL | | }
    = note: `-D clippy::missing-safety-doc` implied by `-D warnings`
 
 error: unsafe function's docs miss `# Safety` section
-  --> $DIR/doc_unsafe.rs:30:5
+  --> $DIR/doc_unsafe.rs:32:5
    |
 LL | /     pub unsafe fn republished() {
 LL | |         unimplemented!();
@@ -17,13 +17,13 @@ LL | |     }
    | |_____^
 
 error: unsafe function's docs miss `# Safety` section
-  --> $DIR/doc_unsafe.rs:38:5
+  --> $DIR/doc_unsafe.rs:40:5
    |
 LL |     unsafe fn woefully_underdocumented(self);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: docs for unsafe trait missing `# Safety` section
-  --> $DIR/doc_unsafe.rs:44:1
+  --> $DIR/doc_unsafe.rs:46:1
    |
 LL | / pub unsafe trait UnsafeTrait {
 LL | |     fn method();
@@ -31,7 +31,7 @@ LL | | }
    | |_^
 
 error: unsafe function's docs miss `# Safety` section
-  --> $DIR/doc_unsafe.rs:74:5
+  --> $DIR/doc_unsafe.rs:76:5
    |
 LL | /     pub unsafe fn more_undocumented_unsafe() -> Self {
 LL | |         unimplemented!();
@@ -39,7 +39,7 @@ LL | |     }
    | |_____^
 
 error: unsafe function's docs miss `# Safety` section
-  --> $DIR/doc_unsafe.rs:90:9
+  --> $DIR/doc_unsafe.rs:92:9
    |
 LL | /         pub unsafe fn whee() {
 LL | |             unimplemented!()
index f73068901c503ac2ff3eb1b685b3336247b1ddd9..30121033de7eae113ff2025830aae75fa0c24d10 100644 (file)
@@ -1,4 +1,4 @@
-error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes
+error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes
   --> $DIR/drop_non_drop.rs:22:5
    |
 LL |     drop(Foo);
@@ -11,7 +11,7 @@ note: argument has type `main::Foo`
 LL |     drop(Foo);
    |          ^^^
 
-error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes
+error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes
   --> $DIR/drop_non_drop.rs:37:5
    |
 LL |     drop(Baz(Foo));
diff --git a/src/tools/clippy/tests/ui/empty_drop.fixed b/src/tools/clippy/tests/ui/empty_drop.fixed
new file mode 100644 (file)
index 0000000..2e1b768
--- /dev/null
@@ -0,0 +1,24 @@
+// run-rustfix
+#![warn(clippy::empty_drop)]
+#![allow(unused)]
+
+// should cause an error
+struct Foo;
+
+
+
+// shouldn't cause an error
+struct Bar;
+
+impl Drop for Bar {
+    fn drop(&mut self) {
+        println!("dropping bar!");
+    }
+}
+
+// should error
+struct Baz;
+
+
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_drop.rs b/src/tools/clippy/tests/ui/empty_drop.rs
new file mode 100644 (file)
index 0000000..75232b0
--- /dev/null
@@ -0,0 +1,30 @@
+// run-rustfix
+#![warn(clippy::empty_drop)]
+#![allow(unused)]
+
+// should cause an error
+struct Foo;
+
+impl Drop for Foo {
+    fn drop(&mut self) {}
+}
+
+// shouldn't cause an error
+struct Bar;
+
+impl Drop for Bar {
+    fn drop(&mut self) {
+        println!("dropping bar!");
+    }
+}
+
+// should error
+struct Baz;
+
+impl Drop for Baz {
+    fn drop(&mut self) {
+        {}
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/empty_drop.stderr b/src/tools/clippy/tests/ui/empty_drop.stderr
new file mode 100644 (file)
index 0000000..70f7880
--- /dev/null
@@ -0,0 +1,22 @@
+error: empty drop implementation
+  --> $DIR/empty_drop.rs:8:1
+   |
+LL | / impl Drop for Foo {
+LL | |     fn drop(&mut self) {}
+LL | | }
+   | |_^ help: try removing this impl
+   |
+   = note: `-D clippy::empty-drop` implied by `-D warnings`
+
+error: empty drop implementation
+  --> $DIR/empty_drop.rs:24:1
+   |
+LL | / impl Drop for Baz {
+LL | |     fn drop(&mut self) {
+LL | |         {}
+LL | |     }
+LL | | }
+   | |_^ help: try removing this impl
+
+error: aborting due to 2 previous errors
+
index 5aedbea381f2317d80c661cc20f7ffbc07748eae..6c2272f4dff97f350344b4fbb66fc5d4ba4cb146 100644 (file)
@@ -37,7 +37,7 @@ fn main() {
     }
 
     // See #815
-    let e = Some(1u8).map(divergent);
+    let e = Some(1u8).map(|a| divergent(a));
     let e = Some(1u8).map(generic);
     let e = Some(1u8).map(generic);
     // See #515
@@ -211,6 +211,10 @@ fn mutable_closure_in_loop() {
     let mut closure = |n| value += n;
     for _ in 0..5 {
         Some(1).map(&mut closure);
+
+        let mut value = 0;
+        let mut in_loop = |n| value += n;
+        Some(1).map(in_loop);
     }
 }
 
@@ -229,7 +233,7 @@ fn late_bound_lifetimes() {
     {
     }
     map_str(|s| take_asref_path(s));
-    map_str_to_path(std::convert::AsRef::as_ref);
+    map_str_to_path(|s| s.as_ref());
 }
 
 mod type_param_bound {
@@ -275,3 +279,15 @@ mod bind_by_ref {
         Some(A).map(|ref a| B::from(a));
     }
 }
+
+// #7812 False positive on coerced closure
+fn coerced_closure() {
+    fn function_returning_unit<F: FnMut(i32)>(f: F) {}
+    function_returning_unit(|x| std::process::exit(x));
+
+    fn arr() -> &'static [u8; 0] {
+        &[]
+    }
+    fn slice_fn(_: impl FnOnce() -> &'static [u8]) {}
+    slice_fn(|| arr());
+}
index 5fdf7fb9771697e2330265458903ac5c12528f94..a1a9c0dfbf381d64ae7f47d3fdc46cb1e981ad69 100644 (file)
@@ -211,6 +211,10 @@ fn mutable_closure_in_loop() {
     let mut closure = |n| value += n;
     for _ in 0..5 {
         Some(1).map(|n| closure(n));
+
+        let mut value = 0;
+        let mut in_loop = |n| value += n;
+        Some(1).map(|n| in_loop(n));
     }
 }
 
@@ -275,3 +279,15 @@ fn test() {
         Some(A).map(|ref a| B::from(a));
     }
 }
+
+// #7812 False positive on coerced closure
+fn coerced_closure() {
+    fn function_returning_unit<F: FnMut(i32)>(f: F) {}
+    function_returning_unit(|x| std::process::exit(x));
+
+    fn arr() -> &'static [u8; 0] {
+        &[]
+    }
+    fn slice_fn(_: impl FnOnce() -> &'static [u8]) {}
+    slice_fn(|| arr());
+}
index cda84982c9b7508020113a57acf2474077147971..bf2e97e744ab348d639991e748d71df3d87c1b82 100644 (file)
@@ -24,12 +24,6 @@ error: redundant closure
 LL |     all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
    |                          ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below`
 
-error: redundant closure
-  --> $DIR/eta.rs:40:27
-   |
-LL |     let e = Some(1u8).map(|a| divergent(a));
-   |                           ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `divergent`
-
 error: redundant closure
   --> $DIR/eta.rs:41:27
    |
@@ -117,10 +111,10 @@ LL |         Some(1).map(|n| closure(n));
    |                     ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
 
 error: redundant closure
-  --> $DIR/eta.rs:232:21
+  --> $DIR/eta.rs:217:21
    |
-LL |     map_str_to_path(|s| s.as_ref());
-   |                     ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref`
+LL |         Some(1).map(|n| in_loop(n));
+   |                     ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop`
 
-error: aborting due to 20 previous errors
+error: aborting due to 19 previous errors
 
index 150acfbfee75996fd89b882784a26fcbc3a5e002..f76127a7105fd5333dc9cca49947f943f394cf21 100644 (file)
@@ -72,4 +72,46 @@ fn unused_lt<'a>(_x: u8) {}
     }
 }
 
+mod issue6437 {
+    pub struct Scalar;
+
+    impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar {
+        fn add_assign(&mut self, _rhs: &Scalar) {
+            unimplemented!();
+        }
+    }
+
+    impl<'b> Scalar {
+        pub fn something<'c>() -> Self {
+            Self
+        }
+    }
+}
+
+// https://github.com/rust-lang/rust-clippy/pull/8737#pullrequestreview-951268213
+mod first_case {
+    use serde::de::Visitor;
+    pub trait Expected {
+        fn fmt(&self, formatter: &mut std::fmt::Formatter);
+    }
+
+    impl<'de, T> Expected for T
+    where
+        T: Visitor<'de>,
+    {
+        fn fmt(&self, formatter: &mut std::fmt::Formatter) {}
+    }
+}
+
+// https://github.com/rust-lang/rust-clippy/pull/8737#pullrequestreview-951268213
+mod second_case {
+    pub trait Source {
+        fn hey();
+    }
+
+    impl<'a, T: Source + ?Sized + 'a> Source for Box<T> {
+        fn hey() {}
+    }
+}
+
 fn main() {}
index 9143fb2c208a05604d86a10e3fdb59f569b7b3b6..fcc12d4ce14b95977f4a5f46e4509b0b32dac7c6 100644 (file)
@@ -18,5 +18,23 @@ error: this lifetime isn't used in the function definition
 LL |         fn unused_lt<'a>(x: u8) {}
    |                      ^^
 
-error: aborting due to 3 previous errors
+error: this lifetime isn't used in the impl
+  --> $DIR/extra_unused_lifetimes.rs:78:10
+   |
+LL |     impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar {
+   |          ^^
+
+error: this lifetime isn't used in the impl
+  --> $DIR/extra_unused_lifetimes.rs:84:10
+   |
+LL |     impl<'b> Scalar {
+   |          ^^
+
+error: this lifetime isn't used in the function definition
+  --> $DIR/extra_unused_lifetimes.rs:85:26
+   |
+LL |         pub fn something<'c>() -> Self {
+   |                          ^^
+
+error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/format_push_string.rs b/src/tools/clippy/tests/ui/format_push_string.rs
new file mode 100644 (file)
index 0000000..4db13d6
--- /dev/null
@@ -0,0 +1,7 @@
+#![warn(clippy::format_push_string)]
+
+fn main() {
+    let mut string = String::new();
+    string += &format!("{:?}", 1234);
+    string.push_str(&format!("{:?}", 5678));
+}
diff --git a/src/tools/clippy/tests/ui/format_push_string.stderr b/src/tools/clippy/tests/ui/format_push_string.stderr
new file mode 100644 (file)
index 0000000..953784b
--- /dev/null
@@ -0,0 +1,19 @@
+error: `format!(..)` appended to existing `String`
+  --> $DIR/format_push_string.rs:5:5
+   |
+LL |     string += &format!("{:?}", 1234);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::format-push-string` implied by `-D warnings`
+   = help: consider using `write!` to avoid the extra allocation
+
+error: `format!(..)` appended to existing `String`
+  --> $DIR/format_push_string.rs:6:5
+   |
+LL |     string.push_str(&format!("{:?}", 5678));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using `write!` to avoid the extra allocation
+
+error: aborting due to 2 previous errors
+
index edc3fe1aec13a556fa0c8c2121a1639553c57952..fec54d00ccb4b8869b90a7c4cdb55e510f8ff543 100644 (file)
@@ -1,3 +1,5 @@
+use std::fmt::Write as _;
+
 const ONE: i64 = 1;
 const NEG_ONE: i64 = -1;
 const ZERO: i64 = 0;
@@ -7,7 +9,7 @@
 impl std::ops::Shl<i32> for A {
     type Output = A;
     fn shl(mut self, other: i32) -> Self {
-        self.0.push_str(&format!("{}", other));
+        let _ = write!(self.0, "{}", other);
         self
     }
 }
@@ -75,4 +77,34 @@ fn main() {
     (x + 1) % 3; // no error
     4 % 3; // no error
     4 % -3; // no error
+
+    // See #8724
+    let a = 0;
+    let b = true;
+    0 + if b { 1 } else { 2 };
+    0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; // no error
+    0 + match a { 0 => 10, _ => 20 };
+    0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; // no error
+    0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; // no error
+    0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; // no error
+    
+    0 + if b { 0 + 1 } else { 2 };
+    0 + match a { 0 =>  0 + 10, _ => 20 };
+    0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 };
+
+    let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 };
+    let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1  { 0 => 30, _ => 40 };
+
+    0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0;
+    
+    0 + { a } + 3; // no error
+    0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; // no error
+    
+    fn f(_: i32) {
+        todo!();
+    }
+    f(1 * a + { 8 * 5 });
+    f(0 + if b { 1 } else { 2 } + 3); // no error
+    const _: i32 = { 2 * 4 } + 0 + 3;
+    const _: i32 = 0 + { 1 + 2 * 3 } + 3; // no error
 }
index 706f01a3dd6c403b245ca8568cdb624def8f943a..d8cb65839cbba5595b6c1cc422427ca30603983b 100644 (file)
@@ -1,5 +1,5 @@
 error: the operation is ineffective. Consider reducing it to `x`
-  --> $DIR/identity_op.rs:37:5
+  --> $DIR/identity_op.rs:39:5
    |
 LL |     x + 0;
    |     ^^^^^
@@ -7,106 +7,196 @@ LL |     x + 0;
    = note: `-D clippy::identity-op` implied by `-D warnings`
 
 error: the operation is ineffective. Consider reducing it to `x`
-  --> $DIR/identity_op.rs:38:5
+  --> $DIR/identity_op.rs:40:5
    |
 LL |     x + (1 - 1);
    |     ^^^^^^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `x`
-  --> $DIR/identity_op.rs:40:5
+  --> $DIR/identity_op.rs:42:5
    |
 LL |     0 + x;
    |     ^^^^^
 
 error: the operation is ineffective. Consider reducing it to `x`
-  --> $DIR/identity_op.rs:43:5
+  --> $DIR/identity_op.rs:45:5
    |
 LL |     x | (0);
    |     ^^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `x`
-  --> $DIR/identity_op.rs:46:5
+  --> $DIR/identity_op.rs:48:5
    |
 LL |     x * 1;
    |     ^^^^^
 
 error: the operation is ineffective. Consider reducing it to `x`
-  --> $DIR/identity_op.rs:47:5
+  --> $DIR/identity_op.rs:49:5
    |
 LL |     1 * x;
    |     ^^^^^
 
 error: the operation is ineffective. Consider reducing it to `x`
-  --> $DIR/identity_op.rs:53:5
+  --> $DIR/identity_op.rs:55:5
    |
 LL |     -1 & x;
    |     ^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `u`
-  --> $DIR/identity_op.rs:56:5
+  --> $DIR/identity_op.rs:58:5
    |
 LL |     u & 255;
    |     ^^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `42`
-  --> $DIR/identity_op.rs:59:5
+  --> $DIR/identity_op.rs:61:5
    |
 LL |     42 << 0;
    |     ^^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `1`
-  --> $DIR/identity_op.rs:60:5
+  --> $DIR/identity_op.rs:62:5
    |
 LL |     1 >> 0;
    |     ^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `42`
-  --> $DIR/identity_op.rs:61:5
+  --> $DIR/identity_op.rs:63:5
    |
 LL |     42 >> 0;
    |     ^^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `&x`
-  --> $DIR/identity_op.rs:62:5
+  --> $DIR/identity_op.rs:64:5
    |
 LL |     &x >> 0;
    |     ^^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `x`
-  --> $DIR/identity_op.rs:63:5
+  --> $DIR/identity_op.rs:65:5
    |
 LL |     x >> &0;
    |     ^^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `2`
-  --> $DIR/identity_op.rs:70:5
+  --> $DIR/identity_op.rs:72:5
    |
 LL |     2 % 3;
    |     ^^^^^
 
 error: the operation is ineffective. Consider reducing it to `-2`
-  --> $DIR/identity_op.rs:71:5
+  --> $DIR/identity_op.rs:73:5
    |
 LL |     -2 % 3;
    |     ^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `2`
-  --> $DIR/identity_op.rs:72:5
+  --> $DIR/identity_op.rs:74:5
    |
 LL |     2 % -3 + x;
    |     ^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `-2`
-  --> $DIR/identity_op.rs:73:5
+  --> $DIR/identity_op.rs:75:5
    |
 LL |     -2 % -3 + x;
    |     ^^^^^^^
 
 error: the operation is ineffective. Consider reducing it to `1`
-  --> $DIR/identity_op.rs:74:9
+  --> $DIR/identity_op.rs:76:9
    |
 LL |     x + 1 % 3;
    |         ^^^^^
 
-error: aborting due to 18 previous errors
+error: the operation is ineffective. Consider reducing it to `if b { 1 } else { 2 }`
+  --> $DIR/identity_op.rs:84:5
+   |
+LL |     0 + if b { 1 } else { 2 };
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `match a { 0 => 10, _ => 20 }`
+  --> $DIR/identity_op.rs:86:5
+   |
+LL |     0 + match a { 0 => 10, _ => 20 };
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `if b { 0 + 1 } else { 2 }`
+  --> $DIR/identity_op.rs:91:5
+   |
+LL |     0 + if b { 0 + 1 } else { 2 };
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `1`
+  --> $DIR/identity_op.rs:91:16
+   |
+LL |     0 + if b { 0 + 1 } else { 2 };
+   |                ^^^^^
+
+error: the operation is ineffective. Consider reducing it to `match a { 0 =>  0 + 10, _ => 20 }`
+  --> $DIR/identity_op.rs:92:5
+   |
+LL |     0 + match a { 0 =>  0 + 10, _ => 20 };
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `10`
+  --> $DIR/identity_op.rs:92:25
+   |
+LL |     0 + match a { 0 =>  0 + 10, _ => 20 };
+   |                         ^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `1`
+  --> $DIR/identity_op.rs:93:16
+   |
+LL |     0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 };
+   |                ^^^^^
+
+error: the operation is ineffective. Consider reducing it to `30`
+  --> $DIR/identity_op.rs:93:52
+   |
+LL |     0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 };
+   |                                                    ^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `1`
+  --> $DIR/identity_op.rs:95:20
+   |
+LL |     let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 };
+   |                    ^^^^^
+
+error: the operation is ineffective. Consider reducing it to `1`
+  --> $DIR/identity_op.rs:95:52
+   |
+LL |     let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 };
+   |                                                    ^^^^^
+
+error: the operation is ineffective. Consider reducing it to `1`
+  --> $DIR/identity_op.rs:96:23
+   |
+LL |     let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1  { 0 => 30, _ => 40 };
+   |                       ^^^^^
+
+error: the operation is ineffective. Consider reducing it to `1`
+  --> $DIR/identity_op.rs:96:58
+   |
+LL |     let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1  { 0 => 30, _ => 40 };
+   |                                                          ^^^^^
+
+error: the operation is ineffective. Consider reducing it to `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }`
+  --> $DIR/identity_op.rs:98:5
+   |
+LL |     0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `a`
+  --> $DIR/identity_op.rs:106:7
+   |
+LL |     f(1 * a + { 8 * 5 });
+   |       ^^^^^
+
+error: the operation is ineffective. Consider reducing it to `{ 2 * 4 }`
+  --> $DIR/identity_op.rs:108:20
+   |
+LL |     const _: i32 = { 2 * 4 } + 0 + 3;
+   |                    ^^^^^^^^^^^^^
+
+error: aborting due to 33 previous errors
 
index 39443775015365d7197d1fb036b4b35bd66bdb4f..aea52a852f987d6ab86d899240c9ed81cd3cddc3 100644 (file)
@@ -1,4 +1,4 @@
-#![allow(dead_code)]
+#![allow(dead_code, clippy::extra_unused_lifetimes)]
 #![warn(clippy::multiple_inherent_impl)]
 
 struct MyStruct;
index 83a36f407d5d877b18511182ca893781bbc628aa..6ae700753f06d672724d197018dd7fbecceb6ecf 100644 (file)
@@ -1,4 +1,4 @@
-error[E0080]: evaluation of `main::{constant#3}::<&i32>` failed
+error[E0080]: evaluation of `main::{constant#3}` failed
   --> $DIR/indexing_slicing_index.rs:31:14
    |
 LL |     const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
diff --git a/src/tools/clippy/tests/ui/is_digit_ascii_radix.fixed b/src/tools/clippy/tests/ui/is_digit_ascii_radix.fixed
new file mode 100644 (file)
index 0000000..c0ba647
--- /dev/null
@@ -0,0 +1,18 @@
+// run-rustfix
+
+#![warn(clippy::is_digit_ascii_radix)]
+
+const TEN: u32 = 10;
+
+fn main() {
+    let c: char = '6';
+
+    // Should trigger the lint.
+    let _ = c.is_ascii_digit();
+    let _ = c.is_ascii_hexdigit();
+    let _ = c.is_ascii_hexdigit();
+
+    // Should not trigger the lint.
+    let _ = c.is_digit(11);
+    let _ = c.is_digit(TEN);
+}
diff --git a/src/tools/clippy/tests/ui/is_digit_ascii_radix.rs b/src/tools/clippy/tests/ui/is_digit_ascii_radix.rs
new file mode 100644 (file)
index 0000000..68e3f32
--- /dev/null
@@ -0,0 +1,18 @@
+// run-rustfix
+
+#![warn(clippy::is_digit_ascii_radix)]
+
+const TEN: u32 = 10;
+
+fn main() {
+    let c: char = '6';
+
+    // Should trigger the lint.
+    let _ = c.is_digit(10);
+    let _ = c.is_digit(16);
+    let _ = c.is_digit(0x10);
+
+    // Should not trigger the lint.
+    let _ = c.is_digit(11);
+    let _ = c.is_digit(TEN);
+}
diff --git a/src/tools/clippy/tests/ui/is_digit_ascii_radix.stderr b/src/tools/clippy/tests/ui/is_digit_ascii_radix.stderr
new file mode 100644 (file)
index 0000000..dc5cb29
--- /dev/null
@@ -0,0 +1,22 @@
+error: use of `char::is_digit` with literal radix of 10
+  --> $DIR/is_digit_ascii_radix.rs:11:13
+   |
+LL |     let _ = c.is_digit(10);
+   |             ^^^^^^^^^^^^^^ help: try: `c.is_ascii_digit()`
+   |
+   = note: `-D clippy::is-digit-ascii-radix` implied by `-D warnings`
+
+error: use of `char::is_digit` with literal radix of 16
+  --> $DIR/is_digit_ascii_radix.rs:12:13
+   |
+LL |     let _ = c.is_digit(16);
+   |             ^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()`
+
+error: use of `char::is_digit` with literal radix of 16
+  --> $DIR/is_digit_ascii_radix.rs:13:13
+   |
+LL |     let _ = c.is_digit(0x10);
+   |             ^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()`
+
+error: aborting due to 3 previous errors
+
index 56761ebbcb80bb0e5dcd595ba3ca0194b41829e2..7c2b05d837ba89828bff9c637927f78e54cbc2b6 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
-#![allow(dead_code)]
+#![allow(dead_code, clippy::let_unit_value)]
 
 fn main() {
     let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()];
index 98321d889b58273fd78a023d8933ad3af3f53074..f2d0b155d2c2aa20553caee5e59618d6fc1e923f 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
-#![allow(dead_code)]
+#![allow(dead_code, clippy::let_unit_value)]
 
 fn main() {
     let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()];
index aea4dba9dd5a0166a422fb921b3efffb183c4684..0330d5549264a122a1c43deee1dfbe41dcea55a3 100644 (file)
@@ -39,6 +39,15 @@ fn should_not_help() {
     let _: Vec<_> = b.drain(0..a.len()).collect();
 }
 
+fn _closed_range(mut x: Vec<String>) {
+    let _: Vec<String> = x.drain(0..=x.len()).collect();
+}
+
+fn _with_mut(x: &mut Vec<String>, y: &mut VecDeque<String>) {
+    let _: Vec<String> = x.drain(..).collect();
+    let _: Vec<String> = y.drain(..).collect();
+}
+
 #[derive(Default)]
 struct Bomb {
     fire: Vec<u8>,
index 271878cffb44ccc8fcd7f1644ec24bbc6bc92dd7..993936fb8de3d5b345ebcd2ef233e27c24f08420 100644 (file)
@@ -39,6 +39,15 @@ fn should_not_help() {
     let _: Vec<_> = b.drain(0..a.len()).collect();
 }
 
+fn _closed_range(mut x: Vec<String>) {
+    let _: Vec<String> = x.drain(0..=x.len()).collect();
+}
+
+fn _with_mut(x: &mut Vec<String>, y: &mut VecDeque<String>) {
+    let _: Vec<String> = x.drain(..).collect();
+    let _: Vec<String> = y.drain(..).collect();
+}
+
 #[derive(Default)]
 struct Bomb {
     fire: Vec<u8>,
index 50744f81c3cf84893697292fc190bb496b7a8f60..11b50492ab29055ec6223164a2a768a53b9083a8 100644 (file)
@@ -1,4 +1,5 @@
 #![warn(clippy::let_underscore_drop)]
+#![allow(clippy::let_unit_value)]
 
 struct Droppable;
 
index 66069e0c5e13f00a8d524c99351a2f7363da0c89..ee7bbe995f1684e28bbf064d9cd79cdb2e6c4257 100644 (file)
@@ -1,5 +1,5 @@
 error: non-binding `let` on a type that implements `Drop`
-  --> $DIR/let_underscore_drop.rs:16:5
+  --> $DIR/let_underscore_drop.rs:17:5
    |
 LL |     let _ = Box::new(());
    |     ^^^^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL |     let _ = Box::new(());
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
 error: non-binding `let` on a type that implements `Drop`
-  --> $DIR/let_underscore_drop.rs:17:5
+  --> $DIR/let_underscore_drop.rs:18:5
    |
 LL |     let _ = Droppable;
    |     ^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL |     let _ = Droppable;
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
 error: non-binding `let` on a type that implements `Drop`
-  --> $DIR/let_underscore_drop.rs:18:5
+  --> $DIR/let_underscore_drop.rs:19:5
    |
 LL |     let _ = Some(Droppable);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
index f398edc23cb5e0bdd019bfea348570872fb5d39d..e72b746232551b87861e2783cf5ce3d7d76dfd21 100644 (file)
@@ -61,3 +61,55 @@ fn multiline_sugg() {
 
 #[derive(Copy, Clone)]
 pub struct ContainsUnit(()); // should be fine
+
+fn _returns_generic() {
+    fn f<T>() -> T {
+        unimplemented!()
+    }
+    fn f2<T, U>(_: T) -> U {
+        unimplemented!()
+    }
+    fn f3<T>(x: T) -> T {
+        x
+    }
+    fn f4<T>(mut x: Vec<T>) -> T {
+        x.pop().unwrap()
+    }
+
+    let _: () = f(); // Ok
+    let _: () = f(); // Lint.
+
+    let _: () = f2(0i32); // Ok
+    let _: () = f2(0i32); // Lint.
+
+    f3(()); // Lint
+    f3(()); // Lint
+
+    f4(vec![()]); // Lint
+    f4(vec![()]); // Lint
+
+    // Ok
+    let _: () = {
+        let x = 5;
+        f2(x)
+    };
+
+    let _: () = if true { f() } else { f2(0) }; // Ok
+    let _: () = if true { f() } else { f2(0) }; // Lint
+
+    // Ok
+    let _: () = match Some(0) {
+        None => f2(1),
+        Some(0) => f(),
+        Some(1) => f2(3),
+        Some(_) => f2('x'),
+    };
+
+    // Lint
+    match Some(0) {
+        None => f2(1),
+        Some(0) => f(),
+        Some(1) => f2(3),
+        Some(_) => (),
+    };
+}
index af5b1fb2ac7e4d526eb6eaa47025d75db8a7992d..47ee0a76724792445b8da0bd6465355b43fafb73 100644 (file)
@@ -61,3 +61,55 @@ fn multiline_sugg() {
 
 #[derive(Copy, Clone)]
 pub struct ContainsUnit(()); // should be fine
+
+fn _returns_generic() {
+    fn f<T>() -> T {
+        unimplemented!()
+    }
+    fn f2<T, U>(_: T) -> U {
+        unimplemented!()
+    }
+    fn f3<T>(x: T) -> T {
+        x
+    }
+    fn f4<T>(mut x: Vec<T>) -> T {
+        x.pop().unwrap()
+    }
+
+    let _: () = f(); // Ok
+    let x: () = f(); // Lint.
+
+    let _: () = f2(0i32); // Ok
+    let x: () = f2(0i32); // Lint.
+
+    let _: () = f3(()); // Lint
+    let x: () = f3(()); // Lint
+
+    let _: () = f4(vec![()]); // Lint
+    let x: () = f4(vec![()]); // Lint
+
+    // Ok
+    let _: () = {
+        let x = 5;
+        f2(x)
+    };
+
+    let _: () = if true { f() } else { f2(0) }; // Ok
+    let x: () = if true { f() } else { f2(0) }; // Lint
+
+    // Ok
+    let _: () = match Some(0) {
+        None => f2(1),
+        Some(0) => f(),
+        Some(1) => f2(3),
+        Some(_) => f2('x'),
+    };
+
+    // Lint
+    let _: () = match Some(0) {
+        None => f2(1),
+        Some(0) => f(),
+        Some(1) => f2(3),
+        Some(_) => (),
+    };
+}
index f2600c6c22883ef6f28434f10efa95a7c8268eea..13ec11a6d33e9a9c12cc06f9a8e37facaab385f3 100644 (file)
@@ -34,5 +34,74 @@ LL +         .map(|_| ())
 LL +         .next()
  ...
 
-error: aborting due to 3 previous errors
+error: this let-binding has unit value
+  --> $DIR/let_unit.rs:80:5
+   |
+LL |     let x: () = f(); // Lint.
+   |     ^^^^-^^^^^^^^^^^
+   |         |
+   |         help: use a wild (`_`) binding: `_`
+
+error: this let-binding has unit value
+  --> $DIR/let_unit.rs:83:5
+   |
+LL |     let x: () = f2(0i32); // Lint.
+   |     ^^^^-^^^^^^^^^^^^^^^^
+   |         |
+   |         help: use a wild (`_`) binding: `_`
+
+error: this let-binding has unit value
+  --> $DIR/let_unit.rs:85:5
+   |
+LL |     let _: () = f3(()); // Lint
+   |     ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());`
+
+error: this let-binding has unit value
+  --> $DIR/let_unit.rs:86:5
+   |
+LL |     let x: () = f3(()); // Lint
+   |     ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());`
+
+error: this let-binding has unit value
+  --> $DIR/let_unit.rs:88:5
+   |
+LL |     let _: () = f4(vec![()]); // Lint
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);`
+
+error: this let-binding has unit value
+  --> $DIR/let_unit.rs:89:5
+   |
+LL |     let x: () = f4(vec![()]); // Lint
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);`
+
+error: this let-binding has unit value
+  --> $DIR/let_unit.rs:98:5
+   |
+LL |     let x: () = if true { f() } else { f2(0) }; // Lint
+   |     ^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |         |
+   |         help: use a wild (`_`) binding: `_`
+
+error: this let-binding has unit value
+  --> $DIR/let_unit.rs:109:5
+   |
+LL | /     let _: () = match Some(0) {
+LL | |         None => f2(1),
+LL | |         Some(0) => f(),
+LL | |         Some(1) => f2(3),
+LL | |         Some(_) => (),
+LL | |     };
+   | |______^
+   |
+help: omit the `let` binding
+   |
+LL ~     match Some(0) {
+LL +         None => f2(1),
+LL +         Some(0) => f(),
+LL +         Some(1) => f2(3),
+LL +         Some(_) => (),
+LL +     };
+   |
+
+error: aborting due to 11 previous errors
 
index 4f1b19b75b8a1014d388b2a7097b3e34d7dc8b8c..386360dbdcdb8af4e1b91f9039343a26e7d5601e 100644 (file)
@@ -1,38 +1,44 @@
 // run-rustfix
 
 #![warn(clippy::manual_bits)]
-#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)]
+#![allow(
+    clippy::no_effect,
+    clippy::useless_conversion,
+    path_statements,
+    unused_must_use,
+    clippy::unnecessary_operation
+)]
 
 use std::mem::{size_of, size_of_val};
 
 fn main() {
-    i8::BITS;
-    i16::BITS;
-    i32::BITS;
-    i64::BITS;
-    i128::BITS;
-    isize::BITS;
-
-    u8::BITS;
-    u16::BITS;
-    u32::BITS;
-    u64::BITS;
-    u128::BITS;
-    usize::BITS;
-
-    i8::BITS;
-    i16::BITS;
-    i32::BITS;
-    i64::BITS;
-    i128::BITS;
-    isize::BITS;
-
-    u8::BITS;
-    u16::BITS;
-    u32::BITS;
-    u64::BITS;
-    u128::BITS;
-    usize::BITS;
+    i8::BITS as usize;
+    i16::BITS as usize;
+    i32::BITS as usize;
+    i64::BITS as usize;
+    i128::BITS as usize;
+    isize::BITS as usize;
+
+    u8::BITS as usize;
+    u16::BITS as usize;
+    u32::BITS as usize;
+    u64::BITS as usize;
+    u128::BITS as usize;
+    usize::BITS as usize;
+
+    i8::BITS as usize;
+    i16::BITS as usize;
+    i32::BITS as usize;
+    i64::BITS as usize;
+    i128::BITS as usize;
+    isize::BITS as usize;
+
+    u8::BITS as usize;
+    u16::BITS as usize;
+    u32::BITS as usize;
+    u64::BITS as usize;
+    u128::BITS as usize;
+    usize::BITS as usize;
 
     size_of::<usize>() * 4;
     4 * size_of::<usize>();
@@ -42,7 +48,12 @@ fn main() {
     size_of_val(&0u32) * 8;
 
     type Word = u32;
-    Word::BITS;
+    Word::BITS as usize;
     type Bool = bool;
     size_of::<Bool>() * 8;
+
+    let _: u32 = u128::BITS as u32;
+    let _: u32 = u128::BITS.try_into().unwrap();
+    let _ = (u128::BITS as usize).pow(5);
+    let _ = &(u128::BITS as usize);
 }
index f8a01313e6ad02117bfb9c3a7bb69a7430878d73..62638f047eb015b299d9b8e45afd4489b9b9a364 100644 (file)
@@ -1,7 +1,13 @@
 // run-rustfix
 
 #![warn(clippy::manual_bits)]
-#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)]
+#![allow(
+    clippy::no_effect,
+    clippy::useless_conversion,
+    path_statements,
+    unused_must_use,
+    clippy::unnecessary_operation
+)]
 
 use std::mem::{size_of, size_of_val};
 
@@ -45,4 +51,9 @@ fn main() {
     size_of::<Word>() * 8;
     type Bool = bool;
     size_of::<Bool>() * 8;
+
+    let _: u32 = (size_of::<u128>() * 8) as u32;
+    let _: u32 = (size_of::<u128>() * 8).try_into().unwrap();
+    let _ = (size_of::<u128>() * 8).pow(5);
+    let _ = &(size_of::<u128>() * 8);
 }
index c4f5af2dcb0ec464192b497776482ac0f36ecc2b..69c591a203d3f2c03eb3ccda6690f35192977126 100644 (file)
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:9:5
+  --> $DIR/manual_bits.rs:15:5
    |
 LL |     size_of::<i8>() * 8;
-   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize`
    |
    = note: `-D clippy::manual-bits` implied by `-D warnings`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:10:5
+  --> $DIR/manual_bits.rs:16:5
    |
 LL |     size_of::<i16>() * 8;
-   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:11:5
+  --> $DIR/manual_bits.rs:17:5
    |
 LL |     size_of::<i32>() * 8;
-   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:12:5
+  --> $DIR/manual_bits.rs:18:5
    |
 LL |     size_of::<i64>() * 8;
-   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:13:5
+  --> $DIR/manual_bits.rs:19:5
    |
 LL |     size_of::<i128>() * 8;
-   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:14:5
+  --> $DIR/manual_bits.rs:20:5
    |
 LL |     size_of::<isize>() * 8;
-   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:16:5
+  --> $DIR/manual_bits.rs:22:5
    |
 LL |     size_of::<u8>() * 8;
-   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:17:5
+  --> $DIR/manual_bits.rs:23:5
    |
 LL |     size_of::<u16>() * 8;
-   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:18:5
+  --> $DIR/manual_bits.rs:24:5
    |
 LL |     size_of::<u32>() * 8;
-   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:19:5
+  --> $DIR/manual_bits.rs:25:5
    |
 LL |     size_of::<u64>() * 8;
-   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:20:5
+  --> $DIR/manual_bits.rs:26:5
    |
 LL |     size_of::<u128>() * 8;
-   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:21:5
+  --> $DIR/manual_bits.rs:27:5
    |
 LL |     size_of::<usize>() * 8;
-   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:23:5
+  --> $DIR/manual_bits.rs:29:5
    |
 LL |     8 * size_of::<i8>();
-   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:24:5
+  --> $DIR/manual_bits.rs:30:5
    |
 LL |     8 * size_of::<i16>();
-   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:25:5
+  --> $DIR/manual_bits.rs:31:5
    |
 LL |     8 * size_of::<i32>();
-   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:26:5
+  --> $DIR/manual_bits.rs:32:5
    |
 LL |     8 * size_of::<i64>();
-   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:27:5
+  --> $DIR/manual_bits.rs:33:5
    |
 LL |     8 * size_of::<i128>();
-   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:28:5
+  --> $DIR/manual_bits.rs:34:5
    |
 LL |     8 * size_of::<isize>();
-   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:30:5
+  --> $DIR/manual_bits.rs:36:5
    |
 LL |     8 * size_of::<u8>();
-   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:31:5
+  --> $DIR/manual_bits.rs:37:5
    |
 LL |     8 * size_of::<u16>();
-   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:32:5
+  --> $DIR/manual_bits.rs:38:5
    |
 LL |     8 * size_of::<u32>();
-   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:33:5
+  --> $DIR/manual_bits.rs:39:5
    |
 LL |     8 * size_of::<u64>();
-   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:34:5
+  --> $DIR/manual_bits.rs:40:5
    |
 LL |     8 * size_of::<u128>();
-   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:35:5
+  --> $DIR/manual_bits.rs:41:5
    |
 LL |     8 * size_of::<usize>();
-   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize`
 
 error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
-  --> $DIR/manual_bits.rs:45:5
+  --> $DIR/manual_bits.rs:51:5
    |
 LL |     size_of::<Word>() * 8;
-   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS`
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:55:18
+   |
+LL |     let _: u32 = (size_of::<u128>() * 8) as u32;
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:56:18
+   |
+LL |     let _: u32 = (size_of::<u128>() * 8).try_into().unwrap();
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:57:13
+   |
+LL |     let _ = (size_of::<u128>() * 8).pow(5);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+  --> $DIR/manual_bits.rs:58:14
+   |
+LL |     let _ = &(size_of::<u128>() * 8);
+   |              ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)`
 
-error: aborting due to 25 previous errors
+error: aborting due to 29 previous errors
 
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive.rs b/src/tools/clippy/tests/ui/manual_non_exhaustive.rs
deleted file mode 100644 (file)
index 7a788f4..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-#![warn(clippy::manual_non_exhaustive)]
-#![allow(unused)]
-
-mod enums {
-    enum E {
-        A,
-        B,
-        #[doc(hidden)]
-        _C,
-    }
-
-    // user forgot to remove the marker
-    #[non_exhaustive]
-    enum Ep {
-        A,
-        B,
-        #[doc(hidden)]
-        _C,
-    }
-
-    // marker variant does not have doc hidden attribute, should be ignored
-    enum NoDocHidden {
-        A,
-        B,
-        _C,
-    }
-
-    // name of variant with doc hidden does not start with underscore, should be ignored
-    enum NoUnderscore {
-        A,
-        B,
-        #[doc(hidden)]
-        C,
-    }
-
-    // variant with doc hidden is not unit, should be ignored
-    enum NotUnit {
-        A,
-        B,
-        #[doc(hidden)]
-        _C(bool),
-    }
-
-    // variant with doc hidden is the only one, should be ignored
-    enum OnlyMarker {
-        #[doc(hidden)]
-        _A,
-    }
-
-    // variant with multiple markers, should be ignored
-    enum MultipleMarkers {
-        A,
-        #[doc(hidden)]
-        _B,
-        #[doc(hidden)]
-        _C,
-    }
-
-    // already non_exhaustive and no markers, should be ignored
-    #[non_exhaustive]
-    enum NonExhaustive {
-        A,
-        B,
-    }
-}
-
-mod structs {
-    struct S {
-        pub a: i32,
-        pub b: i32,
-        _c: (),
-    }
-
-    // user forgot to remove the private field
-    #[non_exhaustive]
-    struct Sp {
-        pub a: i32,
-        pub b: i32,
-        _c: (),
-    }
-
-    // some other fields are private, should be ignored
-    struct PrivateFields {
-        a: i32,
-        pub b: i32,
-        _c: (),
-    }
-
-    // private field name does not start with underscore, should be ignored
-    struct NoUnderscore {
-        pub a: i32,
-        pub b: i32,
-        c: (),
-    }
-
-    // private field is not unit type, should be ignored
-    struct NotUnit {
-        pub a: i32,
-        pub b: i32,
-        _c: i32,
-    }
-
-    // private field is the only field, should be ignored
-    struct OnlyMarker {
-        _a: (),
-    }
-
-    // already non exhaustive and no private fields, should be ignored
-    #[non_exhaustive]
-    struct NonExhaustive {
-        pub a: i32,
-        pub b: i32,
-    }
-}
-
-mod tuple_structs {
-    struct T(pub i32, pub i32, ());
-
-    // user forgot to remove the private field
-    #[non_exhaustive]
-    struct Tp(pub i32, pub i32, ());
-
-    // some other fields are private, should be ignored
-    struct PrivateFields(pub i32, i32, ());
-
-    // private field is not unit type, should be ignored
-    struct NotUnit(pub i32, pub i32, i32);
-
-    // private field is the only field, should be ignored
-    struct OnlyMarker(());
-
-    // already non exhaustive and no private fields, should be ignored
-    #[non_exhaustive]
-    struct NonExhaustive(pub i32, pub i32);
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive.stderr b/src/tools/clippy/tests/ui/manual_non_exhaustive.stderr
deleted file mode 100644 (file)
index 613c5e8..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-error: this seems like a manual implementation of the non-exhaustive pattern
-  --> $DIR/manual_non_exhaustive.rs:5:5
-   |
-LL |       enum E {
-   |       ^-----
-   |       |
-   |  _____help: add the attribute: `#[non_exhaustive] enum E`
-   | |
-LL | |         A,
-LL | |         B,
-LL | |         #[doc(hidden)]
-LL | |         _C,
-LL | |     }
-   | |_____^
-   |
-   = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
-help: remove this variant
-  --> $DIR/manual_non_exhaustive.rs:9:9
-   |
-LL |         _C,
-   |         ^^
-
-error: this seems like a manual implementation of the non-exhaustive pattern
-  --> $DIR/manual_non_exhaustive.rs:14:5
-   |
-LL | /     enum Ep {
-LL | |         A,
-LL | |         B,
-LL | |         #[doc(hidden)]
-LL | |         _C,
-LL | |     }
-   | |_____^
-   |
-help: remove this variant
-  --> $DIR/manual_non_exhaustive.rs:18:9
-   |
-LL |         _C,
-   |         ^^
-
-error: this seems like a manual implementation of the non-exhaustive pattern
-  --> $DIR/manual_non_exhaustive.rs:68:5
-   |
-LL |       struct S {
-   |       ^-------
-   |       |
-   |  _____help: add the attribute: `#[non_exhaustive] struct S`
-   | |
-LL | |         pub a: i32,
-LL | |         pub b: i32,
-LL | |         _c: (),
-LL | |     }
-   | |_____^
-   |
-help: remove this field
-  --> $DIR/manual_non_exhaustive.rs:71:9
-   |
-LL |         _c: (),
-   |         ^^^^^^
-
-error: this seems like a manual implementation of the non-exhaustive pattern
-  --> $DIR/manual_non_exhaustive.rs:76:5
-   |
-LL | /     struct Sp {
-LL | |         pub a: i32,
-LL | |         pub b: i32,
-LL | |         _c: (),
-LL | |     }
-   | |_____^
-   |
-help: remove this field
-  --> $DIR/manual_non_exhaustive.rs:79:9
-   |
-LL |         _c: (),
-   |         ^^^^^^
-
-error: this seems like a manual implementation of the non-exhaustive pattern
-  --> $DIR/manual_non_exhaustive.rs:117:5
-   |
-LL |     struct T(pub i32, pub i32, ());
-   |     --------^^^^^^^^^^^^^^^^^^^^^^^
-   |     |
-   |     help: add the attribute: `#[non_exhaustive] struct T`
-   |
-help: remove this field
-  --> $DIR/manual_non_exhaustive.rs:117:32
-   |
-LL |     struct T(pub i32, pub i32, ());
-   |                                ^^
-
-error: this seems like a manual implementation of the non-exhaustive pattern
-  --> $DIR/manual_non_exhaustive.rs:121:5
-   |
-LL |     struct Tp(pub i32, pub i32, ());
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: remove this field
-  --> $DIR/manual_non_exhaustive.rs:121:33
-   |
-LL |     struct Tp(pub i32, pub i32, ());
-   |                                 ^^
-
-error: aborting due to 6 previous errors
-
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs
new file mode 100644 (file)
index 0000000..f23c6d6
--- /dev/null
@@ -0,0 +1,78 @@
+#![warn(clippy::manual_non_exhaustive)]
+#![allow(unused)]
+
+enum E {
+    A,
+    B,
+    #[doc(hidden)]
+    _C,
+}
+
+// user forgot to remove the marker
+#[non_exhaustive]
+enum Ep {
+    A,
+    B,
+    #[doc(hidden)]
+    _C,
+}
+
+// marker variant does not have doc hidden attribute, should be ignored
+enum NoDocHidden {
+    A,
+    B,
+    _C,
+}
+
+// name of variant with doc hidden does not start with underscore, should be ignored
+enum NoUnderscore {
+    A,
+    B,
+    #[doc(hidden)]
+    C,
+}
+
+// variant with doc hidden is not unit, should be ignored
+enum NotUnit {
+    A,
+    B,
+    #[doc(hidden)]
+    _C(bool),
+}
+
+// variant with doc hidden is the only one, should be ignored
+enum OnlyMarker {
+    #[doc(hidden)]
+    _A,
+}
+
+// variant with multiple markers, should be ignored
+enum MultipleMarkers {
+    A,
+    #[doc(hidden)]
+    _B,
+    #[doc(hidden)]
+    _C,
+}
+
+// already non_exhaustive and no markers, should be ignored
+#[non_exhaustive]
+enum NonExhaustive {
+    A,
+    B,
+}
+
+// marked is used, don't lint
+enum UsedHidden {
+    #[doc(hidden)]
+    _A,
+    B,
+    C,
+}
+fn foo(x: &mut UsedHidden) {
+    if matches!(*x, UsedHidden::B) {
+        *x = UsedHidden::_A;
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr
new file mode 100644 (file)
index 0000000..317a45d
--- /dev/null
@@ -0,0 +1,41 @@
+error: this seems like a manual implementation of the non-exhaustive pattern
+  --> $DIR/manual_non_exhaustive_enum.rs:4:1
+   |
+LL |   enum E {
+   |   ^-----
+   |   |
+   |  _help: add the attribute: `#[non_exhaustive] enum E`
+   | |
+LL | |     A,
+LL | |     B,
+LL | |     #[doc(hidden)]
+LL | |     _C,
+LL | | }
+   | |_^
+   |
+   = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
+help: remove this variant
+  --> $DIR/manual_non_exhaustive_enum.rs:8:5
+   |
+LL |     _C,
+   |     ^^
+
+error: this seems like a manual implementation of the non-exhaustive pattern
+  --> $DIR/manual_non_exhaustive_enum.rs:13:1
+   |
+LL | / enum Ep {
+LL | |     A,
+LL | |     B,
+LL | |     #[doc(hidden)]
+LL | |     _C,
+LL | | }
+   | |_^
+   |
+help: remove this variant
+  --> $DIR/manual_non_exhaustive_enum.rs:17:5
+   |
+LL |     _C,
+   |     ^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs
new file mode 100644 (file)
index 0000000..498eee4
--- /dev/null
@@ -0,0 +1,74 @@
+#![warn(clippy::manual_non_exhaustive)]
+#![allow(unused)]
+
+mod structs {
+    struct S {
+        pub a: i32,
+        pub b: i32,
+        _c: (),
+    }
+
+    // user forgot to remove the private field
+    #[non_exhaustive]
+    struct Sp {
+        pub a: i32,
+        pub b: i32,
+        _c: (),
+    }
+
+    // some other fields are private, should be ignored
+    struct PrivateFields {
+        a: i32,
+        pub b: i32,
+        _c: (),
+    }
+
+    // private field name does not start with underscore, should be ignored
+    struct NoUnderscore {
+        pub a: i32,
+        pub b: i32,
+        c: (),
+    }
+
+    // private field is not unit type, should be ignored
+    struct NotUnit {
+        pub a: i32,
+        pub b: i32,
+        _c: i32,
+    }
+
+    // private field is the only field, should be ignored
+    struct OnlyMarker {
+        _a: (),
+    }
+
+    // already non exhaustive and no private fields, should be ignored
+    #[non_exhaustive]
+    struct NonExhaustive {
+        pub a: i32,
+        pub b: i32,
+    }
+}
+
+mod tuple_structs {
+    struct T(pub i32, pub i32, ());
+
+    // user forgot to remove the private field
+    #[non_exhaustive]
+    struct Tp(pub i32, pub i32, ());
+
+    // some other fields are private, should be ignored
+    struct PrivateFields(pub i32, i32, ());
+
+    // private field is not unit type, should be ignored
+    struct NotUnit(pub i32, pub i32, i32);
+
+    // private field is the only field, should be ignored
+    struct OnlyMarker(());
+
+    // already non exhaustive and no private fields, should be ignored
+    #[non_exhaustive]
+    struct NonExhaustive(pub i32, pub i32);
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr
new file mode 100644 (file)
index 0000000..e0766c1
--- /dev/null
@@ -0,0 +1,65 @@
+error: this seems like a manual implementation of the non-exhaustive pattern
+  --> $DIR/manual_non_exhaustive_struct.rs:5:5
+   |
+LL |       struct S {
+   |       ^-------
+   |       |
+   |  _____help: add the attribute: `#[non_exhaustive] struct S`
+   | |
+LL | |         pub a: i32,
+LL | |         pub b: i32,
+LL | |         _c: (),
+LL | |     }
+   | |_____^
+   |
+   = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
+help: remove this field
+  --> $DIR/manual_non_exhaustive_struct.rs:8:9
+   |
+LL |         _c: (),
+   |         ^^^^^^
+
+error: this seems like a manual implementation of the non-exhaustive pattern
+  --> $DIR/manual_non_exhaustive_struct.rs:13:5
+   |
+LL | /     struct Sp {
+LL | |         pub a: i32,
+LL | |         pub b: i32,
+LL | |         _c: (),
+LL | |     }
+   | |_____^
+   |
+help: remove this field
+  --> $DIR/manual_non_exhaustive_struct.rs:16:9
+   |
+LL |         _c: (),
+   |         ^^^^^^
+
+error: this seems like a manual implementation of the non-exhaustive pattern
+  --> $DIR/manual_non_exhaustive_struct.rs:54:5
+   |
+LL |     struct T(pub i32, pub i32, ());
+   |     --------^^^^^^^^^^^^^^^^^^^^^^^
+   |     |
+   |     help: add the attribute: `#[non_exhaustive] struct T`
+   |
+help: remove this field
+  --> $DIR/manual_non_exhaustive_struct.rs:54:32
+   |
+LL |     struct T(pub i32, pub i32, ());
+   |                                ^^
+
+error: this seems like a manual implementation of the non-exhaustive pattern
+  --> $DIR/manual_non_exhaustive_struct.rs:58:5
+   |
+LL |     struct Tp(pub i32, pub i32, ());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove this field
+  --> $DIR/manual_non_exhaustive_struct.rs:58:33
+   |
+LL |     struct Tp(pub i32, pub i32, ());
+   |                                 ^^
+
+error: aborting due to 4 previous errors
+
index d5113df569a086c4b99b514bb59e9f0f461b3865..c7ca770434a318c53308c073bbb72ecc3e540749 100644 (file)
@@ -2,7 +2,7 @@
 
 #![feature(custom_inner_attributes)]
 #![warn(clippy::manual_split_once)]
-#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)]
+#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
 
 extern crate itertools;
 
@@ -10,27 +10,25 @@ extern crate itertools;
 use itertools::Itertools;
 
 fn main() {
-    let _ = Some("key=value".split_once('=').map_or("key=value", |x| x.0));
     let _ = "key=value".splitn(2, '=').nth(2);
-    let _ = "key=value".split_once('=').map_or("key=value", |x| x.0);
-    let _ = "key=value".split_once('=').map_or("key=value", |x| x.0);
     let _ = "key=value".split_once('=').unwrap().1;
     let _ = "key=value".split_once('=').unwrap().1;
     let (_, _) = "key=value".split_once('=').unwrap();
 
     let s = String::from("key=value");
-    let _ = s.split_once('=').map_or(&*s, |x| x.0);
+    let _ = s.split_once('=').unwrap().1;
 
     let s = Box::<str>::from("key=value");
-    let _ = s.split_once('=').map_or(&*s, |x| x.0);
+    let _ = s.split_once('=').unwrap().1;
 
     let s = &"key=value";
-    let _ = s.split_once('=').map_or(*s, |x| x.0);
+    let _ = s.split_once('=').unwrap().1;
 
     fn _f(s: &str) -> Option<&str> {
-        let _ = s.split_once("key=value").map_or(s, |x| x.0);
-        let _ = s.split_once("key=value")?.1;
-        let _ = s.split_once("key=value")?.1;
+        let _ = s.split_once('=')?.1;
+        let _ = s.split_once('=')?.1;
+        let _ = s.rsplit_once('=')?.0;
+        let _ = s.rsplit_once('=')?.0;
         None
     }
 
@@ -38,19 +36,112 @@ fn main() {
     let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
 
     // `rsplitn` gives the results in the reverse order of `rsplit_once`
-    let _ = "key=value".rsplitn(2, '=').next().unwrap();
     let _ = "key=value".rsplit_once('=').unwrap().0;
-    let _ = "key=value".rsplit_once('=').map(|x| x.1);
     let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap();
+    let _ = s.rsplit_once('=').map(|x| x.0);
+}
+
+fn indirect() -> Option<()> {
+    let (l, r) = "a.b.c".split_once('.').unwrap();
+    
+    
+
+    let (l, r) = "a.b.c".split_once('.')?;
+    
+    
+
+    let (l, r) = "a.b.c".rsplit_once('.').unwrap();
+    
+    
+
+    let (l, r) = "a.b.c".rsplit_once('.')?;
+    
+    
+
+    // could lint, currently doesn't
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let other = 1;
+    let l = iter.next()?;
+    let r = iter.next()?;
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let mut mut_binding = iter.next()?;
+    let r = iter.next()?;
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let tuple = (iter.next()?, iter.next()?);
+
+    // should not lint
+
+    let mut missing_unwrap = "a.b.c".splitn(2, '.');
+    let l = missing_unwrap.next();
+    let r = missing_unwrap.next();
+
+    let mut mixed_unrap = "a.b.c".splitn(2, '.');
+    let unwrap = mixed_unrap.next().unwrap();
+    let question_mark = mixed_unrap.next()?;
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let same_name = iter.next()?;
+    let same_name = iter.next()?;
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let shadows_existing = "d";
+    let shadows_existing = iter.next()?;
+    let r = iter.next()?;
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let becomes_shadowed = iter.next()?;
+    let becomes_shadowed = "d";
+    let r = iter.next()?;
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let l = iter.next()?;
+    let r = iter.next()?;
+    let third_usage = iter.next()?;
+
+    let mut n_three = "a.b.c".splitn(3, '.');
+    let l = n_three.next()?;
+    let r = n_three.next()?;
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    {
+        let in_block = iter.next()?;
+    }
+    let r = iter.next()?;
+
+    let mut lacks_binding = "a.b.c".splitn(2, '.');
+    let _ = lacks_binding.next()?;
+    let r = lacks_binding.next()?;
+
+    let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~");
+    let l = iter.next()?;
+    let r = iter.next()?;
+
+    let mut assigned = "";
+    let mut iter = "a.b.c".splitn(2, '.');
+    let l = iter.next()?;
+    assigned = iter.next()?;
+
+    None
 }
 
 fn _msrv_1_51() {
     #![clippy::msrv = "1.51"]
-    // `str::split_once` was stabilized in 1.16. Do not lint this
+    // `str::split_once` was stabilized in 1.52. Do not lint this
     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let a = iter.next().unwrap();
+    let b = iter.next().unwrap();
 }
 
 fn _msrv_1_52() {
     #![clippy::msrv = "1.52"]
     let _ = "key=value".split_once('=').unwrap().1;
+
+    let (a, b) = "a.b.c".split_once('.').unwrap();
+    
+    
 }
index 80e02952dbd07f8adf537d03f093a9eb580fb92e..ee2848a251ee3b2e666c49644a7768597e41782d 100644 (file)
@@ -2,7 +2,7 @@
 
 #![feature(custom_inner_attributes)]
 #![warn(clippy::manual_split_once)]
-#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)]
+#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
 
 extern crate itertools;
 
 use itertools::Itertools;
 
 fn main() {
-    let _ = "key=value".splitn(2, '=').next();
     let _ = "key=value".splitn(2, '=').nth(2);
-    let _ = "key=value".splitn(2, '=').next().unwrap();
-    let _ = "key=value".splitn(2, '=').nth(0).unwrap();
     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
     let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
     let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
 
     let s = String::from("key=value");
-    let _ = s.splitn(2, '=').next().unwrap();
+    let _ = s.splitn(2, '=').nth(1).unwrap();
 
     let s = Box::<str>::from("key=value");
-    let _ = s.splitn(2, '=').nth(0).unwrap();
+    let _ = s.splitn(2, '=').nth(1).unwrap();
 
     let s = &"key=value";
-    let _ = s.splitn(2, '=').skip(0).next().unwrap();
+    let _ = s.splitn(2, '=').skip(1).next().unwrap();
 
     fn _f(s: &str) -> Option<&str> {
-        let _ = s.splitn(2, "key=value").next()?;
-        let _ = s.splitn(2, "key=value").nth(1)?;
-        let _ = s.splitn(2, "key=value").skip(1).next()?;
+        let _ = s.splitn(2, '=').nth(1)?;
+        let _ = s.splitn(2, '=').skip(1).next()?;
+        let _ = s.rsplitn(2, '=').nth(1)?;
+        let _ = s.rsplitn(2, '=').skip(1).next()?;
         None
     }
 
@@ -38,19 +36,112 @@ fn _f(s: &str) -> Option<&str> {
     let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
 
     // `rsplitn` gives the results in the reverse order of `rsplit_once`
-    let _ = "key=value".rsplitn(2, '=').next().unwrap();
     let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
-    let _ = "key=value".rsplitn(2, '=').nth(0);
     let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
+    let _ = s.rsplitn(2, '=').nth(1);
+}
+
+fn indirect() -> Option<()> {
+    let mut iter = "a.b.c".splitn(2, '.');
+    let l = iter.next().unwrap();
+    let r = iter.next().unwrap();
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let l = iter.next()?;
+    let r = iter.next()?;
+
+    let mut iter = "a.b.c".rsplitn(2, '.');
+    let r = iter.next().unwrap();
+    let l = iter.next().unwrap();
+
+    let mut iter = "a.b.c".rsplitn(2, '.');
+    let r = iter.next()?;
+    let l = iter.next()?;
+
+    // could lint, currently doesn't
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let other = 1;
+    let l = iter.next()?;
+    let r = iter.next()?;
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let mut mut_binding = iter.next()?;
+    let r = iter.next()?;
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let tuple = (iter.next()?, iter.next()?);
+
+    // should not lint
+
+    let mut missing_unwrap = "a.b.c".splitn(2, '.');
+    let l = missing_unwrap.next();
+    let r = missing_unwrap.next();
+
+    let mut mixed_unrap = "a.b.c".splitn(2, '.');
+    let unwrap = mixed_unrap.next().unwrap();
+    let question_mark = mixed_unrap.next()?;
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let same_name = iter.next()?;
+    let same_name = iter.next()?;
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let shadows_existing = "d";
+    let shadows_existing = iter.next()?;
+    let r = iter.next()?;
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let becomes_shadowed = iter.next()?;
+    let becomes_shadowed = "d";
+    let r = iter.next()?;
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let l = iter.next()?;
+    let r = iter.next()?;
+    let third_usage = iter.next()?;
+
+    let mut n_three = "a.b.c".splitn(3, '.');
+    let l = n_three.next()?;
+    let r = n_three.next()?;
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    {
+        let in_block = iter.next()?;
+    }
+    let r = iter.next()?;
+
+    let mut lacks_binding = "a.b.c".splitn(2, '.');
+    let _ = lacks_binding.next()?;
+    let r = lacks_binding.next()?;
+
+    let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~");
+    let l = iter.next()?;
+    let r = iter.next()?;
+
+    let mut assigned = "";
+    let mut iter = "a.b.c".splitn(2, '.');
+    let l = iter.next()?;
+    assigned = iter.next()?;
+
+    None
 }
 
 fn _msrv_1_51() {
     #![clippy::msrv = "1.51"]
-    // `str::split_once` was stabilized in 1.16. Do not lint this
+    // `str::split_once` was stabilized in 1.52. Do not lint this
     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let a = iter.next().unwrap();
+    let b = iter.next().unwrap();
 }
 
 fn _msrv_1_52() {
     #![clippy::msrv = "1.52"]
     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+
+    let mut iter = "a.b.c".splitn(2, '.');
+    let a = iter.next().unwrap();
+    let b = iter.next().unwrap();
 }
index af9c7a2d41bff248f287da4e9f022b06a886b02a..2563a6904b77c928bd94e48fa1fc41473e23bd21 100644 (file)
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:13:13
+  --> $DIR/manual_split_once.rs:14:13
    |
-LL |     let _ = "key=value".splitn(2, '=').next();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some("key=value".split_once('=').map_or("key=value", |x| x.0))`
+LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
    |
    = note: `-D clippy::manual-split-once` implied by `-D warnings`
 
 error: manual implementation of `split_once`
   --> $DIR/manual_split_once.rs:15:13
    |
-LL |     let _ = "key=value".splitn(2, '=').next().unwrap();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)`
-
-error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:16:13
-   |
-LL |     let _ = "key=value".splitn(2, '=').nth(0).unwrap();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)`
-
-error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:17:13
-   |
-LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
-
-error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:18:13
-   |
 LL |     let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:19:18
+  --> $DIR/manual_split_once.rs:16:18
    |
 LL |     let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')`
 
+error: manual implementation of `split_once`
+  --> $DIR/manual_split_once.rs:19:13
+   |
+LL |     let _ = s.splitn(2, '=').nth(1).unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
+
 error: manual implementation of `split_once`
   --> $DIR/manual_split_once.rs:22:13
    |
-LL |     let _ = s.splitn(2, '=').next().unwrap();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)`
+LL |     let _ = s.splitn(2, '=').nth(1).unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
 
 error: manual implementation of `split_once`
   --> $DIR/manual_split_once.rs:25:13
    |
-LL |     let _ = s.splitn(2, '=').nth(0).unwrap();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)`
+LL |     let _ = s.splitn(2, '=').skip(1).next().unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:28:13
+  --> $DIR/manual_split_once.rs:28:17
    |
-LL |     let _ = s.splitn(2, '=').skip(0).next().unwrap();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(*s, |x| x.0)`
+LL |         let _ = s.splitn(2, '=').nth(1)?;
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1`
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:31:17
+  --> $DIR/manual_split_once.rs:29:17
    |
-LL |         let _ = s.splitn(2, "key=value").next()?;
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value").map_or(s, |x| x.0)`
+LL |         let _ = s.splitn(2, '=').skip(1).next()?;
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1`
 
-error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:32:17
+error: manual implementation of `rsplit_once`
+  --> $DIR/manual_split_once.rs:30:17
    |
-LL |         let _ = s.splitn(2, "key=value").nth(1)?;
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
+LL |         let _ = s.rsplitn(2, '=').nth(1)?;
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0`
 
-error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:33:17
+error: manual implementation of `rsplit_once`
+  --> $DIR/manual_split_once.rs:31:17
    |
-LL |         let _ = s.splitn(2, "key=value").skip(1).next()?;
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
+LL |         let _ = s.rsplitn(2, '=').skip(1).next()?;
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0`
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:42:13
+  --> $DIR/manual_split_once.rs:39:13
    |
 LL |     let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0`
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:43:13
+  --> $DIR/manual_split_once.rs:40:18
    |
-LL |     let _ = "key=value".rsplitn(2, '=').nth(0);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|x| x.1)`
+LL |     let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))`
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:44:18
+  --> $DIR/manual_split_once.rs:41:13
    |
-LL |     let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))`
+LL |     let _ = s.rsplitn(2, '=').nth(1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=').map(|x| x.0)`
+
+error: manual implementation of `split_once`
+  --> $DIR/manual_split_once.rs:45:5
+   |
+LL |     let mut iter = "a.b.c".splitn(2, '.');
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     let l = iter.next().unwrap();
+   |     ----------------------------- first usage here
+LL |     let r = iter.next().unwrap();
+   |     ----------------------------- second usage here
+   |
+help: try `split_once`
+   |
+LL |     let (l, r) = "a.b.c".split_once('.').unwrap();
+   |
+help: remove the `iter` usages
+   |
+LL -     let l = iter.next().unwrap();
+LL +     
+   | 
+help: remove the `iter` usages
+   |
+LL -     let r = iter.next().unwrap();
+LL +     
+   | 
 
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:55:13
+  --> $DIR/manual_split_once.rs:49:5
+   |
+LL |     let mut iter = "a.b.c".splitn(2, '.');
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     let l = iter.next()?;
+   |     --------------------- first usage here
+LL |     let r = iter.next()?;
+   |     --------------------- second usage here
+   |
+help: try `split_once`
+   |
+LL |     let (l, r) = "a.b.c".split_once('.')?;
+   |
+help: remove the `iter` usages
+   |
+LL -     let l = iter.next()?;
+LL +     
+   | 
+help: remove the `iter` usages
+   |
+LL -     let r = iter.next()?;
+LL +     
+   | 
+
+error: manual implementation of `rsplit_once`
+  --> $DIR/manual_split_once.rs:53:5
+   |
+LL |     let mut iter = "a.b.c".rsplitn(2, '.');
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     let r = iter.next().unwrap();
+   |     ----------------------------- first usage here
+LL |     let l = iter.next().unwrap();
+   |     ----------------------------- second usage here
+   |
+help: try `rsplit_once`
+   |
+LL |     let (l, r) = "a.b.c".rsplit_once('.').unwrap();
+   |
+help: remove the `iter` usages
+   |
+LL -     let r = iter.next().unwrap();
+LL +     
+   | 
+help: remove the `iter` usages
+   |
+LL -     let l = iter.next().unwrap();
+LL +     
+   | 
+
+error: manual implementation of `rsplit_once`
+  --> $DIR/manual_split_once.rs:57:5
+   |
+LL |     let mut iter = "a.b.c".rsplitn(2, '.');
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     let r = iter.next()?;
+   |     --------------------- first usage here
+LL |     let l = iter.next()?;
+   |     --------------------- second usage here
+   |
+help: try `rsplit_once`
+   |
+LL |     let (l, r) = "a.b.c".rsplit_once('.')?;
+   |
+help: remove the `iter` usages
+   |
+LL -     let r = iter.next()?;
+LL +     
+   | 
+help: remove the `iter` usages
+   |
+LL -     let l = iter.next()?;
+LL +     
+   | 
+
+error: manual implementation of `split_once`
+  --> $DIR/manual_split_once.rs:142:13
    |
 LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
 
-error: aborting due to 16 previous errors
+error: manual implementation of `split_once`
+  --> $DIR/manual_split_once.rs:144:5
+   |
+LL |     let mut iter = "a.b.c".splitn(2, '.');
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     let a = iter.next().unwrap();
+   |     ----------------------------- first usage here
+LL |     let b = iter.next().unwrap();
+   |     ----------------------------- second usage here
+   |
+help: try `split_once`
+   |
+LL |     let (a, b) = "a.b.c".split_once('.').unwrap();
+   |
+help: remove the `iter` usages
+   |
+LL -     let a = iter.next().unwrap();
+LL +     
+   | 
+help: remove the `iter` usages
+   |
+LL -     let b = iter.next().unwrap();
+LL +     
+   | 
+
+error: aborting due to 19 previous errors
 
index 5d57638af43495bea12bce75b930aa600ae51343..a7b36d53cd26c7cc1011553d0a1ceb2fd88268ae 100644 (file)
@@ -5,7 +5,8 @@
     unused_variables,
     overflowing_literals,
     clippy::excessive_precision,
-    clippy::inconsistent_digit_grouping
+    clippy::inconsistent_digit_grouping,
+    clippy::unusual_byte_groupings
 )]
 
 fn main() {
@@ -25,5 +26,18 @@ fn main() {
     let fail28 = 241_251_235E723_f64;
     let ok29 = 42279.911_32;
 
+    // testing that the suggestion actually fits in its type
+    let fail30 = 127_i8; // should be i8
+    let fail31 = 240_u8; // should be u8
+    let ok32 = 360_8; // doesnt fit in either, should be ignored
+    let fail33 = 0x1234_i16;
+    let fail34 = 0xABCD_u16;
+    let ok35 = 0x12345_16;
+    let fail36 = 0xFFFF_FFFF_FFFF_FFFF_u64; // u64
+
+    // issue #6129
+    let ok37 = 123_32.123;
+    let ok38 = 124_64.0;
+
     let _ = 1.123_45E1_f32;
 }
index 12171452885d2b009bdb0e0373abe765a0c29a04..c97b31965c75a7c4cd1f3030dccd3b042f23fd7c 100644 (file)
@@ -5,7 +5,8 @@
     unused_variables,
     overflowing_literals,
     clippy::excessive_precision,
-    clippy::inconsistent_digit_grouping
+    clippy::inconsistent_digit_grouping,
+    clippy::unusual_byte_groupings
 )]
 
 fn main() {
@@ -25,5 +26,18 @@ fn main() {
     let fail28 = 241251235E723_64;
     let ok29 = 42279.911_32;
 
+    // testing that the suggestion actually fits in its type
+    let fail30 = 127_8; // should be i8
+    let fail31 = 240_8; // should be u8
+    let ok32 = 360_8; // doesnt fit in either, should be ignored
+    let fail33 = 0x1234_16;
+    let fail34 = 0xABCD_16;
+    let ok35 = 0x12345_16;
+    let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64
+
+    // issue #6129
+    let ok37 = 123_32.123;
+    let ok38 = 124_64.0;
+
     let _ = 1.12345E1_32;
 }
index d24543c26e4b0d7e48cbb4647f03ebcd2abf3cb0..fb761d9bde45255085e56a861d791d370516c251 100644 (file)
@@ -1,5 +1,5 @@
 error: mistyped literal suffix
-  --> $DIR/mistyped_literal_suffix.rs:12:18
+  --> $DIR/mistyped_literal_suffix.rs:13:18
    |
 LL |     let fail14 = 2_32;
    |                  ^^^^ help: did you mean to write: `2_i32`
@@ -7,64 +7,94 @@ LL |     let fail14 = 2_32;
    = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default
 
 error: mistyped literal suffix
-  --> $DIR/mistyped_literal_suffix.rs:13:18
+  --> $DIR/mistyped_literal_suffix.rs:14:18
    |
 LL |     let fail15 = 4_64;
    |                  ^^^^ help: did you mean to write: `4_i64`
 
 error: mistyped literal suffix
-  --> $DIR/mistyped_literal_suffix.rs:14:18
+  --> $DIR/mistyped_literal_suffix.rs:15:18
    |
 LL |     let fail16 = 7_8; //
    |                  ^^^ help: did you mean to write: `7_i8`
 
 error: mistyped literal suffix
-  --> $DIR/mistyped_literal_suffix.rs:15:18
+  --> $DIR/mistyped_literal_suffix.rs:16:18
    |
 LL |     let fail17 = 23_16; //
    |                  ^^^^^ help: did you mean to write: `23_i16`
 
 error: mistyped literal suffix
-  --> $DIR/mistyped_literal_suffix.rs:18:18
+  --> $DIR/mistyped_literal_suffix.rs:19:18
    |
 LL |     let fail20 = 2__8; //
    |                  ^^^^ help: did you mean to write: `2_i8`
 
 error: mistyped literal suffix
-  --> $DIR/mistyped_literal_suffix.rs:19:18
+  --> $DIR/mistyped_literal_suffix.rs:20:18
    |
 LL |     let fail21 = 4___16; //
    |                  ^^^^^^ help: did you mean to write: `4_i16`
 
 error: mistyped literal suffix
-  --> $DIR/mistyped_literal_suffix.rs:22:18
+  --> $DIR/mistyped_literal_suffix.rs:23:18
    |
 LL |     let fail25 = 1E2_32;
    |                  ^^^^^^ help: did you mean to write: `1E2_f32`
 
 error: mistyped literal suffix
-  --> $DIR/mistyped_literal_suffix.rs:23:18
+  --> $DIR/mistyped_literal_suffix.rs:24:18
    |
 LL |     let fail26 = 43E7_64;
    |                  ^^^^^^^ help: did you mean to write: `43E7_f64`
 
 error: mistyped literal suffix
-  --> $DIR/mistyped_literal_suffix.rs:24:18
+  --> $DIR/mistyped_literal_suffix.rs:25:18
    |
 LL |     let fail27 = 243E17_32;
    |                  ^^^^^^^^^ help: did you mean to write: `243E17_f32`
 
 error: mistyped literal suffix
-  --> $DIR/mistyped_literal_suffix.rs:25:18
+  --> $DIR/mistyped_literal_suffix.rs:26:18
    |
 LL |     let fail28 = 241251235E723_64;
    |                  ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64`
 
 error: mistyped literal suffix
-  --> $DIR/mistyped_literal_suffix.rs:28:13
+  --> $DIR/mistyped_literal_suffix.rs:30:18
+   |
+LL |     let fail30 = 127_8; // should be i8
+   |                  ^^^^^ help: did you mean to write: `127_i8`
+
+error: mistyped literal suffix
+  --> $DIR/mistyped_literal_suffix.rs:31:18
+   |
+LL |     let fail31 = 240_8; // should be u8
+   |                  ^^^^^ help: did you mean to write: `240_u8`
+
+error: mistyped literal suffix
+  --> $DIR/mistyped_literal_suffix.rs:33:18
+   |
+LL |     let fail33 = 0x1234_16;
+   |                  ^^^^^^^^^ help: did you mean to write: `0x1234_i16`
+
+error: mistyped literal suffix
+  --> $DIR/mistyped_literal_suffix.rs:34:18
+   |
+LL |     let fail34 = 0xABCD_16;
+   |                  ^^^^^^^^^ help: did you mean to write: `0xABCD_u16`
+
+error: mistyped literal suffix
+  --> $DIR/mistyped_literal_suffix.rs:36:18
+   |
+LL |     let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to write: `0xFFFF_FFFF_FFFF_FFFF_u64`
+
+error: mistyped literal suffix
+  --> $DIR/mistyped_literal_suffix.rs:42:13
    |
 LL |     let _ = 1.12345E1_32;
    |             ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32`
 
-error: aborting due to 11 previous errors
+error: aborting due to 16 previous errors
 
index a9a04c8f56b945ca7c0e94b35a211119f7799a2d..370dbd5882161c837c4ffd14a531c99aa7145f0d 100644 (file)
@@ -5,7 +5,7 @@
 
 impl Foo {
     fn this_wont_hurt_a_bit(&self) -> &mut Foo {
-        unimplemented!()
+        unsafe { unimplemented!() }
     }
 }
 
@@ -15,29 +15,37 @@ trait Ouch {
 
 impl Ouch for Foo {
     fn ouch(x: &Foo) -> &mut Foo {
-        unimplemented!()
+        unsafe { unimplemented!() }
     }
 }
 
 fn fail(x: &u32) -> &mut u16 {
-    unimplemented!()
+    unsafe { unimplemented!() }
 }
 
 fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 {
-    unimplemented!()
+    unsafe { unimplemented!() }
 }
 
 fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
-    unimplemented!()
+    unsafe { unimplemented!() }
 }
 
 // this is OK, because the result borrows y
 fn works<'a>(x: &u32, y: &'a mut u32) -> &'a mut u32 {
-    unimplemented!()
+    unsafe { unimplemented!() }
 }
 
 // this is also OK, because the result could borrow y
 fn also_works<'a>(x: &'a u32, y: &'a mut u32) -> &'a mut u32 {
+    unsafe { unimplemented!() }
+}
+
+unsafe fn also_broken(x: &u32) -> &mut u32 {
+    unimplemented!()
+}
+
+fn without_unsafe(x: &u32) -> &mut u32 {
     unimplemented!()
 }
 
index 4787999920bc219fde94225fd530ba3fba2c3253..b76d6a13ffb9cf312d2285886671a976549d18b6 100644 (file)
@@ -59,5 +59,17 @@ note: immutable borrow here
 LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
    |                           ^^^^^^^     ^^^^^^^
 
-error: aborting due to 5 previous errors
+error: mutable borrow from immutable input(s)
+  --> $DIR/mut_from_ref.rs:44:35
+   |
+LL | unsafe fn also_broken(x: &u32) -> &mut u32 {
+   |                                   ^^^^^^^^
+   |
+note: immutable borrow here
+  --> $DIR/mut_from_ref.rs:44:26
+   |
+LL | unsafe fn also_broken(x: &u32) -> &mut u32 {
+   |                          ^^^^
+
+error: aborting due to 6 previous errors
 
index f00f9ee4c331b716bd32ad7049f09968ebee88fa..c1685f7b6d7adeba6f5783c4cb7320d669c61904 100644 (file)
@@ -1,6 +1,11 @@
 // run-rustfix
 #![warn(clippy::needless_for_each)]
-#![allow(unused, clippy::needless_return, clippy::match_single_binding)]
+#![allow(
+    unused,
+    clippy::needless_return,
+    clippy::match_single_binding,
+    clippy::let_unit_value
+)]
 
 use std::collections::HashMap;
 
index 1bd400d348ba92ce635f45d8b2dea743cbf6918d..ad17b0956fa93149d2fe436ae3e22755ba1edeb2 100644 (file)
@@ -1,6 +1,11 @@
 // run-rustfix
 #![warn(clippy::needless_for_each)]
-#![allow(unused, clippy::needless_return, clippy::match_single_binding)]
+#![allow(
+    unused,
+    clippy::needless_return,
+    clippy::match_single_binding,
+    clippy::let_unit_value
+)]
 
 use std::collections::HashMap;
 
index 6487e57266c71a7da8a65bc3f722cfffa30884b5..08e995851d7a5c9717bbb5e7c9a7239a80003ae9 100644 (file)
@@ -1,5 +1,5 @@
 error: needless use of `for_each`
-  --> $DIR/needless_for_each_fixable.rs:10:5
+  --> $DIR/needless_for_each_fixable.rs:15:5
    |
 LL | /     v.iter().for_each(|elem| {
 LL | |         acc += elem;
@@ -15,7 +15,7 @@ LL +     }
    |
 
 error: needless use of `for_each`
-  --> $DIR/needless_for_each_fixable.rs:13:5
+  --> $DIR/needless_for_each_fixable.rs:18:5
    |
 LL | /     v.into_iter().for_each(|elem| {
 LL | |         acc += elem;
@@ -30,7 +30,7 @@ LL +     }
    |
 
 error: needless use of `for_each`
-  --> $DIR/needless_for_each_fixable.rs:17:5
+  --> $DIR/needless_for_each_fixable.rs:22:5
    |
 LL | /     [1, 2, 3].iter().for_each(|elem| {
 LL | |         acc += elem;
@@ -45,7 +45,7 @@ LL +     }
    |
 
 error: needless use of `for_each`
-  --> $DIR/needless_for_each_fixable.rs:22:5
+  --> $DIR/needless_for_each_fixable.rs:27:5
    |
 LL | /     hash_map.iter().for_each(|(k, v)| {
 LL | |         acc += k + v;
@@ -60,7 +60,7 @@ LL +     }
    |
 
 error: needless use of `for_each`
-  --> $DIR/needless_for_each_fixable.rs:25:5
+  --> $DIR/needless_for_each_fixable.rs:30:5
    |
 LL | /     hash_map.iter_mut().for_each(|(k, v)| {
 LL | |         acc += *k + *v;
@@ -75,7 +75,7 @@ LL +     }
    |
 
 error: needless use of `for_each`
-  --> $DIR/needless_for_each_fixable.rs:28:5
+  --> $DIR/needless_for_each_fixable.rs:33:5
    |
 LL | /     hash_map.keys().for_each(|k| {
 LL | |         acc += k;
@@ -90,7 +90,7 @@ LL +     }
    |
 
 error: needless use of `for_each`
-  --> $DIR/needless_for_each_fixable.rs:31:5
+  --> $DIR/needless_for_each_fixable.rs:36:5
    |
 LL | /     hash_map.values().for_each(|v| {
 LL | |         acc += v;
@@ -105,7 +105,7 @@ LL +     }
    |
 
 error: needless use of `for_each`
-  --> $DIR/needless_for_each_fixable.rs:38:5
+  --> $DIR/needless_for_each_fixable.rs:43:5
    |
 LL | /     my_vec().iter().for_each(|elem| {
 LL | |         acc += elem;
index 89e012c066fe4d9593460873e0049ada3c8a8993..54e66b391b8d8a46068df048cf91c17bf8a5d03c 100644 (file)
@@ -1,4 +1,15 @@
-#![allow(unused)]
+#![feature(let_chains)]
+#![allow(unused, clippy::nonminimal_bool, clippy::let_unit_value)]
+
+use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
+use std::rc::Rc;
+
+struct SignificantDrop;
+impl std::ops::Drop for SignificantDrop {
+    fn drop(&mut self) {
+        println!("dropped");
+    }
+}
 
 fn main() {
     let a;
@@ -17,13 +28,6 @@ fn main() {
         b = "five"
     }
 
-    let c;
-    if let Some(n) = Some(5) {
-        c = n;
-    } else {
-        c = -50;
-    }
-
     let d;
     if true {
         let temp = 5;
@@ -36,7 +40,7 @@ fn main() {
     if true {
         e = format!("{} {}", a, b);
     } else {
-        e = format!("{}", c);
+        e = format!("{}", n);
     }
 
     let f;
@@ -52,7 +56,27 @@ fn main() {
         panic!();
     }
 
-    println!("{}", a);
+    // Drop order only matters if both are significant
+    let x;
+    let y = SignificantDrop;
+    x = 1;
+
+    let x;
+    let y = 1;
+    x = SignificantDrop;
+
+    let x;
+    // types that should be considered insignificant
+    let y = 1;
+    let y = "2";
+    let y = String::new();
+    let y = vec![3.0];
+    let y = HashMap::<usize, usize>::new();
+    let y = BTreeMap::<usize, usize>::new();
+    let y = HashSet::<usize>::new();
+    let y = BTreeSet::<usize>::new();
+    let y = Box::new(4);
+    x = SignificantDrop;
 }
 
 async fn in_async() -> &'static str {
@@ -176,5 +200,32 @@ macro_rules! in_macro {
     }
     in_macro!();
 
-    println!("{}", x);
+    // ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613
+    let x;
+    if let Some(n) = Some("v") {
+        x = 1;
+    } else {
+        x = 2;
+    }
+
+    let x;
+    if true && let Some(n) = Some("let chains too") {
+        x = 1;
+    } else {
+        x = 2;
+    }
+
+    // ignore mut bindings
+    // https://github.com/shepmaster/twox-hash/blob/b169c16d86eb8ea4a296b0acb9d00ca7e3c3005f/src/sixty_four.rs#L88-L93
+    // https://github.com/dtolnay/thiserror/blob/21c26903e29cb92ba1a7ff11e82ae2001646b60d/tests/test_generics.rs#L91-L100
+    let mut x: usize;
+    x = 1;
+    x = 2;
+    x = 3;
+
+    // should not move the declaration if `x` has a significant drop, and there
+    // is another binding with a significant drop between it and the first usage
+    let x;
+    let y = SignificantDrop;
+    x = SignificantDrop;
 }
index ef79e635d2ae623da06255747c8dc3fd66f059e4..d33a117b288cb5482a5bd0cc87dc446e3f091109 100644 (file)
@@ -1,5 +1,5 @@
-error: unneeded late initalization
-  --> $DIR/needless_late_init.rs:4:5
+error: unneeded late initialization
+  --> $DIR/needless_late_init.rs:15:5
    |
 LL |     let a;
    |     ^^^^^^
@@ -20,8 +20,8 @@ help: add a semicolon after the `match` expression
 LL |     };
    |      +
 
-error: unneeded late initalization
-  --> $DIR/needless_late_init.rs:13:5
+error: unneeded late initialization
+  --> $DIR/needless_late_init.rs:24:5
    |
 LL |     let b;
    |     ^^^^^^
@@ -41,29 +41,8 @@ help: add a semicolon after the `if` expression
 LL |     };
    |      +
 
-error: unneeded late initalization
-  --> $DIR/needless_late_init.rs:20:5
-   |
-LL |     let c;
-   |     ^^^^^^
-   |
-help: declare `c` here
-   |
-LL |     let c = if let Some(n) = Some(5) {
-   |     +++++++
-help: remove the assignments from the branches
-   |
-LL ~         n
-LL |     } else {
-LL ~         -50
-   |
-help: add a semicolon after the `if` expression
-   |
-LL |     };
-   |      +
-
-error: unneeded late initalization
-  --> $DIR/needless_late_init.rs:27:5
+error: unneeded late initialization
+  --> $DIR/needless_late_init.rs:31:5
    |
 LL |     let d;
    |     ^^^^^^
@@ -83,8 +62,8 @@ help: add a semicolon after the `if` expression
 LL |     };
    |      +
 
-error: unneeded late initalization
-  --> $DIR/needless_late_init.rs:35:5
+error: unneeded late initialization
+  --> $DIR/needless_late_init.rs:39:5
    |
 LL |     let e;
    |     ^^^^^^
@@ -97,15 +76,15 @@ help: remove the assignments from the branches
    |
 LL ~         format!("{} {}", a, b)
 LL |     } else {
-LL ~         format!("{}", c)
+LL ~         format!("{}", n)
    |
 help: add a semicolon after the `if` expression
    |
 LL |     };
    |      +
 
-error: unneeded late initalization
-  --> $DIR/needless_late_init.rs:42:5
+error: unneeded late initialization
+  --> $DIR/needless_late_init.rs:46:5
    |
 LL |     let f;
    |     ^^^^^^
@@ -120,8 +99,8 @@ LL -         1 => f = "three",
 LL +         1 => "three",
    | 
 
-error: unneeded late initalization
-  --> $DIR/needless_late_init.rs:48:5
+error: unneeded late initialization
+  --> $DIR/needless_late_init.rs:52:5
    |
 LL |     let g: usize;
    |     ^^^^^^^^^^^^^
@@ -140,8 +119,50 @@ help: add a semicolon after the `if` expression
 LL |     };
    |      +
 
-error: unneeded late initalization
-  --> $DIR/needless_late_init.rs:63:5
+error: unneeded late initialization
+  --> $DIR/needless_late_init.rs:60:5
+   |
+LL |     let x;
+   |     ^^^^^^ created here
+LL |     let y = SignificantDrop;
+LL |     x = 1;
+   |     ^^^^^ initialised here
+   |
+help: declare `x` here
+   |
+LL |     let x = 1;
+   |     ~~~~~
+
+error: unneeded late initialization
+  --> $DIR/needless_late_init.rs:64:5
+   |
+LL |     let x;
+   |     ^^^^^^ created here
+LL |     let y = 1;
+LL |     x = SignificantDrop;
+   |     ^^^^^^^^^^^^^^^^^^^ initialised here
+   |
+help: declare `x` here
+   |
+LL |     let x = SignificantDrop;
+   |     ~~~~~
+
+error: unneeded late initialization
+  --> $DIR/needless_late_init.rs:68:5
+   |
+LL |     let x;
+   |     ^^^^^^ created here
+...
+LL |     x = SignificantDrop;
+   |     ^^^^^^^^^^^^^^^^^^^ initialised here
+   |
+help: declare `x` here
+   |
+LL |     let x = SignificantDrop;
+   |     ~~~~~
+
+error: unneeded late initialization
+  --> $DIR/needless_late_init.rs:87:5
    |
 LL |     let a;
    |     ^^^^^^
@@ -161,8 +182,8 @@ help: add a semicolon after the `match` expression
 LL |     };
    |      +
 
-error: unneeded late initalization
-  --> $DIR/needless_late_init.rs:80:5
+error: unneeded late initialization
+  --> $DIR/needless_late_init.rs:104:5
    |
 LL |     let a;
    |     ^^^^^^
@@ -182,5 +203,5 @@ help: add a semicolon after the `match` expression
 LL |     };
    |      +
 
-error: aborting due to 9 previous errors
+error: aborting due to 11 previous errors
 
index b516f9d86b75b652b0e4f788c85cc16853d573a4..724477e8691df1df790fcdb3efa62cccb49da932 100644 (file)
@@ -15,11 +15,5 @@ fn main() {
     let d: usize = 1;
 
     
-    let mut e = 1;
-    e = 2;
-
-    
-    let h = format!("{}", e);
-
-    println!("{}", a);
+    let e = format!("{}", d);
 }
index 75a4bc916deacb5cbd52e2f10c9f1fa5643abf5d..3e6bd36367275dd2f2a55118f3da9dd0fd8da22d 100644 (file)
@@ -14,12 +14,6 @@ fn main() {
     let d: usize;
     d = 1;
 
-    let mut e;
-    e = 1;
-    e = 2;
-
-    let h;
-    h = format!("{}", e);
-
-    println!("{}", a);
+    let e;
+    e = format!("{}", d);
 }
index 3f3d4f5286b2b2e088a737288c3759955bbcd1af..8c664309e3e83fe4838fbcfc12d7c5e586109c42 100644 (file)
@@ -1,8 +1,10 @@
-error: unneeded late initalization
+error: unneeded late initialization
   --> $DIR/needless_late_init_fixable.rs:6:5
    |
 LL |     let a;
-   |     ^^^^^^
+   |     ^^^^^^ created here
+LL |     a = "zero";
+   |     ^^^^^^^^^^ initialised here
    |
    = note: `-D clippy::needless-late-init` implied by `-D warnings`
 help: declare `a` here
@@ -10,60 +12,59 @@ help: declare `a` here
 LL |     let a = "zero";
    |     ~~~~~
 
-error: unneeded late initalization
+error: unneeded late initialization
   --> $DIR/needless_late_init_fixable.rs:9:5
    |
 LL |     let b;
-   |     ^^^^^^
+   |     ^^^^^^ created here
+LL |     let c;
+LL |     b = 1;
+   |     ^^^^^ initialised here
    |
 help: declare `b` here
    |
 LL |     let b = 1;
    |     ~~~~~
 
-error: unneeded late initalization
+error: unneeded late initialization
   --> $DIR/needless_late_init_fixable.rs:10:5
    |
 LL |     let c;
-   |     ^^^^^^
+   |     ^^^^^^ created here
+LL |     b = 1;
+LL |     c = 2;
+   |     ^^^^^ initialised here
    |
 help: declare `c` here
    |
 LL |     let c = 2;
    |     ~~~~~
 
-error: unneeded late initalization
+error: unneeded late initialization
   --> $DIR/needless_late_init_fixable.rs:14:5
    |
 LL |     let d: usize;
-   |     ^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^ created here
+LL |     d = 1;
+   |     ^^^^^ initialised here
    |
 help: declare `d` here
    |
 LL |     let d: usize = 1;
    |     ~~~~~~~~~~~~
 
-error: unneeded late initalization
+error: unneeded late initialization
   --> $DIR/needless_late_init_fixable.rs:17:5
    |
-LL |     let mut e;
-   |     ^^^^^^^^^^
+LL |     let e;
+   |     ^^^^^^ created here
+LL |     e = format!("{}", d);
+   |     ^^^^^^^^^^^^^^^^^^^^ initialised here
    |
 help: declare `e` here
    |
-LL |     let mut e = 1;
-   |     ~~~~~~~~~
-
-error: unneeded late initalization
-  --> $DIR/needless_late_init_fixable.rs:21:5
-   |
-LL |     let h;
-   |     ^^^^^^
-   |
-help: declare `h` here
-   |
-LL |     let h = format!("{}", e);
+LL |     let e = format!("{}", d);
    |     ~~~~~
 
-error: aborting due to 6 previous errors
+error: aborting due to 5 previous errors
 
index 9ccccaa1725a65dc8867a1a48e9698c811682886..b997e5316cf3f451442295cc4a776f4bf41a8c3f 100644 (file)
@@ -80,6 +80,18 @@ fn if_let_option() {
     } else {
         None
     };
+
+    // Don't trigger
+    let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) };
+}
+
+fn if_let_option_result() -> Result<(), ()> {
+    fn f(x: i32) -> Result<Option<i32>, ()> {
+        Ok(Some(x))
+    }
+    // Don't trigger
+    let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? };
+    Ok(())
 }
 
 fn if_let_result() {
index d210ecff7f1633a8a6e157e643cc9d42725457e9..90482775a1eebbf7d1fa1621b3c3a233b3739d5a 100644 (file)
@@ -103,6 +103,18 @@ fn do_something() {}
     } else {
         None
     };
+
+    // Don't trigger
+    let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) };
+}
+
+fn if_let_option_result() -> Result<(), ()> {
+    fn f(x: i32) -> Result<Option<i32>, ()> {
+        Ok(Some(x))
+    }
+    // Don't trigger
+    let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? };
+    Ok(())
 }
 
 fn if_let_result() {
index 34c5226f06057611d9eb87ac5c631b4d241850e8..2d679631c6f0d71d9da899d9966bb3bb8e595e3c 100644 (file)
@@ -72,19 +72,19 @@ LL |     let _ = if let Some(a) = Some(1) { Some(a) } else { None };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)`
 
 error: this if-let expression is unnecessary
-  --> $DIR/needless_match.rs:110:31
+  --> $DIR/needless_match.rs:122:31
    |
 LL |     let _: Result<i32, i32> = if let Err(e) = x { Err(e) } else { x };
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
 
 error: this if-let expression is unnecessary
-  --> $DIR/needless_match.rs:111:31
+  --> $DIR/needless_match.rs:123:31
    |
 LL |     let _: Result<i32, i32> = if let Ok(val) = x { Ok(val) } else { x };
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
 
 error: this if-let expression is unnecessary
-  --> $DIR/needless_match.rs:117:21
+  --> $DIR/needless_match.rs:129:21
    |
 LL |       let _: Simple = if let Simple::A = x {
    |  _____________________^
@@ -97,7 +97,7 @@ LL | |     };
    | |_____^ help: replace it with: `x`
 
 error: this match expression is unnecessary
-  --> $DIR/needless_match.rs:156:26
+  --> $DIR/needless_match.rs:168:26
    |
 LL |           let _: Complex = match ce {
    |  __________________________^
index c09b07db3dca9787a2c6d585057dcf188e39b508..acd22c6bb43372e6048a016d656f1bbd6aec2b4f 100644 (file)
@@ -16,6 +16,20 @@ fn main() {
     let _ = Some(Box::new(1)).as_deref();
     let _ = Some(Box::new(1)).as_deref_mut();
 
+    let mut y = 0;
+    let mut x = Some(&mut y);
+    for _ in 0..3 {
+        let _ = x.as_deref_mut();
+    }
+
+    let mut y = 0;
+    let mut x = Some(&mut y);
+    let mut closure = || {
+        let _ = x.as_deref_mut();
+    };
+    closure();
+    closure();
+
     // #7846
     let mut i = 0;
     let mut opt_vec = vec![Some(&mut i)];
index c3ba27ecccf22fe8c09ca1327c62944f8b72d75b..61eda5052a21efed8363013d28c364f042943736 100644 (file)
@@ -16,6 +16,20 @@ fn main() {
     let _ = Some(Box::new(1)).as_deref();
     let _ = Some(Box::new(1)).as_deref_mut();
 
+    let mut y = 0;
+    let mut x = Some(&mut y);
+    for _ in 0..3 {
+        let _ = x.as_deref_mut();
+    }
+
+    let mut y = 0;
+    let mut x = Some(&mut y);
+    let mut closure = || {
+        let _ = x.as_deref_mut();
+    };
+    closure();
+    closure();
+
     // #7846
     let mut i = 0;
     let mut opt_vec = vec![Some(&mut i)];
diff --git a/src/tools/clippy/tests/ui/needless_option_take.fixed b/src/tools/clippy/tests/ui/needless_option_take.fixed
new file mode 100644 (file)
index 0000000..29691e8
--- /dev/null
@@ -0,0 +1,15 @@
+// run-rustfix
+
+fn main() {
+    println!("Testing non erroneous option_take_on_temporary");
+    let mut option = Some(1);
+    let _ = Box::new(move || option.take().unwrap());
+
+    println!("Testing non erroneous option_take_on_temporary");
+    let x = Some(3);
+    x.as_ref();
+
+    println!("Testing erroneous option_take_on_temporary");
+    let x = Some(3);
+    x.as_ref();
+}
diff --git a/src/tools/clippy/tests/ui/needless_option_take.rs b/src/tools/clippy/tests/ui/needless_option_take.rs
new file mode 100644 (file)
index 0000000..9f4109e
--- /dev/null
@@ -0,0 +1,15 @@
+// run-rustfix
+
+fn main() {
+    println!("Testing non erroneous option_take_on_temporary");
+    let mut option = Some(1);
+    let _ = Box::new(move || option.take().unwrap());
+
+    println!("Testing non erroneous option_take_on_temporary");
+    let x = Some(3);
+    x.as_ref();
+
+    println!("Testing erroneous option_take_on_temporary");
+    let x = Some(3);
+    x.as_ref().take();
+}
diff --git a/src/tools/clippy/tests/ui/needless_option_take.stderr b/src/tools/clippy/tests/ui/needless_option_take.stderr
new file mode 100644 (file)
index 0000000..cb3bf01
--- /dev/null
@@ -0,0 +1,10 @@
+error: called `Option::take()` on a temporary value
+  --> $DIR/needless_option_take.rs:14:5
+   |
+LL |     x.as_ref().take();
+   |     ^^^^^^^^^^^^^^^^^ help: try: `x.as_ref()`
+   |
+   = note: `-D clippy::needless-option-take` implied by `-D warnings`
+
+error: aborting due to previous error
+
index f6a4b2f17d33d81f048a1c828fe3bc2cfb744631..61f5fc4e679edafbb609cfa70dd550ff3df2e39e 100644 (file)
@@ -24,4 +24,24 @@ fn main() {
     let _ = str.rsplitn(2, '=').nth(1);
     let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
     let (_, _) = str.rsplit('=').next_tuple().unwrap();
+
+    let _ = str.split('=').next();
+    let _ = str.split('=').nth(3);
+    let _ = str.splitn(5, '=').nth(4);
+    let _ = str.splitn(5, '=').nth(5);
+}
+
+fn _question_mark(s: &str) -> Option<()> {
+    let _ = s.split('=').next()?;
+    let _ = s.split('=').nth(0)?;
+    let _ = s.rsplit('=').next()?;
+    let _ = s.rsplit('=').nth(0)?;
+
+    Some(())
+}
+
+fn _test_msrv() {
+    #![clippy::msrv = "1.51"]
+    // `manual_split_once` MSRV shouldn't apply to `needless_splitn`
+    let _ = "key=value".split('=').nth(0).unwrap();
 }
index 6ba32255bb2d767da5e6bc6c155cd7b05d22ef0e..71d9a7077faa6f354e9475507b456d8e53e884e9 100644 (file)
@@ -24,4 +24,24 @@ fn main() {
     let _ = str.rsplitn(2, '=').nth(1);
     let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
     let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
+
+    let _ = str.splitn(5, '=').next();
+    let _ = str.splitn(5, '=').nth(3);
+    let _ = str.splitn(5, '=').nth(4);
+    let _ = str.splitn(5, '=').nth(5);
+}
+
+fn _question_mark(s: &str) -> Option<()> {
+    let _ = s.splitn(2, '=').next()?;
+    let _ = s.splitn(2, '=').nth(0)?;
+    let _ = s.rsplitn(2, '=').next()?;
+    let _ = s.rsplitn(2, '=').nth(0)?;
+
+    Some(())
+}
+
+fn _test_msrv() {
+    #![clippy::msrv = "1.51"]
+    // `manual_split_once` MSRV shouldn't apply to `needless_splitn`
+    let _ = "key=value".splitn(2, '=').nth(0).unwrap();
 }
index 66de2256554e3ad2ea2ea1b0ddd13891a94e6845..f112b29e7f2066ccb0751f049d479b45d5a79c58 100644 (file)
@@ -36,5 +36,47 @@ error: unnecessary use of `rsplitn`
 LL |     let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
    |                  ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
 
-error: aborting due to 6 previous errors
+error: unnecessary use of `splitn`
+  --> $DIR/needless_splitn.rs:28:13
+   |
+LL |     let _ = str.splitn(5, '=').next();
+   |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+
+error: unnecessary use of `splitn`
+  --> $DIR/needless_splitn.rs:29:13
+   |
+LL |     let _ = str.splitn(5, '=').nth(3);
+   |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+
+error: unnecessary use of `splitn`
+  --> $DIR/needless_splitn.rs:35:13
+   |
+LL |     let _ = s.splitn(2, '=').next()?;
+   |             ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')`
+
+error: unnecessary use of `splitn`
+  --> $DIR/needless_splitn.rs:36:13
+   |
+LL |     let _ = s.splitn(2, '=').nth(0)?;
+   |             ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')`
+
+error: unnecessary use of `rsplitn`
+  --> $DIR/needless_splitn.rs:37:13
+   |
+LL |     let _ = s.rsplitn(2, '=').next()?;
+   |             ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')`
+
+error: unnecessary use of `rsplitn`
+  --> $DIR/needless_splitn.rs:38:13
+   |
+LL |     let _ = s.rsplitn(2, '=').nth(0)?;
+   |             ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')`
+
+error: unnecessary use of `splitn`
+  --> $DIR/needless_splitn.rs:46:13
+   |
+LL |     let _ = "key=value".splitn(2, '=').nth(0).unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split('=')`
+
+error: aborting due to 13 previous errors
 
index e94f99c95f481145edad86d5c112673dcd72d082..538927b18055a289c771f5cb1cfdc1ba9bfad531 100644 (file)
@@ -1,4 +1,4 @@
-#![allow(dead_code, clippy::missing_safety_doc)]
+#![allow(dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes)]
 #![warn(clippy::new_without_default)]
 
 pub struct Foo;
index 9937005d68d8ae43092952b1a4f307d8f21f816c..583096ac054a15dcc076570b91eb6991d84c258c 100644 (file)
@@ -1,5 +1,5 @@
 #![warn(clippy::all)]
-#![allow(unused, clippy::println_empty_string, non_snake_case)]
+#![allow(unused, clippy::println_empty_string, non_snake_case, clippy::let_unit_value)]
 
 #[derive(Clone, Debug)]
 enum MaybeInst {
index 1da97e9687988aa856051a79a1a7b868b6b0d7b6..3710b3e9c81e0ac7263f3df216cbddbe77f00e74 100644 (file)
@@ -30,4 +30,9 @@ fn main() {
 
     // Ok because it's in macro
     let _ = tuple_struct_init!();
+
+    type Alias = TupleStruct;
+
+    // Aliases can't be tuple constructed #8638
+    let _ = Alias { 0: 0, 1: 1, 2: 2 };
 }
index 08ec405a5606e2ae8772754dbcf35697ad5b4cdb..2af84bc0642a534683b963c3d51b37e85761eae4 100644 (file)
@@ -38,4 +38,9 @@ fn main() {
 
     // Ok because it's in macro
     let _ = tuple_struct_init!();
+
+    type Alias = TupleStruct;
+
+    // Aliases can't be tuple constructed #8638
+    let _ = Alias { 0: 0, 1: 1, 2: 2 };
 }
index 7790c816481d1b78bbc2320c3b1cf57d36a3242b..e12e13a57f1f0b43286aa3b2cf498e385566342e 100644 (file)
@@ -1,6 +1,11 @@
 // run-rustfix
 #![warn(clippy::option_if_let_else)]
-#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)]
+#![allow(
+    clippy::redundant_closure,
+    clippy::ref_option_ref,
+    clippy::equatable_if_let,
+    clippy::let_unit_value
+)]
 
 fn bad1(string: Option<&str>) -> (bool, &str) {
     string.map_or((false, "hello"), |x| (true, x))
index 3d9f76ee4a6b570430f18538518b18cc33a47973..b5206fc26a9e13ae091cfe7cbdb528d56c19901a 100644 (file)
@@ -1,6 +1,11 @@
 // run-rustfix
 #![warn(clippy::option_if_let_else)]
-#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)]
+#![allow(
+    clippy::redundant_closure,
+    clippy::ref_option_ref,
+    clippy::equatable_if_let,
+    clippy::let_unit_value
+)]
 
 fn bad1(string: Option<&str>) -> (bool, &str) {
     if let Some(x) = string {
index 546131ceb5b633688ae9113df9a96f41e373fcd1..40aef977b989bfd057c7c7fb1b4b7805fd79e08f 100644 (file)
@@ -1,5 +1,5 @@
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:6:5
+  --> $DIR/option_if_let_else.rs:11:5
    |
 LL | /     if let Some(x) = string {
 LL | |         (true, x)
@@ -11,19 +11,19 @@ LL | |     }
    = note: `-D clippy::option-if-let-else` implied by `-D warnings`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:24:13
+  --> $DIR/option_if_let_else.rs:29:13
    |
 LL |     let _ = if let Some(s) = *string { s.len() } else { 0 };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:25:13
+  --> $DIR/option_if_let_else.rs:30:13
    |
 LL |     let _ = if let Some(s) = &num { s } else { &0 };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:26:13
+  --> $DIR/option_if_let_else.rs:31:13
    |
 LL |       let _ = if let Some(s) = &mut num {
    |  _____________^
@@ -43,13 +43,13 @@ LL ~     });
    |
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:32:13
+  --> $DIR/option_if_let_else.rs:37:13
    |
 LL |     let _ = if let Some(ref s) = num { s } else { &0 };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:33:13
+  --> $DIR/option_if_let_else.rs:38:13
    |
 LL |       let _ = if let Some(mut s) = num {
    |  _____________^
@@ -69,7 +69,7 @@ LL ~     });
    |
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:39:13
+  --> $DIR/option_if_let_else.rs:44:13
    |
 LL |       let _ = if let Some(ref mut s) = num {
    |  _____________^
@@ -89,7 +89,7 @@ LL ~     });
    |
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:48:5
+  --> $DIR/option_if_let_else.rs:53:5
    |
 LL | /     if let Some(x) = arg {
 LL | |         let y = x * x;
@@ -108,7 +108,7 @@ LL +     })
    |
 
 error: use Option::map_or_else instead of an if let/else
-  --> $DIR/option_if_let_else.rs:61:13
+  --> $DIR/option_if_let_else.rs:66:13
    |
 LL |       let _ = if let Some(x) = arg {
    |  _____________^
@@ -120,7 +120,7 @@ LL | |     };
    | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)`
 
 error: use Option::map_or_else instead of an if let/else
-  --> $DIR/option_if_let_else.rs:70:13
+  --> $DIR/option_if_let_else.rs:75:13
    |
 LL |       let _ = if let Some(x) = arg {
    |  _____________^
@@ -143,7 +143,7 @@ LL ~     }, |x| x * x * x * x);
    |
 
 error: use Option::map_or_else instead of an if let/else
-  --> $DIR/option_if_let_else.rs:103:13
+  --> $DIR/option_if_let_else.rs:108:13
    |
 LL | /             if let Some(idx) = s.find('.') {
 LL | |                 vec![s[..idx].to_string(), s[idx..].to_string()]
@@ -153,13 +153,13 @@ LL | |             }
    | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:127:13
+  --> $DIR/option_if_let_else.rs:132:13
    |
 LL |     let _ = if let Some(x) = optional { x + 2 } else { 5 };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:136:13
+  --> $DIR/option_if_let_else.rs:141:13
    |
 LL |       let _ = if let Some(x) = Some(0) {
    |  _____________^
@@ -181,13 +181,13 @@ LL ~         });
    |
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:164:13
+  --> $DIR/option_if_let_else.rs:169:13
    |
 LL |     let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:168:13
+  --> $DIR/option_if_let_else.rs:173:13
    |
 LL |       let _ = if let Some(x) = Some(0) {
    |  _____________^
diff --git a/src/tools/clippy/tests/ui/option_take_on_temporary.fixed b/src/tools/clippy/tests/ui/option_take_on_temporary.fixed
new file mode 100644 (file)
index 0000000..29691e8
--- /dev/null
@@ -0,0 +1,15 @@
+// run-rustfix
+
+fn main() {
+    println!("Testing non erroneous option_take_on_temporary");
+    let mut option = Some(1);
+    let _ = Box::new(move || option.take().unwrap());
+
+    println!("Testing non erroneous option_take_on_temporary");
+    let x = Some(3);
+    x.as_ref();
+
+    println!("Testing erroneous option_take_on_temporary");
+    let x = Some(3);
+    x.as_ref();
+}
index 6e0d5a87f6807b012fcc55428b5b0aee61882301..844cc4b7a09281ebaa23604078f83735625b17c7 100644 (file)
@@ -1,7 +1,7 @@
 // run-rustfix
 
 #![warn(clippy::or_then_unwrap)]
-#![allow(clippy::map_identity)]
+#![allow(clippy::map_identity, clippy::let_unit_value)]
 
 struct SomeStruct;
 impl SomeStruct {
index e406a71d46d00d377024971cee471851e0c3a063..1528ef9be964de41ba522ee5fef9930ca3a159bb 100644 (file)
@@ -1,7 +1,7 @@
 // run-rustfix
 
 #![warn(clippy::or_then_unwrap)]
-#![allow(clippy::map_identity)]
+#![allow(clippy::map_identity, clippy::let_unit_value)]
 
 struct SomeStruct;
 impl SomeStruct {
index 12a0c776ae2fd6feb90371be57be32ddd59379de..041ef17fa6834240c97e3234601e8432f3f0ff0a 100644 (file)
@@ -1,4 +1,4 @@
-#![allow(clippy::assertions_on_constants, clippy::eq_op)]
+#![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::let_unit_value)]
 #![feature(inline_const)]
 #![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)]
 
diff --git a/src/tools/clippy/tests/ui/pub_use.rs b/src/tools/clippy/tests/ui/pub_use.rs
new file mode 100644 (file)
index 0000000..65542be
--- /dev/null
@@ -0,0 +1,14 @@
+#![warn(clippy::pub_use)]
+#![allow(unused_imports)]
+#![no_main]
+
+pub mod outer {
+    mod inner {
+        pub struct Test {}
+    }
+    // should be linted
+    pub use inner::Test;
+}
+
+// should not be linted
+use std::fmt;
diff --git a/src/tools/clippy/tests/ui/pub_use.stderr b/src/tools/clippy/tests/ui/pub_use.stderr
new file mode 100644 (file)
index 0000000..9ab710d
--- /dev/null
@@ -0,0 +1,11 @@
+error: using `pub use`
+  --> $DIR/pub_use.rs:10:5
+   |
+LL |     pub use inner::Test;
+   |     ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::pub-use` implied by `-D warnings`
+   = help: move the exported item to a public module instead
+
+error: aborting due to previous error
+
index 25f2fd061b88ef0a014f078c8752796354766195..106947de68c12802c1c31162e840e970c961c53e 100644 (file)
@@ -104,4 +104,14 @@ mod m4 {
 
 pub use m4::*;
 
+mod issue_8732 {
+    #[allow(unused_macros)]
+    macro_rules! some_macro {
+        () => {};
+    }
+
+    #[allow(unused_imports)]
+    pub(crate) use some_macro; // ok: macro exports are exempt
+}
+
 fn main() {}
index 616286b4f39f4fb7f04910d92416e8970f0d7c4c..f96cfd3184384c6e09128891da36b02f88057064 100644 (file)
@@ -104,4 +104,14 @@ pub fn h() {}
 
 pub use m4::*;
 
+mod issue_8732 {
+    #[allow(unused_macros)]
+    macro_rules! some_macro {
+        () => {};
+    }
+
+    #[allow(unused_imports)]
+    pub(crate) use some_macro; // ok: macro exports are exempt
+}
+
 fn main() {}
index 24a0c812291982371e67496695f2eb1b8275abe5..9c4079ad6d306a39a07518382598878b349d39f9 100644 (file)
@@ -1,71 +1,70 @@
-//! Test for Clippy lint renames.
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
 // run-rustfix
 
-#![allow(dead_code)]
-// allow the new lint name here, to test if the new name works
-#![allow(clippy::module_name_repetitions)]
-#![allow(clippy::new_without_default)]
+#![allow(clippy::blocks_in_if_conditions)]
+#![allow(clippy::box_collection)]
 #![allow(clippy::redundant_static_lifetimes)]
 #![allow(clippy::cognitive_complexity)]
+#![allow(clippy::disallowed_methods)]
+#![allow(clippy::disallowed_types)]
+#![allow(clippy::for_loops_over_fallibles)]
+#![allow(clippy::useless_conversion)]
+#![allow(clippy::match_result_ok)]
+#![allow(clippy::new_without_default)]
 #![allow(clippy::bind_instead_of_map)]
-#![allow(clippy::box_collection)]
-#![allow(clippy::blocks_in_if_conditions)]
+#![allow(clippy::expect_used)]
 #![allow(clippy::map_unwrap_or)]
 #![allow(clippy::unwrap_used)]
-#![allow(clippy::expect_used)]
-#![allow(clippy::for_loops_over_fallibles)]
-#![allow(clippy::useless_conversion)]
-#![allow(clippy::invisible_characters)]
+#![allow(clippy::needless_borrow)]
 #![allow(clippy::single_char_add_str)]
-#![allow(clippy::match_result_ok)]
-#![allow(clippy::disallowed_types)]
-#![allow(clippy::disallowed_methods)]
+#![allow(clippy::module_name_repetitions)]
 #![allow(clippy::recursive_format_impl)]
-// uplifted lints
-#![allow(invalid_value)]
-#![allow(array_into_iter)]
-#![allow(unused_labels)]
+#![allow(clippy::invisible_characters)]
 #![allow(drop_bounds)]
-#![allow(temporary_cstring_as_ptr)]
-#![allow(non_fmt_panics)]
-#![allow(unknown_lints)]
+#![allow(array_into_iter)]
 #![allow(invalid_atomic_ordering)]
+#![allow(invalid_value)]
 #![allow(enum_intrinsics_non_enums)]
-// warn for the old lint name here, to test if the renaming worked
-#![warn(clippy::module_name_repetitions)]
-#![warn(clippy::new_without_default)]
+#![allow(non_fmt_panics)]
+#![allow(temporary_cstring_as_ptr)]
+#![allow(unknown_lints)]
+#![allow(unused_labels)]
+#![warn(clippy::blocks_in_if_conditions)]
+#![warn(clippy::blocks_in_if_conditions)]
+#![warn(clippy::box_collection)]
 #![warn(clippy::redundant_static_lifetimes)]
 #![warn(clippy::cognitive_complexity)]
+#![warn(clippy::disallowed_methods)]
+#![warn(clippy::disallowed_types)]
+#![warn(clippy::for_loops_over_fallibles)]
+#![warn(clippy::for_loops_over_fallibles)]
+#![warn(clippy::useless_conversion)]
+#![warn(clippy::match_result_ok)]
+#![warn(clippy::new_without_default)]
 #![warn(clippy::bind_instead_of_map)]
-#![warn(clippy::box_collection)]
-#![warn(clippy::blocks_in_if_conditions)]
-#![warn(clippy::blocks_in_if_conditions)]
-#![warn(clippy::map_unwrap_or)]
+#![warn(clippy::expect_used)]
 #![warn(clippy::map_unwrap_or)]
 #![warn(clippy::map_unwrap_or)]
 #![warn(clippy::unwrap_used)]
-#![warn(clippy::unwrap_used)]
-#![warn(clippy::expect_used)]
+#![warn(clippy::needless_borrow)]
 #![warn(clippy::expect_used)]
-#![warn(clippy::for_loops_over_fallibles)]
-#![warn(clippy::for_loops_over_fallibles)]
-#![warn(clippy::useless_conversion)]
-#![warn(clippy::invisible_characters)]
+#![warn(clippy::map_unwrap_or)]
+#![warn(clippy::unwrap_used)]
 #![warn(clippy::single_char_add_str)]
-#![warn(clippy::match_result_ok)]
-#![warn(clippy::disallowed_types)]
-#![warn(clippy::disallowed_methods)]
-#![warn(clippy::needless_borrow)]
+#![warn(clippy::module_name_repetitions)]
 #![warn(clippy::recursive_format_impl)]
-// uplifted lints
-#![warn(invalid_value)]
-#![warn(array_into_iter)]
-#![warn(unused_labels)]
+#![warn(clippy::invisible_characters)]
 #![warn(drop_bounds)]
-#![warn(temporary_cstring_as_ptr)]
-#![warn(non_fmt_panics)]
-#![warn(unknown_lints)]
+#![warn(array_into_iter)]
 #![warn(invalid_atomic_ordering)]
+#![warn(invalid_value)]
 #![warn(enum_intrinsics_non_enums)]
+#![warn(non_fmt_panics)]
+#![warn(temporary_cstring_as_ptr)]
+#![warn(unknown_lints)]
+#![warn(unused_labels)]
 
 fn main() {}
index ea64234c680d37f5ef8b7951100723a5a33a3054..e83e66b7fbd4211dbfc80dd2c903a5fcb807ca94 100644 (file)
@@ -1,71 +1,70 @@
-//! Test for Clippy lint renames.
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
 // run-rustfix
 
-#![allow(dead_code)]
-// allow the new lint name here, to test if the new name works
-#![allow(clippy::module_name_repetitions)]
-#![allow(clippy::new_without_default)]
+#![allow(clippy::blocks_in_if_conditions)]
+#![allow(clippy::box_collection)]
 #![allow(clippy::redundant_static_lifetimes)]
 #![allow(clippy::cognitive_complexity)]
+#![allow(clippy::disallowed_methods)]
+#![allow(clippy::disallowed_types)]
+#![allow(clippy::for_loops_over_fallibles)]
+#![allow(clippy::useless_conversion)]
+#![allow(clippy::match_result_ok)]
+#![allow(clippy::new_without_default)]
 #![allow(clippy::bind_instead_of_map)]
-#![allow(clippy::box_collection)]
-#![allow(clippy::blocks_in_if_conditions)]
+#![allow(clippy::expect_used)]
 #![allow(clippy::map_unwrap_or)]
 #![allow(clippy::unwrap_used)]
-#![allow(clippy::expect_used)]
-#![allow(clippy::for_loops_over_fallibles)]
-#![allow(clippy::useless_conversion)]
-#![allow(clippy::invisible_characters)]
+#![allow(clippy::needless_borrow)]
 #![allow(clippy::single_char_add_str)]
-#![allow(clippy::match_result_ok)]
-#![allow(clippy::disallowed_types)]
-#![allow(clippy::disallowed_methods)]
+#![allow(clippy::module_name_repetitions)]
 #![allow(clippy::recursive_format_impl)]
-// uplifted lints
-#![allow(invalid_value)]
-#![allow(array_into_iter)]
-#![allow(unused_labels)]
+#![allow(clippy::invisible_characters)]
 #![allow(drop_bounds)]
-#![allow(temporary_cstring_as_ptr)]
-#![allow(non_fmt_panics)]
-#![allow(unknown_lints)]
+#![allow(array_into_iter)]
 #![allow(invalid_atomic_ordering)]
+#![allow(invalid_value)]
 #![allow(enum_intrinsics_non_enums)]
-// warn for the old lint name here, to test if the renaming worked
-#![warn(clippy::stutter)]
-#![warn(clippy::new_without_default_derive)]
+#![allow(non_fmt_panics)]
+#![allow(temporary_cstring_as_ptr)]
+#![allow(unknown_lints)]
+#![allow(unused_labels)]
+#![warn(clippy::block_in_if_condition_expr)]
+#![warn(clippy::block_in_if_condition_stmt)]
+#![warn(clippy::box_vec)]
 #![warn(clippy::const_static_lifetime)]
 #![warn(clippy::cyclomatic_complexity)]
+#![warn(clippy::disallowed_method)]
+#![warn(clippy::disallowed_type)]
+#![warn(clippy::for_loop_over_option)]
+#![warn(clippy::for_loop_over_result)]
+#![warn(clippy::identity_conversion)]
+#![warn(clippy::if_let_some_result)]
+#![warn(clippy::new_without_default_derive)]
 #![warn(clippy::option_and_then_some)]
-#![warn(clippy::box_vec)]
-#![warn(clippy::block_in_if_condition_expr)]
-#![warn(clippy::block_in_if_condition_stmt)]
+#![warn(clippy::option_expect_used)]
 #![warn(clippy::option_map_unwrap_or)]
 #![warn(clippy::option_map_unwrap_or_else)]
-#![warn(clippy::result_map_unwrap_or_else)]
 #![warn(clippy::option_unwrap_used)]
-#![warn(clippy::result_unwrap_used)]
-#![warn(clippy::option_expect_used)]
+#![warn(clippy::ref_in_deref)]
 #![warn(clippy::result_expect_used)]
-#![warn(clippy::for_loop_over_option)]
-#![warn(clippy::for_loop_over_result)]
-#![warn(clippy::identity_conversion)]
-#![warn(clippy::zero_width_space)]
+#![warn(clippy::result_map_unwrap_or_else)]
+#![warn(clippy::result_unwrap_used)]
 #![warn(clippy::single_char_push_str)]
-#![warn(clippy::if_let_some_result)]
-#![warn(clippy::disallowed_type)]
-#![warn(clippy::disallowed_method)]
-#![warn(clippy::ref_in_deref)]
+#![warn(clippy::stutter)]
 #![warn(clippy::to_string_in_display)]
-// uplifted lints
-#![warn(clippy::invalid_ref)]
-#![warn(clippy::into_iter_on_array)]
-#![warn(clippy::unused_label)]
+#![warn(clippy::zero_width_space)]
 #![warn(clippy::drop_bounds)]
-#![warn(clippy::temporary_cstring_as_ptr)]
-#![warn(clippy::panic_params)]
-#![warn(clippy::unknown_clippy_lints)]
+#![warn(clippy::into_iter_on_array)]
 #![warn(clippy::invalid_atomic_ordering)]
+#![warn(clippy::invalid_ref)]
 #![warn(clippy::mem_discriminant_non_enum)]
+#![warn(clippy::panic_params)]
+#![warn(clippy::temporary_cstring_as_ptr)]
+#![warn(clippy::unknown_clippy_lints)]
+#![warn(clippy::unused_label)]
 
 fn main() {}
index 8b132a7838470cbd90aba6f1e1c36be73274e8bc..f811b10d017104220a46575d3aa7d4af4ce9f827 100644 (file)
@@ -1,82 +1,82 @@
-error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
+error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
   --> $DIR/rename.rs:35:9
    |
-LL | #![warn(clippy::stutter)]
-   |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
+LL | #![warn(clippy::block_in_if_condition_expr)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
    |
    = note: `-D renamed-and-removed-lints` implied by `-D warnings`
 
-error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
+error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
   --> $DIR/rename.rs:36:9
    |
-LL | #![warn(clippy::new_without_default_derive)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
+LL | #![warn(clippy::block_in_if_condition_stmt)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
 
-error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
+error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
   --> $DIR/rename.rs:37:9
    |
+LL | #![warn(clippy::box_vec)]
+   |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
+
+error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
+  --> $DIR/rename.rs:38:9
+   |
 LL | #![warn(clippy::const_static_lifetime)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
 
 error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
-  --> $DIR/rename.rs:38:9
+  --> $DIR/rename.rs:39:9
    |
 LL | #![warn(clippy::cyclomatic_complexity)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
 
-error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
-  --> $DIR/rename.rs:39:9
-   |
-LL | #![warn(clippy::option_and_then_some)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
-
-error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
+error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
   --> $DIR/rename.rs:40:9
    |
-LL | #![warn(clippy::box_vec)]
-   |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
+LL | #![warn(clippy::disallowed_method)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
 
-error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
+error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
   --> $DIR/rename.rs:41:9
    |
-LL | #![warn(clippy::block_in_if_condition_expr)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
+LL | #![warn(clippy::disallowed_type)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
 
-error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
+error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles`
   --> $DIR/rename.rs:42:9
    |
-LL | #![warn(clippy::block_in_if_condition_stmt)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
+LL | #![warn(clippy::for_loop_over_option)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
 
-error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
+error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles`
   --> $DIR/rename.rs:43:9
    |
-LL | #![warn(clippy::option_map_unwrap_or)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
+LL | #![warn(clippy::for_loop_over_result)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
 
-error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
+error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
   --> $DIR/rename.rs:44:9
    |
-LL | #![warn(clippy::option_map_unwrap_or_else)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
+LL | #![warn(clippy::identity_conversion)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
 
-error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
+error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
   --> $DIR/rename.rs:45:9
    |
-LL | #![warn(clippy::result_map_unwrap_or_else)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
+LL | #![warn(clippy::if_let_some_result)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
 
-error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
+error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
   --> $DIR/rename.rs:46:9
    |
-LL | #![warn(clippy::option_unwrap_used)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
+LL | #![warn(clippy::new_without_default_derive)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
 
-error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
+error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
   --> $DIR/rename.rs:47:9
    |
-LL | #![warn(clippy::result_unwrap_used)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
+LL | #![warn(clippy::option_and_then_some)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
 
 error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
   --> $DIR/rename.rs:48:9
@@ -84,107 +84,113 @@ error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_use
 LL | #![warn(clippy::option_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
-error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
+error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
   --> $DIR/rename.rs:49:9
    |
-LL | #![warn(clippy::result_expect_used)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
+LL | #![warn(clippy::option_map_unwrap_or)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
-error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles`
+error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
   --> $DIR/rename.rs:50:9
    |
-LL | #![warn(clippy::for_loop_over_option)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
+LL | #![warn(clippy::option_map_unwrap_or_else)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
-error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles`
+error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
   --> $DIR/rename.rs:51:9
    |
-LL | #![warn(clippy::for_loop_over_result)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
+LL | #![warn(clippy::option_unwrap_used)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
-error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
+error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
   --> $DIR/rename.rs:52:9
    |
-LL | #![warn(clippy::identity_conversion)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
+LL | #![warn(clippy::ref_in_deref)]
+   |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
 
-error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
+error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
   --> $DIR/rename.rs:53:9
    |
-LL | #![warn(clippy::zero_width_space)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
+LL | #![warn(clippy::result_expect_used)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
-error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
+error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
   --> $DIR/rename.rs:54:9
    |
-LL | #![warn(clippy::single_char_push_str)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
+LL | #![warn(clippy::result_map_unwrap_or_else)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
-error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
+error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
   --> $DIR/rename.rs:55:9
    |
-LL | #![warn(clippy::if_let_some_result)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
+LL | #![warn(clippy::result_unwrap_used)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
-error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
+error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
   --> $DIR/rename.rs:56:9
    |
-LL | #![warn(clippy::disallowed_type)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
+LL | #![warn(clippy::single_char_push_str)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
 
-error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
+error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
   --> $DIR/rename.rs:57:9
    |
-LL | #![warn(clippy::disallowed_method)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
+LL | #![warn(clippy::stutter)]
+   |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
 
-error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
+error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
   --> $DIR/rename.rs:58:9
    |
-LL | #![warn(clippy::ref_in_deref)]
-   |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
+LL | #![warn(clippy::to_string_in_display)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
 
-error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
+error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
   --> $DIR/rename.rs:59:9
    |
-LL | #![warn(clippy::to_string_in_display)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
+LL | #![warn(clippy::zero_width_space)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
 
-error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
-  --> $DIR/rename.rs:61:9
+error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
+  --> $DIR/rename.rs:60:9
    |
-LL | #![warn(clippy::invalid_ref)]
-   |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
+LL | #![warn(clippy::drop_bounds)]
+   |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
 
 error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
-  --> $DIR/rename.rs:62:9
+  --> $DIR/rename.rs:61:9
    |
 LL | #![warn(clippy::into_iter_on_array)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
 
-error: lint `clippy::unused_label` has been renamed to `unused_labels`
+error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
+  --> $DIR/rename.rs:62:9
+   |
+LL | #![warn(clippy::invalid_atomic_ordering)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
+
+error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
   --> $DIR/rename.rs:63:9
    |
-LL | #![warn(clippy::unused_label)]
-   |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
+LL | #![warn(clippy::invalid_ref)]
+   |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
 
-error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
+error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
   --> $DIR/rename.rs:64:9
    |
-LL | #![warn(clippy::drop_bounds)]
-   |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
+LL | #![warn(clippy::mem_discriminant_non_enum)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
 
-error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
+error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
   --> $DIR/rename.rs:65:9
    |
-LL | #![warn(clippy::temporary_cstring_as_ptr)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
+LL | #![warn(clippy::panic_params)]
+   |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
 
-error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
+error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
   --> $DIR/rename.rs:66:9
    |
-LL | #![warn(clippy::panic_params)]
-   |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
+LL | #![warn(clippy::temporary_cstring_as_ptr)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
 
 error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
   --> $DIR/rename.rs:67:9
@@ -192,17 +198,11 @@ error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
 LL | #![warn(clippy::unknown_clippy_lints)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
 
-error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
+error: lint `clippy::unused_label` has been renamed to `unused_labels`
   --> $DIR/rename.rs:68:9
    |
-LL | #![warn(clippy::invalid_atomic_ordering)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
-
-error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
-  --> $DIR/rename.rs:69:9
-   |
-LL | #![warn(clippy::mem_discriminant_non_enum)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
+LL | #![warn(clippy::unused_label)]
+   |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
 
 error: aborting due to 34 previous errors
 
index 38fc9969804fa593e46df30c22ed61ca3072dce6..086331af6b5673fed75858630f90677e24f5e22c 100644 (file)
@@ -39,4 +39,19 @@ fn main() {
 
     // No lint
     foo!(a_struct);
+
+    #[non_exhaustive]
+    struct B {
+        a: u32,
+        b: u32,
+        c: u64,
+    }
+
+    let b_struct = B { a: 5, b: 42, c: 342 };
+
+    match b_struct {
+        B { a: 5, b: 42, .. } => {},
+        B { a: 0, b: 0, c: 128, .. } => {}, // No Lint
+        _ => {},
+    }
 }
index 7f28f0257904578f9d42b7900eda8df4bc38e530..3d2295912c9fd08827596a2cef226b76f58e17b0 100644 (file)
@@ -1,3 +1,5 @@
+#![feature(adt_const_params)]
+#![allow(incomplete_features)]
 #![warn(clippy::same_functions_in_if_condition)]
 #![allow(clippy::ifs_same_cond)] // This warning is different from `ifs_same_cond`.
 #![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks
@@ -87,4 +89,21 @@ fn main() {
         "linux"
     };
     println!("{}", os);
+
+    #[derive(PartialEq, Eq)]
+    enum E {
+        A,
+        B,
+    }
+    fn generic<const P: E>() -> bool {
+        match P {
+            E::A => true,
+            E::B => false,
+        }
+    }
+    if generic::<{ E::A }>() {
+        println!("A");
+    } else if generic::<{ E::B }>() {
+        println!("B");
+    }
 }
index 363a03846d236e72ce5b2cd3296e16bb4d7aaf02..71e82910ef7529e6cbef6c67426b334301941ba0 100644 (file)
@@ -1,72 +1,72 @@
 error: this `if` has the same function call as a previous `if`
-  --> $DIR/same_functions_in_if_condition.rs:29:15
+  --> $DIR/same_functions_in_if_condition.rs:31:15
    |
 LL |     } else if function() {
    |               ^^^^^^^^^^
    |
    = note: `-D clippy::same-functions-in-if-condition` implied by `-D warnings`
 note: same as this
-  --> $DIR/same_functions_in_if_condition.rs:28:8
+  --> $DIR/same_functions_in_if_condition.rs:30:8
    |
 LL |     if function() {
    |        ^^^^^^^^^^
 
 error: this `if` has the same function call as a previous `if`
-  --> $DIR/same_functions_in_if_condition.rs:34:15
+  --> $DIR/same_functions_in_if_condition.rs:36:15
    |
 LL |     } else if fn_arg(a) {
    |               ^^^^^^^^^
    |
 note: same as this
-  --> $DIR/same_functions_in_if_condition.rs:33:8
+  --> $DIR/same_functions_in_if_condition.rs:35:8
    |
 LL |     if fn_arg(a) {
    |        ^^^^^^^^^
 
 error: this `if` has the same function call as a previous `if`
-  --> $DIR/same_functions_in_if_condition.rs:39:15
+  --> $DIR/same_functions_in_if_condition.rs:41:15
    |
 LL |     } else if obj.method() {
    |               ^^^^^^^^^^^^
    |
 note: same as this
-  --> $DIR/same_functions_in_if_condition.rs:38:8
+  --> $DIR/same_functions_in_if_condition.rs:40:8
    |
 LL |     if obj.method() {
    |        ^^^^^^^^^^^^
 
 error: this `if` has the same function call as a previous `if`
-  --> $DIR/same_functions_in_if_condition.rs:44:15
+  --> $DIR/same_functions_in_if_condition.rs:46:15
    |
 LL |     } else if obj.method_arg(a) {
    |               ^^^^^^^^^^^^^^^^^
    |
 note: same as this
-  --> $DIR/same_functions_in_if_condition.rs:43:8
+  --> $DIR/same_functions_in_if_condition.rs:45:8
    |
 LL |     if obj.method_arg(a) {
    |        ^^^^^^^^^^^^^^^^^
 
 error: this `if` has the same function call as a previous `if`
-  --> $DIR/same_functions_in_if_condition.rs:51:15
+  --> $DIR/same_functions_in_if_condition.rs:53:15
    |
 LL |     } else if v.pop() == None {
    |               ^^^^^^^^^^^^^^^
    |
 note: same as this
-  --> $DIR/same_functions_in_if_condition.rs:49:8
+  --> $DIR/same_functions_in_if_condition.rs:51:8
    |
 LL |     if v.pop() == None {
    |        ^^^^^^^^^^^^^^^
 
 error: this `if` has the same function call as a previous `if`
-  --> $DIR/same_functions_in_if_condition.rs:56:15
+  --> $DIR/same_functions_in_if_condition.rs:58:15
    |
 LL |     } else if v.len() == 42 {
    |               ^^^^^^^^^^^^^
    |
 note: same as this
-  --> $DIR/same_functions_in_if_condition.rs:54:8
+  --> $DIR/same_functions_in_if_condition.rs:56:8
    |
 LL |     if v.len() == 42 {
    |        ^^^^^^^^^^^^^
index 0321f8c4cdf84ba14f107536ba7019033d09d851..a394ef8f25c675b529db3c275eb97308fe835b5b 100644 (file)
@@ -1,4 +1,5 @@
 #![warn(clippy::shadow_same, clippy::shadow_reuse, clippy::shadow_unrelated)]
+#![allow(clippy::let_unit_value)]
 
 fn shadow_same() {
     let x = 1;
index f8b9221d555871d7f6fbd4378857a619da51f41f..3bd41d0626049da49e07ce5c0f50ce10c6f2c0d4 100644 (file)
 error: `x` is shadowed by itself in `x`
-  --> $DIR/shadow.rs:5:9
+  --> $DIR/shadow.rs:6:9
    |
 LL |     let x = x;
    |         ^
    |
    = note: `-D clippy::shadow-same` implied by `-D warnings`
 note: previous binding is here
-  --> $DIR/shadow.rs:4:9
+  --> $DIR/shadow.rs:5:9
    |
 LL |     let x = 1;
    |         ^
 
 error: `mut x` is shadowed by itself in `&x`
-  --> $DIR/shadow.rs:6:13
+  --> $DIR/shadow.rs:7:13
    |
 LL |     let mut x = &x;
    |             ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:5:9
+  --> $DIR/shadow.rs:6:9
    |
 LL |     let x = x;
    |         ^
 
 error: `x` is shadowed by itself in `&mut x`
-  --> $DIR/shadow.rs:7:9
+  --> $DIR/shadow.rs:8:9
    |
 LL |     let x = &mut x;
    |         ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:6:9
+  --> $DIR/shadow.rs:7:9
    |
 LL |     let mut x = &x;
    |         ^^^^^
 
 error: `x` is shadowed by itself in `*x`
-  --> $DIR/shadow.rs:8:9
+  --> $DIR/shadow.rs:9:9
    |
 LL |     let x = *x;
    |         ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:7:9
+  --> $DIR/shadow.rs:8:9
    |
 LL |     let x = &mut x;
    |         ^
 
 error: `x` is shadowed
-  --> $DIR/shadow.rs:13:9
+  --> $DIR/shadow.rs:14:9
    |
 LL |     let x = x.0;
    |         ^
    |
    = note: `-D clippy::shadow-reuse` implied by `-D warnings`
 note: previous binding is here
-  --> $DIR/shadow.rs:12:9
+  --> $DIR/shadow.rs:13:9
    |
 LL |     let x = ([[0]], ());
    |         ^
 
 error: `x` is shadowed
-  --> $DIR/shadow.rs:14:9
+  --> $DIR/shadow.rs:15:9
    |
 LL |     let x = x[0];
    |         ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:13:9
+  --> $DIR/shadow.rs:14:9
    |
 LL |     let x = x.0;
    |         ^
 
 error: `x` is shadowed
-  --> $DIR/shadow.rs:15:10
+  --> $DIR/shadow.rs:16:10
    |
 LL |     let [x] = x;
    |          ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:14:9
+  --> $DIR/shadow.rs:15:9
    |
 LL |     let x = x[0];
    |         ^
 
 error: `x` is shadowed
-  --> $DIR/shadow.rs:16:9
+  --> $DIR/shadow.rs:17:9
    |
 LL |     let x = Some(x);
    |         ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:15:10
+  --> $DIR/shadow.rs:16:10
    |
 LL |     let [x] = x;
    |          ^
 
 error: `x` is shadowed
-  --> $DIR/shadow.rs:17:9
+  --> $DIR/shadow.rs:18:9
    |
 LL |     let x = foo(x);
    |         ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:16:9
+  --> $DIR/shadow.rs:17:9
    |
 LL |     let x = Some(x);
    |         ^
 
 error: `x` is shadowed
-  --> $DIR/shadow.rs:18:9
+  --> $DIR/shadow.rs:19:9
    |
 LL |     let x = || x;
    |         ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:17:9
+  --> $DIR/shadow.rs:18:9
    |
 LL |     let x = foo(x);
    |         ^
 
 error: `x` is shadowed
-  --> $DIR/shadow.rs:19:9
+  --> $DIR/shadow.rs:20:9
    |
 LL |     let x = Some(1).map(|_| x)?;
    |         ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:18:9
+  --> $DIR/shadow.rs:19:9
    |
 LL |     let x = || x;
    |         ^
 
 error: `y` is shadowed
-  --> $DIR/shadow.rs:21:9
+  --> $DIR/shadow.rs:22:9
    |
 LL |     let y = match y {
    |         ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:20:9
+  --> $DIR/shadow.rs:21:9
    |
 LL |     let y = 1;
    |         ^
 
 error: `x` shadows a previous, unrelated binding
-  --> $DIR/shadow.rs:30:9
+  --> $DIR/shadow.rs:31:9
    |
 LL |     let x = 2;
    |         ^
    |
    = note: `-D clippy::shadow-unrelated` implied by `-D warnings`
 note: previous binding is here
-  --> $DIR/shadow.rs:29:9
+  --> $DIR/shadow.rs:30:9
    |
 LL |     let x = 1;
    |         ^
 
 error: `x` shadows a previous, unrelated binding
-  --> $DIR/shadow.rs:35:13
+  --> $DIR/shadow.rs:36:13
    |
 LL |         let x = 1;
    |             ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:34:10
+  --> $DIR/shadow.rs:35:10
    |
 LL |     fn f(x: u32) {
    |          ^
 
 error: `x` shadows a previous, unrelated binding
-  --> $DIR/shadow.rs:40:14
+  --> $DIR/shadow.rs:41:14
    |
 LL |         Some(x) => {
    |              ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:37:9
+  --> $DIR/shadow.rs:38:9
    |
 LL |     let x = 1;
    |         ^
 
 error: `x` shadows a previous, unrelated binding
-  --> $DIR/shadow.rs:41:17
+  --> $DIR/shadow.rs:42:17
    |
 LL |             let x = 1;
    |                 ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:40:14
+  --> $DIR/shadow.rs:41:14
    |
 LL |         Some(x) => {
    |              ^
 
 error: `x` shadows a previous, unrelated binding
-  --> $DIR/shadow.rs:45:17
+  --> $DIR/shadow.rs:46:17
    |
 LL |     if let Some(x) = Some(1) {}
    |                 ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:37:9
+  --> $DIR/shadow.rs:38:9
    |
 LL |     let x = 1;
    |         ^
 
 error: `x` shadows a previous, unrelated binding
-  --> $DIR/shadow.rs:46:20
+  --> $DIR/shadow.rs:47:20
    |
 LL |     while let Some(x) = Some(1) {}
    |                    ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:37:9
+  --> $DIR/shadow.rs:38:9
    |
 LL |     let x = 1;
    |         ^
 
 error: `x` shadows a previous, unrelated binding
-  --> $DIR/shadow.rs:47:15
+  --> $DIR/shadow.rs:48:15
    |
 LL |     let _ = |[x]: [u32; 1]| {
    |               ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:37:9
+  --> $DIR/shadow.rs:38:9
    |
 LL |     let x = 1;
    |         ^
 
 error: `x` shadows a previous, unrelated binding
-  --> $DIR/shadow.rs:48:13
+  --> $DIR/shadow.rs:49:13
    |
 LL |         let x = 1;
    |             ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:47:15
+  --> $DIR/shadow.rs:48:15
    |
 LL |     let _ = |[x]: [u32; 1]| {
    |               ^
 
 error: `y` is shadowed
-  --> $DIR/shadow.rs:51:17
+  --> $DIR/shadow.rs:52:17
    |
 LL |     if let Some(y) = y {}
    |                 ^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:50:9
+  --> $DIR/shadow.rs:51:9
    |
 LL |     let y = Some(1);
    |         ^
 
 error: `_b` shadows a previous, unrelated binding
-  --> $DIR/shadow.rs:87:9
+  --> $DIR/shadow.rs:88:9
    |
 LL |     let _b = _a;
    |         ^^
    |
 note: previous binding is here
-  --> $DIR/shadow.rs:86:28
+  --> $DIR/shadow.rs:87:28
    |
 LL | pub async fn foo2(_a: i32, _b: i64) {
    |                            ^^
index 76f6ce9ee6b47f3826824a35e2495c0513d80c23..c21225d153bd6cf7d928592030e8e44a25cb1cc2 100644 (file)
@@ -3,7 +3,8 @@
     unused,
     clippy::println_empty_string,
     clippy::empty_loop,
-    clippy::diverging_sub_expression
+    clippy::diverging_sub_expression,
+    clippy::let_unit_value
 )]
 
 struct Foo {
index faf572b0c6bc27010c8af554b37cd863e295ab2a..6e77269389737946b636198d686918ec9e8b2f5a 100644 (file)
@@ -1,84 +1,84 @@
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:20:9
+  --> $DIR/similar_names.rs:21:9
    |
 LL |     let bpple: i32;
    |         ^^^^^
    |
    = note: `-D clippy::similar-names` implied by `-D warnings`
 note: existing binding defined here
-  --> $DIR/similar_names.rs:18:9
+  --> $DIR/similar_names.rs:19:9
    |
 LL |     let apple: i32;
    |         ^^^^^
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:22:9
+  --> $DIR/similar_names.rs:23:9
    |
 LL |     let cpple: i32;
    |         ^^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:18:9
+  --> $DIR/similar_names.rs:19:9
    |
 LL |     let apple: i32;
    |         ^^^^^
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:46:9
+  --> $DIR/similar_names.rs:47:9
    |
 LL |     let bluby: i32;
    |         ^^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:45:9
+  --> $DIR/similar_names.rs:46:9
    |
 LL |     let blubx: i32;
    |         ^^^^^
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:50:9
+  --> $DIR/similar_names.rs:51:9
    |
 LL |     let coke: i32;
    |         ^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:48:9
+  --> $DIR/similar_names.rs:49:9
    |
 LL |     let cake: i32;
    |         ^^^^
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:68:9
+  --> $DIR/similar_names.rs:69:9
    |
 LL |     let xyzeabc: i32;
    |         ^^^^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:66:9
+  --> $DIR/similar_names.rs:67:9
    |
 LL |     let xyz1abc: i32;
    |         ^^^^^^^
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:72:9
+  --> $DIR/similar_names.rs:73:9
    |
 LL |     let parsee: i32;
    |         ^^^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:70:9
+  --> $DIR/similar_names.rs:71:9
    |
 LL |     let parser: i32;
    |         ^^^^^^
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:93:16
+  --> $DIR/similar_names.rs:94:16
    |
 LL |         bpple: sprang,
    |                ^^^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:92:16
+  --> $DIR/similar_names.rs:93:16
    |
 LL |         apple: spring,
    |                ^^^^^^
index 261d8bc7260ca5deab6ab1519d4d9387eebaac87..69c5b236f7cf8dea4d221d6c444cc7fcb12e058e 100644 (file)
@@ -1,4 +1,5 @@
 #![warn(clippy::single_char_lifetime_names)]
+#![allow(clippy::let_unit_value)]
 
 // Lifetimes should only be linted when they're introduced
 struct DiagnosticCtx<'a, 'b>
index 013b64f46a8ce1d2795a288c6864a8603dec0c6b..1438b3999dba85ad1b4ddc810854958fb0296dfb 100644 (file)
@@ -1,5 +1,5 @@
 error: single-character lifetime names are likely uninformative
-  --> $DIR/single_char_lifetime_names.rs:4:22
+  --> $DIR/single_char_lifetime_names.rs:5:22
    |
 LL | struct DiagnosticCtx<'a, 'b>
    |                      ^^
@@ -8,7 +8,7 @@ LL | struct DiagnosticCtx<'a, 'b>
    = help: use a more informative name
 
 error: single-character lifetime names are likely uninformative
-  --> $DIR/single_char_lifetime_names.rs:4:26
+  --> $DIR/single_char_lifetime_names.rs:5:26
    |
 LL | struct DiagnosticCtx<'a, 'b>
    |                          ^^
@@ -16,7 +16,7 @@ LL | struct DiagnosticCtx<'a, 'b>
    = help: use a more informative name
 
 error: single-character lifetime names are likely uninformative
-  --> $DIR/single_char_lifetime_names.rs:13:6
+  --> $DIR/single_char_lifetime_names.rs:14:6
    |
 LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> {
    |      ^^
@@ -24,7 +24,7 @@ LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> {
    = help: use a more informative name
 
 error: single-character lifetime names are likely uninformative
-  --> $DIR/single_char_lifetime_names.rs:13:10
+  --> $DIR/single_char_lifetime_names.rs:14:10
    |
 LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> {
    |          ^^
@@ -32,7 +32,7 @@ LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> {
    = help: use a more informative name
 
 error: single-character lifetime names are likely uninformative
-  --> $DIR/single_char_lifetime_names.rs:33:15
+  --> $DIR/single_char_lifetime_names.rs:34:15
    |
 LL | fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) {
    |               ^^
index b624a41a29b2da10375b212d321afbabac572c6a..82387f3d80b7780a0781dd116dfbbd341d47a3b8 100644 (file)
@@ -1,7 +1,12 @@
+// aux-build: proc_macro_with_span.rs
+
 #![warn(clippy::single_match_else)]
 #![allow(clippy::needless_return)]
 #![allow(clippy::no_effect)]
 
+extern crate proc_macro_with_span;
+use proc_macro_with_span::with_span;
+
 enum ExprNode {
     ExprAddrOf,
     Butterflies,
@@ -11,13 +16,22 @@ enum ExprNode {
 static NODE: ExprNode = ExprNode::Unicorns;
 
 fn unwrap_addr() -> Option<&'static ExprNode> {
-    match ExprNode::Butterflies {
+    let _ = match ExprNode::Butterflies {
         ExprNode::ExprAddrOf => Some(&NODE),
         _ => {
             let x = 5;
             None
         },
-    }
+    };
+
+    // Don't lint
+    with_span!(span match ExprNode::Butterflies {
+        ExprNode::ExprAddrOf => Some(&NODE),
+        _ => {
+            let x = 5;
+            None
+        },
+    })
 }
 
 macro_rules! unwrap_addr {
index 21ea704b62ab5c9134fcccdcda1d853a27af1c42..7756c6f204e67eb016ecf792a3d6cfb7b70efc66 100644 (file)
@@ -1,22 +1,23 @@
 error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
-  --> $DIR/single_match_else.rs:14:5
+  --> $DIR/single_match_else.rs:19:13
    |
-LL | /     match ExprNode::Butterflies {
+LL |       let _ = match ExprNode::Butterflies {
+   |  _____________^
 LL | |         ExprNode::ExprAddrOf => Some(&NODE),
 LL | |         _ => {
 LL | |             let x = 5;
 LL | |             None
 LL | |         },
-LL | |     }
+LL | |     };
    | |_____^
    |
    = note: `-D clippy::single-match-else` implied by `-D warnings`
 help: try this
    |
-LL ~     if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else {
+LL ~     let _ = if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else {
 LL +         let x = 5;
 LL +         None
-LL +     }
+LL ~     };
    |
 
 error: aborting due to previous error
index b8d22ed250467b533789ed47155142fcc5ec2d00..c35e0c22ae8994a4cced07921e44d0b281f306c6 100644 (file)
@@ -5,7 +5,7 @@ LL |     vec.sort();
    |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
    |
    = note: `-D clippy::stable-sort-primitive` implied by `-D warnings`
-   = note: an unstable sort would perform faster without any observable difference for this data type
+   = note: an unstable sort typically performs faster without any observable difference for this data type
 
 error: used `sort` on primitive type `bool`
   --> $DIR/stable_sort_primitive.rs:9:5
@@ -13,7 +13,7 @@ error: used `sort` on primitive type `bool`
 LL |     vec.sort();
    |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
    |
-   = note: an unstable sort would perform faster without any observable difference for this data type
+   = note: an unstable sort typically performs faster without any observable difference for this data type
 
 error: used `sort` on primitive type `char`
   --> $DIR/stable_sort_primitive.rs:11:5
@@ -21,7 +21,7 @@ error: used `sort` on primitive type `char`
 LL |     vec.sort();
    |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
    |
-   = note: an unstable sort would perform faster without any observable difference for this data type
+   = note: an unstable sort typically performs faster without any observable difference for this data type
 
 error: used `sort` on primitive type `str`
   --> $DIR/stable_sort_primitive.rs:13:5
@@ -29,7 +29,7 @@ error: used `sort` on primitive type `str`
 LL |     vec.sort();
    |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
    |
-   = note: an unstable sort would perform faster without any observable difference for this data type
+   = note: an unstable sort typically performs faster without any observable difference for this data type
 
 error: used `sort` on primitive type `tuple`
   --> $DIR/stable_sort_primitive.rs:15:5
@@ -37,7 +37,7 @@ error: used `sort` on primitive type `tuple`
 LL |     vec.sort();
    |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
    |
-   = note: an unstable sort would perform faster without any observable difference for this data type
+   = note: an unstable sort typically performs faster without any observable difference for this data type
 
 error: used `sort` on primitive type `array`
   --> $DIR/stable_sort_primitive.rs:17:5
@@ -45,7 +45,7 @@ error: used `sort` on primitive type `array`
 LL |     vec.sort();
    |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
    |
-   = note: an unstable sort would perform faster without any observable difference for this data type
+   = note: an unstable sort typically performs faster without any observable difference for this data type
 
 error: used `sort` on primitive type `i32`
   --> $DIR/stable_sort_primitive.rs:19:5
@@ -53,7 +53,7 @@ error: used `sort` on primitive type `i32`
 LL |     arr.sort();
    |     ^^^^^^^^^^ help: try: `arr.sort_unstable()`
    |
-   = note: an unstable sort would perform faster without any observable difference for this data type
+   = note: an unstable sort typically performs faster without any observable difference for this data type
 
 error: aborting due to 7 previous errors
 
index fcd827a91c7f62d843bfd47a30016a58e9dbbf2d..21753e5dc6a47c0546f43f6210392062821d9c9a 100644 (file)
@@ -1,7 +1,7 @@
 // aux-build:proc_macro_suspicious_else_formatting.rs
 
 #![warn(clippy::suspicious_else_formatting)]
-#![allow(clippy::if_same_then_else)]
+#![allow(clippy::if_same_then_else, clippy::let_unit_value)]
 
 extern crate proc_macro_suspicious_else_formatting;
 use proc_macro_suspicious_else_formatting::DeriveBadSpan;
index 19184df0becb579fc6a505bf5924fc089aeb52e9..3c5e9642714657676c3a950ca78c4ac51af3e687 100644 (file)
@@ -6,6 +6,6 @@ fn main() {
     let c = 'x';
     let d = &c;
 
-    let _ = d.is_digit(10);
-    let _ = char::is_digit(c, 10);
+    let _ = d.is_digit(8);
+    let _ = char::is_digit(c, 8);
 }
index 45a6728ebf57812f6facc06f55a104780bc4972b..4f247c06ceedaba747c89d5ff4b602e55c5653cb 100644 (file)
@@ -6,6 +6,6 @@ fn main() {
     let c = 'x';
     let d = &c;
 
-    let _ = d.to_digit(10).is_some();
-    let _ = char::to_digit(c, 10).is_some();
+    let _ = d.to_digit(8).is_some();
+    let _ = char::to_digit(c, 8).is_some();
 }
index 177d3ccd3e23d7a4f507df4dc3dd053161ee1d8d..10a1b393a3906fced44d5ec028e9e9666282312c 100644 (file)
@@ -1,16 +1,16 @@
 error: use of `.to_digit(..).is_some()`
   --> $DIR/to_digit_is_some.rs:9:13
    |
-LL |     let _ = d.to_digit(10).is_some();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(10)`
+LL |     let _ = d.to_digit(8).is_some();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(8)`
    |
    = note: `-D clippy::to-digit-is-some` implied by `-D warnings`
 
 error: use of `.to_digit(..).is_some()`
   --> $DIR/to_digit_is_some.rs:10:13
    |
-LL |     let _ = char::to_digit(c, 10).is_some();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 10)`
+LL |     let _ = char::to_digit(c, 8).is_some();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 8)`
 
 error: aborting due to 2 previous errors
 
index f5ca91143af25683625d19ba51efe22d070e5233..a21d4c5d637daec8678ce052656d1d86bff631aa 100644 (file)
@@ -95,4 +95,7 @@ fn bar()
     }
 }
 
+// This should not lint
+fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
+
 fn main() {}
index 6f8c8e47dfbf1fe56589a06f80017513e69167e8..d0a4cfb88370e2060620137f7e015632a96ff3e7 100644 (file)
@@ -67,5 +67,13 @@ LL |         Self: Iterator<Item = Foo>,
    |
    = help: consider removing this trait bound
 
-error: aborting due to 8 previous errors
+error: this trait bound is already specified in the where clause
+  --> $DIR/trait_duplication_in_bounds.rs:99:23
+   |
+LL | fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
+   |                       ^^^^^^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: aborting due to 9 previous errors
 
index cd5a7127791a86784fe8332d91c2589e9ba10d49..5a431bee04a45c03d8b54ee438a1b4b003aa611a 100644 (file)
@@ -1,7 +1,7 @@
 #![warn(clippy::unsound_collection_transmute)]
 
 use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
-use std::mem::transmute;
+use std::mem::{transmute, MaybeUninit};
 
 fn main() {
     unsafe {
@@ -43,5 +43,8 @@ fn main() {
         // wrong layout
         let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, [u8; 4]>::new());
         let _ = transmute::<_, HashMap<u32, u32>>(HashMap::<[u8; 4], u32>::new());
+
+        let _ = transmute::<_, Vec<u8>>(Vec::<MaybeUninit<u8>>::new());
+        let _ = transmute::<_, Vec<*mut u32>>(Vec::<Box<u32>>::new());
     }
 }
diff --git a/src/tools/clippy/tests/ui/trim_split_whitespace.fixed b/src/tools/clippy/tests/ui/trim_split_whitespace.fixed
new file mode 100644 (file)
index 0000000..e4d352f
--- /dev/null
@@ -0,0 +1,91 @@
+// run-rustfix
+#![warn(clippy::trim_split_whitespace)]
+#![allow(clippy::let_unit_value)]
+
+struct Custom;
+impl Custom {
+    fn trim(self) -> Self {
+        self
+    }
+    fn split_whitespace(self) {}
+}
+
+struct DerefStr(&'static str);
+impl std::ops::Deref for DerefStr {
+    type Target = str;
+    fn deref(&self) -> &Self::Target {
+        self.0
+    }
+}
+
+struct DerefStrAndCustom(&'static str);
+impl std::ops::Deref for DerefStrAndCustom {
+    type Target = str;
+    fn deref(&self) -> &Self::Target {
+        self.0
+    }
+}
+impl DerefStrAndCustom {
+    fn trim(self) -> Self {
+        self
+    }
+    fn split_whitespace(self) {}
+}
+
+struct DerefStrAndCustomSplit(&'static str);
+impl std::ops::Deref for DerefStrAndCustomSplit {
+    type Target = str;
+    fn deref(&self) -> &Self::Target {
+        self.0
+    }
+}
+impl DerefStrAndCustomSplit {
+    #[allow(dead_code)]
+    fn split_whitespace(self) {}
+}
+
+struct DerefStrAndCustomTrim(&'static str);
+impl std::ops::Deref for DerefStrAndCustomTrim {
+    type Target = str;
+    fn deref(&self) -> &Self::Target {
+        self.0
+    }
+}
+impl DerefStrAndCustomTrim {
+    fn trim(self) -> Self {
+        self
+    }
+}
+
+fn main() {
+    // &str
+    let _ = " A B C ".split_whitespace(); // should trigger lint
+    let _ = " A B C ".split_whitespace(); // should trigger lint
+    let _ = " A B C ".split_whitespace(); // should trigger lint
+
+    // String
+    let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint
+    let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint
+    let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint
+
+    // Custom
+    let _ = Custom.trim().split_whitespace(); // should not trigger lint
+
+    // Deref<Target=str>
+    let s = DerefStr(" A B C ");
+    let _ = s.split_whitespace(); // should trigger lint
+
+    // Deref<Target=str> + custom impl
+    let s = DerefStrAndCustom(" A B C ");
+    let _ = s.trim().split_whitespace(); // should not trigger lint
+
+    // Deref<Target=str> + only custom split_ws() impl
+    let s = DerefStrAndCustomSplit(" A B C ");
+    let _ = s.split_whitespace(); // should trigger lint
+    // Expl: trim() is called on str (deref) and returns &str.
+    //       Thus split_ws() is called on str as well and the custom impl on S is unused
+
+    // Deref<Target=str> + only custom trim() impl
+    let s = DerefStrAndCustomTrim(" A B C ");
+    let _ = s.trim().split_whitespace(); // should not trigger lint
+}
diff --git a/src/tools/clippy/tests/ui/trim_split_whitespace.rs b/src/tools/clippy/tests/ui/trim_split_whitespace.rs
new file mode 100644 (file)
index 0000000..f98451a
--- /dev/null
@@ -0,0 +1,91 @@
+// run-rustfix
+#![warn(clippy::trim_split_whitespace)]
+#![allow(clippy::let_unit_value)]
+
+struct Custom;
+impl Custom {
+    fn trim(self) -> Self {
+        self
+    }
+    fn split_whitespace(self) {}
+}
+
+struct DerefStr(&'static str);
+impl std::ops::Deref for DerefStr {
+    type Target = str;
+    fn deref(&self) -> &Self::Target {
+        self.0
+    }
+}
+
+struct DerefStrAndCustom(&'static str);
+impl std::ops::Deref for DerefStrAndCustom {
+    type Target = str;
+    fn deref(&self) -> &Self::Target {
+        self.0
+    }
+}
+impl DerefStrAndCustom {
+    fn trim(self) -> Self {
+        self
+    }
+    fn split_whitespace(self) {}
+}
+
+struct DerefStrAndCustomSplit(&'static str);
+impl std::ops::Deref for DerefStrAndCustomSplit {
+    type Target = str;
+    fn deref(&self) -> &Self::Target {
+        self.0
+    }
+}
+impl DerefStrAndCustomSplit {
+    #[allow(dead_code)]
+    fn split_whitespace(self) {}
+}
+
+struct DerefStrAndCustomTrim(&'static str);
+impl std::ops::Deref for DerefStrAndCustomTrim {
+    type Target = str;
+    fn deref(&self) -> &Self::Target {
+        self.0
+    }
+}
+impl DerefStrAndCustomTrim {
+    fn trim(self) -> Self {
+        self
+    }
+}
+
+fn main() {
+    // &str
+    let _ = " A B C ".trim().split_whitespace(); // should trigger lint
+    let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint
+    let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint
+
+    // String
+    let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint
+    let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint
+    let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint
+
+    // Custom
+    let _ = Custom.trim().split_whitespace(); // should not trigger lint
+
+    // Deref<Target=str>
+    let s = DerefStr(" A B C ");
+    let _ = s.trim().split_whitespace(); // should trigger lint
+
+    // Deref<Target=str> + custom impl
+    let s = DerefStrAndCustom(" A B C ");
+    let _ = s.trim().split_whitespace(); // should not trigger lint
+
+    // Deref<Target=str> + only custom split_ws() impl
+    let s = DerefStrAndCustomSplit(" A B C ");
+    let _ = s.trim().split_whitespace(); // should trigger lint
+    // Expl: trim() is called on str (deref) and returns &str.
+    //       Thus split_ws() is called on str as well and the custom impl on S is unused
+
+    // Deref<Target=str> + only custom trim() impl
+    let s = DerefStrAndCustomTrim(" A B C ");
+    let _ = s.trim().split_whitespace(); // should not trigger lint
+}
diff --git a/src/tools/clippy/tests/ui/trim_split_whitespace.stderr b/src/tools/clippy/tests/ui/trim_split_whitespace.stderr
new file mode 100644 (file)
index 0000000..5ae7849
--- /dev/null
@@ -0,0 +1,52 @@
+error: found call to `str::trim` before `str::split_whitespace`
+  --> $DIR/trim_split_whitespace.rs:62:23
+   |
+LL |     let _ = " A B C ".trim().split_whitespace(); // should trigger lint
+   |                       ^^^^^^^ help: remove `trim()`
+   |
+   = note: `-D clippy::trim-split-whitespace` implied by `-D warnings`
+
+error: found call to `str::trim_start` before `str::split_whitespace`
+  --> $DIR/trim_split_whitespace.rs:63:23
+   |
+LL |     let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint
+   |                       ^^^^^^^^^^^^^ help: remove `trim_start()`
+
+error: found call to `str::trim_end` before `str::split_whitespace`
+  --> $DIR/trim_split_whitespace.rs:64:23
+   |
+LL |     let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint
+   |                       ^^^^^^^^^^^ help: remove `trim_end()`
+
+error: found call to `str::trim` before `str::split_whitespace`
+  --> $DIR/trim_split_whitespace.rs:67:37
+   |
+LL |     let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint
+   |                                     ^^^^^^^ help: remove `trim()`
+
+error: found call to `str::trim_start` before `str::split_whitespace`
+  --> $DIR/trim_split_whitespace.rs:68:37
+   |
+LL |     let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint
+   |                                     ^^^^^^^^^^^^^ help: remove `trim_start()`
+
+error: found call to `str::trim_end` before `str::split_whitespace`
+  --> $DIR/trim_split_whitespace.rs:69:37
+   |
+LL |     let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint
+   |                                     ^^^^^^^^^^^ help: remove `trim_end()`
+
+error: found call to `str::trim` before `str::split_whitespace`
+  --> $DIR/trim_split_whitespace.rs:76:15
+   |
+LL |     let _ = s.trim().split_whitespace(); // should trigger lint
+   |               ^^^^^^^ help: remove `trim()`
+
+error: found call to `str::trim` before `str::split_whitespace`
+  --> $DIR/trim_split_whitespace.rs:84:15
+   |
+LL |     let _ = s.trim().split_whitespace(); // should trigger lint
+   |               ^^^^^^^ help: remove `trim()`
+
+error: aborting due to 8 previous errors
+
index fc740ee11d6ab7b815ab274dd283c76b44055da6..d11432f9046111d4ff77f7279250bc7f10083975 100644 (file)
@@ -79,4 +79,7 @@ struct Foo<T, U>
     u: U,
 }
 
+// This should not lint
+fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
+
 fn main() {}
index 148c19c7d0701dc2910de718d175a03122d26e03..abc25e59496bfa13262c3a5b938ce282a7b25edb 100644 (file)
@@ -19,5 +19,13 @@ LL |     Self: Copy + Default + Ord,
    |
    = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord`
 
-error: aborting due to 2 previous errors
+error: this type has already been used as a bound predicate
+  --> $DIR/type_repetition_in_bounds.rs:83:43
+   |
+LL | fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
+   |                                           ^^^^^^^^^^
+   |
+   = help: consider combining the bounds: `impl AsRef<str>: AsRef<str> + AsRef<str>`
+
+error: aborting due to 3 previous errors
 
index afa337c45f41766033abe0d1d85b2694f7fa11f6..7be15b0b2dd3d4669f7202a15d18bd1b9a2d7c20 100644 (file)
@@ -1,6 +1,7 @@
 // aux-build:proc_macro_unsafe.rs
 
 #![warn(clippy::undocumented_unsafe_blocks)]
+#![allow(clippy::let_unit_value)]
 
 extern crate proc_macro_unsafe;
 
index 856a07fd31685e3170eba2ff3baf47221494cf01..87d445bd7b86b12e39844c8718076717ff517aaf 100644 (file)
@@ -1,5 +1,5 @@
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:256:19
+  --> $DIR/undocumented_unsafe_blocks.rs:257:19
    |
 LL |     /* Safety: */ unsafe {}
    |                   ^^^^^^^^^
@@ -8,7 +8,7 @@ LL |     /* Safety: */ unsafe {}
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:260:5
+  --> $DIR/undocumented_unsafe_blocks.rs:261:5
    |
 LL |     unsafe {}
    |     ^^^^^^^^^
@@ -16,7 +16,7 @@ LL |     unsafe {}
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:264:14
+  --> $DIR/undocumented_unsafe_blocks.rs:265:14
    |
 LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
    |              ^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:264:29
+  --> $DIR/undocumented_unsafe_blocks.rs:265:29
    |
 LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
    |                             ^^^^^^^^^^^^^
@@ -32,7 +32,7 @@ LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:264:48
+  --> $DIR/undocumented_unsafe_blocks.rs:265:48
    |
 LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
    |                                                ^^^^^^^^^^^^^
@@ -40,7 +40,7 @@ LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:268:18
+  --> $DIR/undocumented_unsafe_blocks.rs:269:18
    |
 LL |     let _ = (42, unsafe {}, "test", unsafe {});
    |                  ^^^^^^^^^
@@ -48,7 +48,7 @@ LL |     let _ = (42, unsafe {}, "test", unsafe {});
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:268:37
+  --> $DIR/undocumented_unsafe_blocks.rs:269:37
    |
 LL |     let _ = (42, unsafe {}, "test", unsafe {});
    |                                     ^^^^^^^^^
@@ -56,7 +56,7 @@ LL |     let _ = (42, unsafe {}, "test", unsafe {});
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:272:14
+  --> $DIR/undocumented_unsafe_blocks.rs:273:14
    |
 LL |     let _ = *unsafe { &42 };
    |              ^^^^^^^^^^^^^^
@@ -64,7 +64,7 @@ LL |     let _ = *unsafe { &42 };
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:277:19
+  --> $DIR/undocumented_unsafe_blocks.rs:278:19
    |
 LL |     let _ = match unsafe {} {
    |                   ^^^^^^^^^
@@ -72,7 +72,7 @@ LL |     let _ = match unsafe {} {
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:283:14
+  --> $DIR/undocumented_unsafe_blocks.rs:284:14
    |
 LL |     let _ = &unsafe {};
    |              ^^^^^^^^^
@@ -80,7 +80,7 @@ LL |     let _ = &unsafe {};
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:287:14
+  --> $DIR/undocumented_unsafe_blocks.rs:288:14
    |
 LL |     let _ = [unsafe {}; 5];
    |              ^^^^^^^^^
@@ -88,7 +88,7 @@ LL |     let _ = [unsafe {}; 5];
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:291:13
+  --> $DIR/undocumented_unsafe_blocks.rs:292:13
    |
 LL |     let _ = unsafe {};
    |             ^^^^^^^^^
@@ -96,7 +96,7 @@ LL |     let _ = unsafe {};
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:301:8
+  --> $DIR/undocumented_unsafe_blocks.rs:302:8
    |
 LL |     t!(unsafe {});
    |        ^^^^^^^^^
@@ -104,7 +104,7 @@ LL |     t!(unsafe {});
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:307:13
+  --> $DIR/undocumented_unsafe_blocks.rs:308:13
    |
 LL |             unsafe {}
    |             ^^^^^^^^^
@@ -116,7 +116,7 @@ LL |     t!();
    = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:315:5
+  --> $DIR/undocumented_unsafe_blocks.rs:316:5
    |
 LL |     unsafe {} // SAFETY:
    |     ^^^^^^^^^
@@ -124,7 +124,7 @@ LL |     unsafe {} // SAFETY:
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:319:5
+  --> $DIR/undocumented_unsafe_blocks.rs:320:5
    |
 LL |     unsafe {
    |     ^^^^^^^^
@@ -132,7 +132,7 @@ LL |     unsafe {
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:329:5
+  --> $DIR/undocumented_unsafe_blocks.rs:330:5
    |
 LL |     unsafe {};
    |     ^^^^^^^^^
@@ -140,7 +140,7 @@ LL |     unsafe {};
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:333:20
+  --> $DIR/undocumented_unsafe_blocks.rs:334:20
    |
 LL |     println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index 1ed3883c1f06059285c858c40323eec9b069de77..dac5ce272c02665133022468802bbfe679768d0d 100644 (file)
@@ -1,4 +1,5 @@
 #![feature(stmt_expr_attributes)]
+#![allow(clippy::let_unit_value)]
 
 use std::mem::{self, MaybeUninit};
 
index 85b64a8419ab021bd36585ebde6e9431e84301e6..15ef2349489fa711a5ed4a68557a2a8d3b8b3dfd 100644 (file)
@@ -1,5 +1,5 @@
 error: this call for this type may be undefined behavior
-  --> $DIR/uninit.rs:6:29
+  --> $DIR/uninit.rs:7:29
    |
 LL |     let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,13 +7,13 @@ LL |     let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
    = note: `#[deny(clippy::uninit_assumed_init)]` on by default
 
 error: this call for this type may be undefined behavior
-  --> $DIR/uninit.rs:9:31
+  --> $DIR/uninit.rs:10:31
    |
 LL |     let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() };
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this call for this type may be undefined behavior
-  --> $DIR/uninit.rs:24:29
+  --> $DIR/uninit.rs:25:29
    |
 LL |     let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() };
    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index 535683729f686a965f7aab4618adae726a9c6aee..38be87bddf19845de6797e70384317bdbfb91162 100644 (file)
@@ -7,7 +7,8 @@
     clippy::unnecessary_wraps,
     clippy::or_fun_call,
     clippy::needless_question_mark,
-    clippy::self_named_constructors
+    clippy::self_named_constructors,
+    clippy::let_unit_value
 )]
 
 use std::fmt::Debug;
index 5cfb367a7be80795eb5da98fd6b2b3ea4b4614fa..394dee29dc96e5c802826bb52830063e25024ae6 100644 (file)
@@ -1,5 +1,5 @@
 error: passing a unit value to a function
-  --> $DIR/unit_arg.rs:56:5
+  --> $DIR/unit_arg.rs:57:5
    |
 LL | /     foo({
 LL | |         1;
@@ -20,7 +20,7 @@ LL ~     foo(());
    |
 
 error: passing a unit value to a function
-  --> $DIR/unit_arg.rs:59:5
+  --> $DIR/unit_arg.rs:60:5
    |
 LL |     foo(foo(1));
    |     ^^^^^^^^^^^
@@ -32,7 +32,7 @@ LL ~     foo(());
    |
 
 error: passing a unit value to a function
-  --> $DIR/unit_arg.rs:60:5
+  --> $DIR/unit_arg.rs:61:5
    |
 LL | /     foo({
 LL | |         foo(1);
@@ -54,7 +54,7 @@ LL ~     foo(());
    |
 
 error: passing a unit value to a function
-  --> $DIR/unit_arg.rs:65:5
+  --> $DIR/unit_arg.rs:66:5
    |
 LL | /     b.bar({
 LL | |         1;
@@ -74,7 +74,7 @@ LL ~     b.bar(());
    |
 
 error: passing unit values to a function
-  --> $DIR/unit_arg.rs:68:5
+  --> $DIR/unit_arg.rs:69:5
    |
 LL |     taking_multiple_units(foo(0), foo(1));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -87,7 +87,7 @@ LL ~     taking_multiple_units((), ());
    |
 
 error: passing unit values to a function
-  --> $DIR/unit_arg.rs:69:5
+  --> $DIR/unit_arg.rs:70:5
    |
 LL | /     taking_multiple_units(foo(0), {
 LL | |         foo(1);
@@ -110,7 +110,7 @@ LL ~     taking_multiple_units((), ());
    |
 
 error: passing unit values to a function
-  --> $DIR/unit_arg.rs:73:5
+  --> $DIR/unit_arg.rs:74:5
    |
 LL | /     taking_multiple_units(
 LL | |         {
@@ -140,7 +140,7 @@ LL +         foo(2);
  ...
 
 error: passing a unit value to a function
-  --> $DIR/unit_arg.rs:84:13
+  --> $DIR/unit_arg.rs:85:13
    |
 LL |     None.or(Some(foo(2)));
    |             ^^^^^^^^^^^^
@@ -154,7 +154,7 @@ LL ~     });
    |
 
 error: passing a unit value to a function
-  --> $DIR/unit_arg.rs:87:5
+  --> $DIR/unit_arg.rs:88:5
    |
 LL |     foo(foo(()));
    |     ^^^^^^^^^^^^
@@ -166,7 +166,7 @@ LL ~     foo(());
    |
 
 error: passing a unit value to a function
-  --> $DIR/unit_arg.rs:124:5
+  --> $DIR/unit_arg.rs:125:5
    |
 LL |     Some(foo(1))
    |     ^^^^^^^^^^^^
index 989916c239bad090ad71a54dcb6109220da07b3f..43eb54eff477b72518c92b93e5b9448830aea6f0 100644 (file)
@@ -1,4 +1,5 @@
 #![warn(clippy::unit_hash)]
+#![allow(clippy::let_unit_value)]
 
 use std::collections::hash_map::DefaultHasher;
 use std::hash::Hash;
index da276296e0282507b446f629bb5ff051c2f2086f..050fa55a12bea71bab7f4c44f11b81724d1400cf 100644 (file)
@@ -1,5 +1,5 @@
 error: this call to `hash` on the unit type will do nothing
-  --> $DIR/unit_hash.rs:18:23
+  --> $DIR/unit_hash.rs:19:23
    |
 LL |         Foo::Empty => ().hash(&mut state),
    |                       ^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
@@ -8,7 +8,7 @@ LL |         Foo::Empty => ().hash(&mut state),
    = note: the implementation of `Hash` for `()` is a no-op
 
 error: this call to `hash` on the unit type will do nothing
-  --> $DIR/unit_hash.rs:23:5
+  --> $DIR/unit_hash.rs:24:5
    |
 LL |     res.hash(&mut state);
    |     ^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
@@ -16,7 +16,7 @@ LL |     res.hash(&mut state);
    = note: the implementation of `Hash` for `()` is a no-op
 
 error: this call to `hash` on the unit type will do nothing
-  --> $DIR/unit_hash.rs:26:5
+  --> $DIR/unit_hash.rs:27:5
    |
 LL |     do_nothing().hash(&mut state);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
diff --git a/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.fixed b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.fixed
new file mode 100644 (file)
index 0000000..f95f913
--- /dev/null
@@ -0,0 +1,22 @@
+// run-rustfix
+
+#![warn(clippy::unnecessary_owned_empty_strings)]
+
+fn ref_str_argument(_value: &str) {}
+
+#[allow(clippy::ptr_arg)]
+fn ref_string_argument(_value: &String) {}
+
+fn main() {
+    // should be linted
+    ref_str_argument("");
+
+    // should be linted
+    ref_str_argument("");
+
+    // should not be linted
+    ref_str_argument("");
+
+    // should not be linted
+    ref_string_argument(&String::new());
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.rs b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.rs
new file mode 100644 (file)
index 0000000..0cbdc15
--- /dev/null
@@ -0,0 +1,22 @@
+// run-rustfix
+
+#![warn(clippy::unnecessary_owned_empty_strings)]
+
+fn ref_str_argument(_value: &str) {}
+
+#[allow(clippy::ptr_arg)]
+fn ref_string_argument(_value: &String) {}
+
+fn main() {
+    // should be linted
+    ref_str_argument(&String::new());
+
+    // should be linted
+    ref_str_argument(&String::from(""));
+
+    // should not be linted
+    ref_str_argument("");
+
+    // should not be linted
+    ref_string_argument(&String::new());
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.stderr b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.stderr
new file mode 100644 (file)
index 0000000..46bc459
--- /dev/null
@@ -0,0 +1,16 @@
+error: usage of `&String::new()` for a function expecting a `&str` argument
+  --> $DIR/unnecessary_owned_empty_strings.rs:12:22
+   |
+LL |     ref_str_argument(&String::new());
+   |                      ^^^^^^^^^^^^^^ help: try: `""`
+   |
+   = note: `-D clippy::unnecessary-owned-empty-strings` implied by `-D warnings`
+
+error: usage of `&String::from("")` for a function expecting a `&str` argument
+  --> $DIR/unnecessary_owned_empty_strings.rs:15:22
+   |
+LL |     ref_str_argument(&String::from(""));
+   |                      ^^^^^^^^^^^^^^^^^ help: try: `""`
+
+error: aborting due to 2 previous errors
+
index 38ba41ac54ecb2ca7814f135f1525b44d66a825a..7455e22d49b21f8df93bea25f1095f5beac55983 100644 (file)
@@ -2,6 +2,7 @@
 
 #![allow(clippy::ptr_arg)]
 #![warn(clippy::unnecessary_to_owned)]
+#![feature(custom_inner_attributes)]
 
 use std::borrow::Cow;
 use std::ffi::{CStr, CString, OsStr, OsString};
@@ -213,6 +214,17 @@ fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::E
 
 fn require_string(_: &String) {}
 
+fn _msrv_1_35() {
+    #![clippy::msrv = "1.35"]
+    // `copied` was stabilized in 1.36, so clippy should use `cloned`.
+    let _ = &["x"][..].iter().cloned();
+}
+
+fn _msrv_1_36() {
+    #![clippy::msrv = "1.36"]
+    let _ = &["x"][..].iter().copied();
+}
+
 // https://github.com/rust-lang/rust-clippy/issues/8507
 mod issue_8507 {
     #![allow(dead_code)]
index 15fb7ee83e3d100a50cfa13dfd54cf12afcd7cd0..bbcd00ad2204a9a789cee2e457b6d89357aaa223 100644 (file)
@@ -2,6 +2,7 @@
 
 #![allow(clippy::ptr_arg)]
 #![warn(clippy::unnecessary_to_owned)]
+#![feature(custom_inner_attributes)]
 
 use std::borrow::Cow;
 use std::ffi::{CStr, CString, OsStr, OsString};
@@ -213,6 +214,17 @@ fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::E
 
 fn require_string(_: &String) {}
 
+fn _msrv_1_35() {
+    #![clippy::msrv = "1.35"]
+    // `copied` was stabilized in 1.36, so clippy should use `cloned`.
+    let _ = &["x"][..].to_vec().into_iter();
+}
+
+fn _msrv_1_36() {
+    #![clippy::msrv = "1.36"]
+    let _ = &["x"][..].to_vec().into_iter();
+}
+
 // https://github.com/rust-lang/rust-clippy/issues/8507
 mod issue_8507 {
     #![allow(dead_code)]
index c53ce32be775706f91d4ce47b2e4b5bd4220abd9..f9713559e4f7fce4ac6b3d6096ae1e25ee83ee52 100644 (file)
@@ -1,54 +1,54 @@
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:150:64
+  --> $DIR/unnecessary_to_owned.rs:151:64
    |
 LL |     require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
    |                                                                ^^^^^^^^^^^ help: remove this
    |
    = note: `-D clippy::redundant-clone` implied by `-D warnings`
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:150:20
+  --> $DIR/unnecessary_to_owned.rs:151:20
    |
 LL |     require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:151:40
+  --> $DIR/unnecessary_to_owned.rs:152:40
    |
 LL |     require_os_str(&OsString::from("x").to_os_string());
    |                                        ^^^^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:151:21
+  --> $DIR/unnecessary_to_owned.rs:152:21
    |
 LL |     require_os_str(&OsString::from("x").to_os_string());
    |                     ^^^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:152:48
+  --> $DIR/unnecessary_to_owned.rs:153:48
    |
 LL |     require_path(&std::path::PathBuf::from("x").to_path_buf());
    |                                                ^^^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:152:19
+  --> $DIR/unnecessary_to_owned.rs:153:19
    |
 LL |     require_path(&std::path::PathBuf::from("x").to_path_buf());
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:153:35
+  --> $DIR/unnecessary_to_owned.rs:154:35
    |
 LL |     require_str(&String::from("x").to_string());
    |                                   ^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:153:18
+  --> $DIR/unnecessary_to_owned.rs:154:18
    |
 LL |     require_str(&String::from("x").to_string());
    |                  ^^^^^^^^^^^^^^^^^
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:59:36
+  --> $DIR/unnecessary_to_owned.rs:60:36
    |
 LL |     require_c_str(&Cow::from(c_str).into_owned());
    |                                    ^^^^^^^^^^^^^ help: remove this
@@ -56,427 +56,427 @@ LL |     require_c_str(&Cow::from(c_str).into_owned());
    = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:60:19
+  --> $DIR/unnecessary_to_owned.rs:61:19
    |
 LL |     require_c_str(&c_str.to_owned());
    |                   ^^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_os_string`
-  --> $DIR/unnecessary_to_owned.rs:62:20
+  --> $DIR/unnecessary_to_owned.rs:63:20
    |
 LL |     require_os_str(&os_str.to_os_string());
    |                    ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:63:38
+  --> $DIR/unnecessary_to_owned.rs:64:38
    |
 LL |     require_os_str(&Cow::from(os_str).into_owned());
    |                                      ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:64:20
+  --> $DIR/unnecessary_to_owned.rs:65:20
    |
 LL |     require_os_str(&os_str.to_owned());
    |                    ^^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_path_buf`
-  --> $DIR/unnecessary_to_owned.rs:66:18
+  --> $DIR/unnecessary_to_owned.rs:67:18
    |
 LL |     require_path(&path.to_path_buf());
    |                  ^^^^^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:67:34
+  --> $DIR/unnecessary_to_owned.rs:68:34
    |
 LL |     require_path(&Cow::from(path).into_owned());
    |                                  ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:68:18
+  --> $DIR/unnecessary_to_owned.rs:69:18
    |
 LL |     require_path(&path.to_owned());
    |                  ^^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:70:17
+  --> $DIR/unnecessary_to_owned.rs:71:17
    |
 LL |     require_str(&s.to_string());
    |                 ^^^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:71:30
+  --> $DIR/unnecessary_to_owned.rs:72:30
    |
 LL |     require_str(&Cow::from(s).into_owned());
    |                              ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:72:17
+  --> $DIR/unnecessary_to_owned.rs:73:17
    |
 LL |     require_str(&s.to_owned());
    |                 ^^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:73:17
+  --> $DIR/unnecessary_to_owned.rs:74:17
    |
 LL |     require_str(&x_ref.to_string());
    |                 ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:75:19
+  --> $DIR/unnecessary_to_owned.rs:76:19
    |
 LL |     require_slice(&slice.to_vec());
    |                   ^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:76:36
+  --> $DIR/unnecessary_to_owned.rs:77:36
    |
 LL |     require_slice(&Cow::from(slice).into_owned());
    |                                    ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:77:19
+  --> $DIR/unnecessary_to_owned.rs:78:19
    |
 LL |     require_slice(&array.to_owned());
    |                   ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:78:19
+  --> $DIR/unnecessary_to_owned.rs:79:19
    |
 LL |     require_slice(&array_ref.to_owned());
    |                   ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:79:19
+  --> $DIR/unnecessary_to_owned.rs:80:19
    |
 LL |     require_slice(&slice.to_owned());
    |                   ^^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:80:19
+  --> $DIR/unnecessary_to_owned.rs:81:19
    |
 LL |     require_slice(&x_ref.to_owned());
    |                   ^^^^^^^^^^^^^^^^^ help: use: `x_ref`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:82:42
+  --> $DIR/unnecessary_to_owned.rs:83:42
    |
 LL |     require_x(&Cow::<X>::Owned(x.clone()).into_owned());
    |                                          ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:83:15
+  --> $DIR/unnecessary_to_owned.rs:84:15
    |
 LL |     require_x(&x_ref.to_owned());
    |               ^^^^^^^^^^^^^^^^^ help: use: `x_ref`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:85:25
+  --> $DIR/unnecessary_to_owned.rs:86:25
    |
 LL |     require_deref_c_str(c_str.to_owned());
    |                         ^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:86:26
+  --> $DIR/unnecessary_to_owned.rs:87:26
    |
 LL |     require_deref_os_str(os_str.to_owned());
    |                          ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:87:24
+  --> $DIR/unnecessary_to_owned.rs:88:24
    |
 LL |     require_deref_path(path.to_owned());
    |                        ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:88:23
+  --> $DIR/unnecessary_to_owned.rs:89:23
    |
 LL |     require_deref_str(s.to_owned());
    |                       ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:89:25
+  --> $DIR/unnecessary_to_owned.rs:90:25
    |
 LL |     require_deref_slice(slice.to_owned());
    |                         ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:91:30
+  --> $DIR/unnecessary_to_owned.rs:92:30
    |
 LL |     require_impl_deref_c_str(c_str.to_owned());
    |                              ^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:92:31
+  --> $DIR/unnecessary_to_owned.rs:93:31
    |
 LL |     require_impl_deref_os_str(os_str.to_owned());
    |                               ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:93:29
+  --> $DIR/unnecessary_to_owned.rs:94:29
    |
 LL |     require_impl_deref_path(path.to_owned());
    |                             ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:94:28
+  --> $DIR/unnecessary_to_owned.rs:95:28
    |
 LL |     require_impl_deref_str(s.to_owned());
    |                            ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:95:30
+  --> $DIR/unnecessary_to_owned.rs:96:30
    |
 LL |     require_impl_deref_slice(slice.to_owned());
    |                              ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:97:29
+  --> $DIR/unnecessary_to_owned.rs:98:29
    |
 LL |     require_deref_str_slice(s.to_owned(), slice.to_owned());
    |                             ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:97:43
+  --> $DIR/unnecessary_to_owned.rs:98:43
    |
 LL |     require_deref_str_slice(s.to_owned(), slice.to_owned());
    |                                           ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:98:29
+  --> $DIR/unnecessary_to_owned.rs:99:29
    |
 LL |     require_deref_slice_str(slice.to_owned(), s.to_owned());
    |                             ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:98:47
+  --> $DIR/unnecessary_to_owned.rs:99:47
    |
 LL |     require_deref_slice_str(slice.to_owned(), s.to_owned());
    |                                               ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:100:26
+  --> $DIR/unnecessary_to_owned.rs:101:26
    |
 LL |     require_as_ref_c_str(c_str.to_owned());
    |                          ^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:101:27
+  --> $DIR/unnecessary_to_owned.rs:102:27
    |
 LL |     require_as_ref_os_str(os_str.to_owned());
    |                           ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:102:25
+  --> $DIR/unnecessary_to_owned.rs:103:25
    |
 LL |     require_as_ref_path(path.to_owned());
    |                         ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:103:24
+  --> $DIR/unnecessary_to_owned.rs:104:24
    |
 LL |     require_as_ref_str(s.to_owned());
    |                        ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:104:24
+  --> $DIR/unnecessary_to_owned.rs:105:24
    |
 LL |     require_as_ref_str(x.to_owned());
    |                        ^^^^^^^^^^^^ help: use: `&x`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:105:26
+  --> $DIR/unnecessary_to_owned.rs:106:26
    |
 LL |     require_as_ref_slice(array.to_owned());
    |                          ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:106:26
+  --> $DIR/unnecessary_to_owned.rs:107:26
    |
 LL |     require_as_ref_slice(array_ref.to_owned());
    |                          ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:107:26
+  --> $DIR/unnecessary_to_owned.rs:108:26
    |
 LL |     require_as_ref_slice(slice.to_owned());
    |                          ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:109:31
+  --> $DIR/unnecessary_to_owned.rs:110:31
    |
 LL |     require_impl_as_ref_c_str(c_str.to_owned());
    |                               ^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:110:32
+  --> $DIR/unnecessary_to_owned.rs:111:32
    |
 LL |     require_impl_as_ref_os_str(os_str.to_owned());
    |                                ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:111:30
+  --> $DIR/unnecessary_to_owned.rs:112:30
    |
 LL |     require_impl_as_ref_path(path.to_owned());
    |                              ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:112:29
+  --> $DIR/unnecessary_to_owned.rs:113:29
    |
 LL |     require_impl_as_ref_str(s.to_owned());
    |                             ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:113:29
+  --> $DIR/unnecessary_to_owned.rs:114:29
    |
 LL |     require_impl_as_ref_str(x.to_owned());
    |                             ^^^^^^^^^^^^ help: use: `&x`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:114:31
+  --> $DIR/unnecessary_to_owned.rs:115:31
    |
 LL |     require_impl_as_ref_slice(array.to_owned());
    |                               ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:115:31
+  --> $DIR/unnecessary_to_owned.rs:116:31
    |
 LL |     require_impl_as_ref_slice(array_ref.to_owned());
    |                               ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:116:31
+  --> $DIR/unnecessary_to_owned.rs:117:31
    |
 LL |     require_impl_as_ref_slice(slice.to_owned());
    |                               ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:118:30
+  --> $DIR/unnecessary_to_owned.rs:119:30
    |
 LL |     require_as_ref_str_slice(s.to_owned(), array.to_owned());
    |                              ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:118:44
+  --> $DIR/unnecessary_to_owned.rs:119:44
    |
 LL |     require_as_ref_str_slice(s.to_owned(), array.to_owned());
    |                                            ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:119:30
+  --> $DIR/unnecessary_to_owned.rs:120:30
    |
 LL |     require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
    |                              ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:119:44
+  --> $DIR/unnecessary_to_owned.rs:120:44
    |
 LL |     require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
    |                                            ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:120:30
+  --> $DIR/unnecessary_to_owned.rs:121:30
    |
 LL |     require_as_ref_str_slice(s.to_owned(), slice.to_owned());
    |                              ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:120:44
+  --> $DIR/unnecessary_to_owned.rs:121:44
    |
 LL |     require_as_ref_str_slice(s.to_owned(), slice.to_owned());
    |                                            ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:121:30
+  --> $DIR/unnecessary_to_owned.rs:122:30
    |
 LL |     require_as_ref_slice_str(array.to_owned(), s.to_owned());
    |                              ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:121:48
+  --> $DIR/unnecessary_to_owned.rs:122:48
    |
 LL |     require_as_ref_slice_str(array.to_owned(), s.to_owned());
    |                                                ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:122:30
+  --> $DIR/unnecessary_to_owned.rs:123:30
    |
 LL |     require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
    |                              ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:122:52
+  --> $DIR/unnecessary_to_owned.rs:123:52
    |
 LL |     require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
    |                                                    ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:123:30
+  --> $DIR/unnecessary_to_owned.rs:124:30
    |
 LL |     require_as_ref_slice_str(slice.to_owned(), s.to_owned());
    |                              ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:123:48
+  --> $DIR/unnecessary_to_owned.rs:124:48
    |
 LL |     require_as_ref_slice_str(slice.to_owned(), s.to_owned());
    |                                                ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:125:20
+  --> $DIR/unnecessary_to_owned.rs:126:20
    |
 LL |     let _ = x.join(&x_ref.to_string());
    |                    ^^^^^^^^^^^^^^^^^^ help: use: `x_ref`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:127:13
+  --> $DIR/unnecessary_to_owned.rs:128:13
    |
 LL |     let _ = slice.to_vec().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:128:13
+  --> $DIR/unnecessary_to_owned.rs:129:13
    |
 LL |     let _ = slice.to_owned().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:129:13
+  --> $DIR/unnecessary_to_owned.rs:130:13
    |
 LL |     let _ = [std::path::PathBuf::new()][..].to_vec().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:130:13
+  --> $DIR/unnecessary_to_owned.rs:131:13
    |
 LL |     let _ = [std::path::PathBuf::new()][..].to_owned().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:132:13
+  --> $DIR/unnecessary_to_owned.rs:133:13
    |
 LL |     let _ = IntoIterator::into_iter(slice.to_vec());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:133:13
+  --> $DIR/unnecessary_to_owned.rs:134:13
    |
 LL |     let _ = IntoIterator::into_iter(slice.to_owned());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:134:13
+  --> $DIR/unnecessary_to_owned.rs:135:13
    |
 LL |     let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:135:13
+  --> $DIR/unnecessary_to_owned.rs:136:13
    |
 LL |     let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:196:14
+  --> $DIR/unnecessary_to_owned.rs:197:14
    |
 LL |     for t in file_types.to_vec() {
    |              ^^^^^^^^^^^^^^^^^^^
@@ -491,11 +491,23 @@ LL -         let path = match get_file_path(&t) {
 LL +         let path = match get_file_path(t) {
    | 
 
+error: unnecessary use of `to_vec`
+  --> $DIR/unnecessary_to_owned.rs:220:14
+   |
+LL |     let _ = &["x"][..].to_vec().into_iter();
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()`
+
+error: unnecessary use of `to_vec`
+  --> $DIR/unnecessary_to_owned.rs:225:14
+   |
+LL |     let _ = &["x"][..].to_vec().into_iter();
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()`
+
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:260:24
+  --> $DIR/unnecessary_to_owned.rs:272:24
    |
 LL |         Box::new(build(y.to_string()))
    |                        ^^^^^^^^^^^^^ help: use: `y`
 
-error: aborting due to 77 previous errors
+error: aborting due to 79 previous errors
 
index 46463a29e9b20deec853a9a7fab3293cad5017d5..c223b5bc711b2a83956542e652457132f8136fd6 100644 (file)
@@ -6,10 +6,13 @@
 #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
 
 fn main() {
+    // Should be ignored by this lint, as nesting requires more characters.
+    if let &0 | &2 = &0 {}
+
     if let box (0 | 2) = Box::new(0) {}
     if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
-    const C0: &u8 = &1;
-    if let &(0 | 2) | C0 = &0 {}
+    const C0: Option<u8> = Some(1);
+    if let Some(1 | 2) | C0 = None {}
     if let &mut (0 | 2) = &mut 0 {}
     if let x @ (0 | 2) = 0 {}
     if let (0, 1 | 2 | 3) = (0, 0) {}
index 8ce0738bfc27b3c31cfe88e858544cf8d3bddc71..04cd11036e4e00e954a27faf2c35cfb650db530e 100644 (file)
@@ -6,10 +6,13 @@
 #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
 
 fn main() {
+    // Should be ignored by this lint, as nesting requires more characters.
+    if let &0 | &2 = &0 {}
+
     if let box 0 | box 2 = Box::new(0) {}
     if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {}
-    const C0: &u8 = &1;
-    if let &0 | C0 | &2 = &0 {}
+    const C0: Option<u8> = Some(1);
+    if let Some(1) | C0 | Some(2) = None {}
     if let &mut 0 | &mut 2 = &mut 0 {}
     if let x @ 0 | x @ 2 = 0 {}
     if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
index de424c3fdb8f2c628da68ce4eab5d39ab5523002..453c66cbba8fc2838311408c637b65e8b20c9e59 100644 (file)
@@ -1,5 +1,5 @@
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:9:12
+  --> $DIR/unnested_or_patterns.rs:12:12
    |
 LL |     if let box 0 | box 2 = Box::new(0) {}
    |            ^^^^^^^^^^^^^
@@ -11,7 +11,7 @@ LL |     if let box (0 | 2) = Box::new(0) {}
    |            ~~~~~~~~~~~
 
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:10:12
+  --> $DIR/unnested_or_patterns.rs:13:12
    |
 LL |     if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -22,18 +22,18 @@ LL |     if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
    |            ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:12:12
+  --> $DIR/unnested_or_patterns.rs:15:12
    |
-LL |     if let &0 | C0 | &2 = &0 {}
-   |            ^^^^^^^^^^^^
+LL |     if let Some(1) | C0 | Some(2) = None {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^
    |
 help: nest the patterns
    |
-LL |     if let &(0 | 2) | C0 = &0 {}
-   |            ~~~~~~~~~~~~~
+LL |     if let Some(1 | 2) | C0 = None {}
+   |            ~~~~~~~~~~~~~~~~
 
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:13:12
+  --> $DIR/unnested_or_patterns.rs:16:12
    |
 LL |     if let &mut 0 | &mut 2 = &mut 0 {}
    |            ^^^^^^^^^^^^^^^
@@ -44,7 +44,7 @@ LL |     if let &mut (0 | 2) = &mut 0 {}
    |            ~~~~~~~~~~~~
 
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:14:12
+  --> $DIR/unnested_or_patterns.rs:17:12
    |
 LL |     if let x @ 0 | x @ 2 = 0 {}
    |            ^^^^^^^^^^^^^
@@ -55,7 +55,7 @@ LL |     if let x @ (0 | 2) = 0 {}
    |            ~~~~~~~~~~~
 
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:15:12
+  --> $DIR/unnested_or_patterns.rs:18:12
    |
 LL |     if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -66,7 +66,7 @@ LL |     if let (0, 1 | 2 | 3) = (0, 0) {}
    |            ~~~~~~~~~~~~~~
 
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:16:12
+  --> $DIR/unnested_or_patterns.rs:19:12
    |
 LL |     if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -77,7 +77,7 @@ LL |     if let (1 | 2 | 3, 0) = (0, 0) {}
    |            ~~~~~~~~~~~~~~
 
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:17:12
+  --> $DIR/unnested_or_patterns.rs:20:12
    |
 LL |     if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -88,7 +88,7 @@ LL |     if let (x, ..) | (x, 1 | 2) = (0, 1) {}
    |            ~~~~~~~~~~~~~~~~~~~~
 
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:18:12
+  --> $DIR/unnested_or_patterns.rs:21:12
    |
 LL |     if let [0] | [1] = [0] {}
    |            ^^^^^^^^^
@@ -99,7 +99,7 @@ LL |     if let [0 | 1] = [0] {}
    |            ~~~~~~~
 
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:19:12
+  --> $DIR/unnested_or_patterns.rs:22:12
    |
 LL |     if let [x, 0] | [x, 1] = [0, 1] {}
    |            ^^^^^^^^^^^^^^^
@@ -110,7 +110,7 @@ LL |     if let [x, 0 | 1] = [0, 1] {}
    |            ~~~~~~~~~~
 
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:20:12
+  --> $DIR/unnested_or_patterns.rs:23:12
    |
 LL |     if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -121,7 +121,7 @@ LL |     if let [x, 0 | 1 | 2] = [0, 1] {}
    |            ~~~~~~~~~~~~~~
 
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:21:12
+  --> $DIR/unnested_or_patterns.rs:24:12
    |
 LL |     if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -132,7 +132,7 @@ LL |     if let [x, ..] | [x, 1 | 2] = [0, 1] {}
    |            ~~~~~~~~~~~~~~~~~~~~
 
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:23:12
+  --> $DIR/unnested_or_patterns.rs:26:12
    |
 LL |     if let TS(0, x) | TS(1, x) = TS(0, 0) {}
    |            ^^^^^^^^^^^^^^^^^^^
@@ -143,7 +143,7 @@ LL |     if let TS(0 | 1, x) = TS(0, 0) {}
    |            ~~~~~~~~~~~~
 
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:24:12
+  --> $DIR/unnested_or_patterns.rs:27:12
    |
 LL |     if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -154,7 +154,7 @@ LL |     if let TS(1 | 2 | 3, 0) = TS(0, 0) {}
    |            ~~~~~~~~~~~~~~~~
 
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:25:12
+  --> $DIR/unnested_or_patterns.rs:28:12
    |
 LL |     if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -165,7 +165,7 @@ LL |     if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {}
    |            ~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnested or-patterns
-  --> $DIR/unnested_or_patterns.rs:30:12
+  --> $DIR/unnested_or_patterns.rs:33:12
    |
 LL |     if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index ce58a80347b558ef7a46b48b2883def9f0455f71..c23231a99e9f049015d44694fbd8d58ee2b8c1c3 100644 (file)
@@ -57,6 +57,12 @@ pub use std::io::prelude::*;
 #[allow(clippy::enum_glob_use)]
 pub use std::cmp::Ordering::*;
 
+// don't lint on clippy::redundant_pub_crate
+mod c {
+    #[allow(clippy::redundant_pub_crate)]
+    pub(crate) struct S;
+}
+
 fn test_indented_attr() {
     #![allow(clippy::almost_swapped)]
     use std::collections::HashSet;
index c82bb9ba07fd731efb14a91a954fb3a132cc6423..7a7b198ea6078e701ce33c9faf5269f72820f5c4 100644 (file)
@@ -57,6 +57,12 @@ mod b {
 #[allow(clippy::enum_glob_use)]
 pub use std::cmp::Ordering::*;
 
+// don't lint on clippy::redundant_pub_crate
+mod c {
+    #[allow(clippy::redundant_pub_crate)]
+    pub(crate) struct S;
+}
+
 fn test_indented_attr() {
     #[allow(clippy::almost_swapped)]
     use std::collections::HashSet;
index d0194e4bbbe5b0a3133c130a8ccd30a80402fd59..255d2876355316ed987b39a86231806c7f122d31 100644 (file)
@@ -13,7 +13,7 @@ LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)`
 
 error: useless lint attribute
-  --> $DIR/useless_attribute.rs:61:5
+  --> $DIR/useless_attribute.rs:67:5
    |
 LL |     #[allow(clippy::almost_swapped)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]`
index 8402c33a4cd5fb695d37116caf003763dcf186bd..b6f47ae906b73709b6096e5ffeeb4caa36dedab3 100644 (file)
@@ -8,8 +8,7 @@
 // FIXME: We should likely add another edition 2021 test case for this lint
 
 #![warn(clippy::wildcard_imports)]
-#![allow(unused)]
-#![allow(clippy::unnecessary_wraps)]
+#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)]
 #![warn(unused_imports)]
 
 extern crate wildcard_imports_helper;
index faaeaade9b02bff03b30793a73638ba5a3a560eb..eb404b7a3de5f661cbb2af6b428ca042830ac985 100644 (file)
@@ -8,8 +8,7 @@
 // FIXME: We should likely add another edition 2021 test case for this lint
 
 #![warn(clippy::wildcard_imports)]
-#![allow(unused)]
-#![allow(clippy::unnecessary_wraps)]
+#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)]
 #![warn(unused_imports)]
 
 extern crate wildcard_imports_helper;
index 7534a65ec9bd56c0878846b81663d05ef269cb92..626c1754fc82c8720bbc3e1ac84c0f932dbce630 100644 (file)
@@ -1,5 +1,5 @@
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:17:5
+  --> $DIR/wildcard_imports.rs:16:5
    |
 LL | use crate::fn_mod::*;
    |     ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
@@ -7,85 +7,85 @@ LL | use crate::fn_mod::*;
    = note: `-D clippy::wildcard-imports` implied by `-D warnings`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:18:5
+  --> $DIR/wildcard_imports.rs:17:5
    |
 LL | use crate::mod_mod::*;
    |     ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:19:5
+  --> $DIR/wildcard_imports.rs:18:5
    |
 LL | use crate::multi_fn_mod::*;
    |     ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:21:5
+  --> $DIR/wildcard_imports.rs:20:5
    |
 LL | use crate::struct_mod::*;
    |     ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:25:5
+  --> $DIR/wildcard_imports.rs:24:5
    |
 LL | use wildcard_imports_helper::inner::inner_for_self_import::*;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:26:5
+  --> $DIR/wildcard_imports.rs:25:5
    |
 LL | use wildcard_imports_helper::*;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:97:13
+  --> $DIR/wildcard_imports.rs:96:13
    |
 LL |         use crate::fn_mod::*;
    |             ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:103:75
+  --> $DIR/wildcard_imports.rs:102:75
    |
 LL |         use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
    |                                                                           ^ help: try: `inner_extern_foo`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:104:13
+  --> $DIR/wildcard_imports.rs:103:13
    |
 LL |         use wildcard_imports_helper::*;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:115:20
+  --> $DIR/wildcard_imports.rs:114:20
    |
 LL |         use self::{inner::*, inner2::*};
    |                    ^^^^^^^^ help: try: `inner::inner_foo`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:115:30
+  --> $DIR/wildcard_imports.rs:114:30
    |
 LL |         use self::{inner::*, inner2::*};
    |                              ^^^^^^^^^ help: try: `inner2::inner_bar`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:122:13
+  --> $DIR/wildcard_imports.rs:121:13
    |
 LL |         use wildcard_imports_helper::*;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:151:9
+  --> $DIR/wildcard_imports.rs:150:9
    |
 LL |     use crate::in_fn_test::*;
    |         ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:160:9
+  --> $DIR/wildcard_imports.rs:159:9
    |
 LL |     use crate:: in_fn_test::  * ;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:161:9
+  --> $DIR/wildcard_imports.rs:160:9
    |
 LL |       use crate:: fn_mod::
    |  _________^
@@ -93,37 +93,37 @@ LL | |         *;
    | |_________^ help: try: `crate:: fn_mod::foo`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:172:13
+  --> $DIR/wildcard_imports.rs:171:13
    |
 LL |         use super::*;
    |             ^^^^^^^^ help: try: `super::foofoo`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:207:17
+  --> $DIR/wildcard_imports.rs:206:17
    |
 LL |             use super::*;
    |                 ^^^^^^^^ help: try: `super::insidefoo`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:215:13
+  --> $DIR/wildcard_imports.rs:214:13
    |
 LL |         use super_imports::*;
    |             ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:224:17
+  --> $DIR/wildcard_imports.rs:223:17
    |
 LL |             use super::super::*;
    |                 ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:233:13
+  --> $DIR/wildcard_imports.rs:232:13
    |
 LL |         use super::super::super_imports::*;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
 
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:241:13
+  --> $DIR/wildcard_imports.rs:240:13
    |
 LL |         use super::*;
    |             ^^^^^^^^ help: try: `super::foofoo`
index f8fee4b3ab2d8fb420c66ada277a88a046a4bc2d..e3cc90ee222ada2f2e2ca21742f13a03b10ba50b 100644 (file)
@@ -193,11 +193,6 @@ pub mod issue8142 {
     struct S;
 
     impl S {
-        // Should lint: is_ methods should only take &self, or no self at all.
-        fn is_still_buggy(&mut self) -> bool {
-            false
-        }
-
         // Should not lint: "no self at all" is allowed.
         fn is_forty_two(x: u32) -> bool {
             x == 42
index 5493a99572e068746d2900921ca208595e99f9ad..2e7ee51d7e11a7e07593df9a926e2b7912a2f78c 100644 (file)
@@ -31,7 +31,7 @@ LL |     fn into_i32(&self) {}
    |
    = help: consider choosing a less ambiguous name
 
-error: methods called `is_*` usually take `self` by reference or no `self`
+error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self`
   --> $DIR/wrong_self_convention.rs:38:15
    |
 LL |     fn is_i32(self) {}
@@ -71,7 +71,7 @@ LL |     pub fn into_i64(&self) {}
    |
    = help: consider choosing a less ambiguous name
 
-error: methods called `is_*` usually take `self` by reference or no `self`
+error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self`
   --> $DIR/wrong_self_convention.rs:46:19
    |
 LL |     pub fn is_i64(self) {}
@@ -111,7 +111,7 @@ LL |         fn into_i32_ref(&self) {}
    |
    = help: consider choosing a less ambiguous name
 
-error: methods called `is_*` usually take `self` by reference or no `self`
+error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self`
   --> $DIR/wrong_self_convention.rs:98:19
    |
 LL |         fn is_i32(self) {}
@@ -143,7 +143,7 @@ LL |         fn into_i32_ref(&self);
    |
    = help: consider choosing a less ambiguous name
 
-error: methods called `is_*` usually take `self` by reference or no `self`
+error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self`
   --> $DIR/wrong_self_convention.rs:122:19
    |
 LL |         fn is_i32(self);
@@ -191,13 +191,5 @@ LL |         fn to_u64(self) -> u64 {
    |
    = help: consider choosing a less ambiguous name
 
-error: methods called `is_*` usually take `self` by reference or no `self`
-  --> $DIR/wrong_self_convention.rs:197:27
-   |
-LL |         fn is_still_buggy(&mut self) -> bool {
-   |                           ^^^^^^^^^
-   |
-   = help: consider choosing a less ambiguous name
-
-error: aborting due to 25 previous errors
+error: aborting due to 24 previous errors
 
index a8fe8331133778ebcf934b4b234c07c430f484c9..0dcf4743e8b8dbcd1186b794a9f2846a763a2da2 100644 (file)
@@ -104,3 +104,13 @@ pub fn to_mut(self: Pin<&mut Self>) {}
         pub fn to_other_thingy(self: Pin<&Self>) {}
     }
 }
+
+mod issue_8480_8513 {
+    struct Cat(String);
+
+    impl Cat {
+        fn is_animal(&mut self) -> bool {
+            todo!();
+        }
+    }
+}